如何让我的char缓冲区更高效?

时间:2009-09-26 10:47:13

标签: c++ stl iostream

我必须将大量数据读入:

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 个答案:

答案 0 :(得分:2)

如果我对你的回调函数的猜测很有意义,那么除了向量之外你不需要任何东西:

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);
}

答案 1 :(得分:1)

根据您计划使用结果的方式,您可能会尝试将传入数据放入rope数据结构而不是向量,尤其是如果您希望进入的字符串非常大。附加到绳索非常快,但随后的char-by-char遍历因常数因素而变慢。这种权衡可能对你有用,我不知道你需要对结果做些什么。

编辑:我从你的评论中看到,这是不可取的。在一般情况下,当进入的数据大小完全是任意的时,我认为你不能做得更有效率。否则,您可以尝试在向量中初始保留足够的空间,以便在平均情况下,数据适合不进行或最多只能进行一次重新分配。

我注意到你的代码有一件事:

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

我认为你的意思是

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

答案 2 :(得分:0)

你可以做的主要是避免做那么多的数据复制。现在,当调用insert()时,您将数据复制到缓冲区中。然后,当调用getbuff()时,您将数据复制到他们(希望)指定的缓冲区。因此,要从外部获取数据,您需要复制每个字节两次。

这部分:

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

似乎假设arOutObj实际上是一个向量。如果是这样的话,将getbuff重写为采用(引用a)向量而不是真正仅适用于一种类型参数的模板的正常函数会好得多。

从那里,完全消除一个数据副本变得相当简单。在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();
}

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