代码之家  ›  专栏  ›  技术社区  ›  Satbir

如何使char缓冲区更具性能?

  •  1
  • Satbir  · 技术社区  · 15 年前

    我必须阅读大量数据:

    vector<char> 
    

    第三方库轮流读取这些数据。每次调用的回调函数的签名如下:

    CallbackFun ( int CBMsgFileItemID,
                  unsigned long CBtag,
                  void* CBuserInfo,
                  int CBdataSize,
                  void* CBdataBuffer,
                  int CBisFirst,
                  int CBisLast )
    {
    
       ...
    
    }
    

    目前,我已经使用stl容器实现了一个缓冲容器,其中我的方法 insert() getBuff 用于插入新缓冲区和获取存储缓冲区。但我仍然希望更好地执行代码,以便最小化分配和取消分配:

    template<typename T1>
    class buffContainer
    {
    private:
            class atomBuff
            {
            private:
                atomBuff(const atomBuff& arObj);
                atomBuff operator=(const atomBuff& arObj);
                public:
                int len;
                char *buffPtr;
                atomBuff():len(0),buffPtr(NULL)
                {}
                ~atomBuff()
                {
                    if(buffPtr!=NULL)
                        delete []buffPtr;
                }
            };
    public :
        buffContainer():_totalLen(0){}
    void insert(const char const *aptr,const  unsigned long  &alen);
    unsigned long getBuff(T1 &arOutObj);
    
    private:
        std::vector<atomBuff*> moleculeBuff;
        int _totalLen;
    };
    template<typename T1>
    void buffContainer< T1>::insert(const char const *aPtr,const  unsigned long  &aLen)
    {
        if(aPtr==NULL,aLen<=0)
            return;
        atomBuff *obj=new atomBuff();
        obj->len=aLen;
        obj->buffPtr=new char[aLen];
        memcpy(obj->buffPtr,aPtr,aLen);
        _totalLen+=aLen;
        moleculeBuff.push_back(obj);
    
    }
    template<typename T1>
    unsigned long buffContainer<T1>::getBuff(T1 &arOutObj)
    {
        std::cout<<"Total Lenght of Data is: "<<_totalLen<<std::endl;
        if(_totalLen==0)
            return _totalLen;
        // Note : Logic pending for case size(T1) > T2::Value_Type
        int noOfObjRqd=_totalLen/sizeof(T1::value_type);
        arOutObj.resize(noOfObjRqd);
        char *ptr=(char*)(&arOutObj[0]);
        for(std::vector<atomBuff*>::const_iterator itr=moleculeBuff.begin();itr!=moleculeBuff.end();itr++)
        {
            memcpy(ptr,(*itr)->buffPtr,(*itr)->len);
            ptr+= (*itr)->len;
        }
        std::cout<<arOutObj.size()<<std::endl;
    
        return _totalLen;
    }
    

    我怎样才能使它更具性能呢?

    3 回复  |  直到 15 年前
        1
  •  2
  •   jmucchiello    15 年前

    std::vector<char> foo;
    foo.reserve(MAGIC); // this is the important part. Reserve the right amount here.
                        // and you don't have any reallocs.
    setup_callback_fun(CallbackFun, &foo);
    
    CallbackFun ( int CBMsgFileItemID,
                  unsigned long CBtag,
                  void* CBuserInfo,
                  int CBdataSize,
                  void* CBdataBuffer,
                  int CBisFirst,
                  int CBisLast )
    {
         std::vector<char>* pFoo = static_cast<std::vector<char>*>(CBuserInfo);
    
         char* data = static_cast<char*>CBdataBuffer;
         pFoo->insert(pFoo->end(), data, data+CBdataSize);
    }
    
        2
  •  1
  •   Rüdiger Hanke shraddha hattimare    15 年前

    rope

    if(aPtr==NULL,aLen<=0)
    

    if(aPtr==NULL || aLen<=0)
    
        3
  •  0
  •   Jerry Coffin    15 年前

    arOutObj.resize(noOfObjRqd);
    char *ptr=(char*)(&arOutObj[0]);
    

    似乎认为aroutobj是一个向量。如果是这样,那么将getbuff重写为一个取(引用)向量的普通函数,而不是一个只适用于一种类型参数的模板,会更好。

    从此,完全消除数据的一个副本就变得相当简单了。在insert()中,不要手动分配内存和跟踪大小,而是将数据直接放入向量中。然后,当调用getbuff()时,不要将数据复制到它们的缓冲区中,只需给它一个对现有向量的引用。

    class buffContainer {
        std::vector<char> moleculeBuff;
    public:
        void insert(char const *p, unsigned long len) { 
    Edit: Here you really want to add:
            moleculeBuff.reserve(moleculeBuff.size()+len);
    End of edit.
            std::copy(p, p+len, std::back_inserter(moleculeBuff));
        }
    
        void getbuff(vector<char> &output) { 
            output = moleculeBuff;
        }
    };
    

    注意,我已经将getbuff的结果改为void——因为你给了它们一个向量,所以它的大小是已知的,并且没有返回大小的意义。实际上,您可能需要稍微更改签名,以返回缓冲区:

    vector<char> getbuff() { 
        vector<char> temp;
        temp.swap(moleculeBuff);
        return temp;
    }
    

    因为它按值返回一个(可能很大)向量,这在很大程度上取决于编译器实现命名的返回值优化(NRVO),但1)最坏的情况是,它执行的是您以前所做的,2)几乎所有合理的当前编译器都执行NRVO。

    这也解决了另一个细节,你的原始代码没有(似乎)。实际上,getbuff返回一些数据,但是如果您再次调用它,它(显然不跟踪已经返回的数据,所以它再次返回所有数据)。它不断地分配数据,但从不删除任何数据。这就是交换的目的:它创建一个空向量,然后用buffcontainer维护的向量交换,所以buffcontainer现在有一个空向量,填充的向量被移交给任何称为getbuff()的对象。

    另一种方法是将交换进一步:基本上,您有两个缓冲区:

    1. 一个属于BuffContainer
    2. 一个由调用getBuffer()的任何对象拥有

    在正常情况下,我们可能会期望缓冲区大小很快达到某个最大值。从那时起,我们真的想简单地重新循环这个空间:将一些数据读取到一个空间中,传递给另一个空间进行处理,当这发生时,将数据读取到另一个空间中。

    事实上,这也很容易做到。将getbuff()更改为如下所示:

    void getbuff(vector<char> &output) {
        swap(moleculeBuff, output);
        moleculeBuff.clear();
    }
    

    这将大大提高速度,而不是来回复制数据,而是将一个向量的指针与其他向量的指针交换(以及其他一些细节,如当前的分配大小和使用的向量大小)。正常情况下清除 真的? fast——对于一个向量(或者任何没有dtor的类型),它只会将向量中的项数设置为零(当然,如果项有dtor,它必须销毁它们)。从那里,下次调用insert()时,新数据将被复制到向量已经拥有的内存中(直到/除非它需要比向量分配的空间更多的空间)。