1

我必须将大量数据读入:

vector<char> 

第 3 方库多次读取此数据。在每一轮它都会调用我的回调函数,其签名如下:

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

   ...

}

目前,我已经使用 STL Container 实现了一个缓冲区容器,其中提供了我的方法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;
}

我怎样才能使它更高效?

4

3 回答 3

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);
}
于 2009-09-28T06:14:19.693 回答
1

根据您打算如何使用结果,您可以尝试将传入的数据放入绳索数据结构而不是向量中,尤其是在您希望输入的字符串非常大的情况下。附加到绳索非常快,但随后的逐个字符遍历要慢一个常数因子。权衡可能对你有用,我不知道你需要对结果做什么。

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

我注意到您的代码的一件事:

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

我想你的意思是

if(aPtr==NULL || aLen<=0)
于 2009-09-26T11:38:05.973 回答
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();
}

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

于 2009-09-28T04:47:50.553 回答