1

私はたくさんのデータを以下に読まなければなりません:

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

どうすればこれをよりパフォーマンスの高いものにすることができますか?

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

結果をどのように使用するかによっては、特に、入力する予定の文字列が非常に大きい場合は、入力データをベクトルではなくロープデータ構造に入れてみることができます。ロープへの追加は非常に高速ですが、その後の文字ごとの走査は一定の係数で遅くなります。トレードオフがうまくいくかどうかはわかりませんが、結果をどうする必要があるのか​​わかりません。

編集:あなたのコメントから、これはオプションではないことがわかります。入ってくるデータのサイズが完全に任意である一般的なケースでは、これ以上効率的にできるとは思いません。それ以外の場合は、最初にベクトルに十分なスペースを予約して、平均的なケースなどで再割り当てなしで、または最大で1つの再割り当てなしでデータが収まるようにすることができます。

あなたのコードについて私が気づいたことの1つ:

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

私はあなたが意味すると思います

if(aPtr==NULL || aLen<=0)
于 2009-09-26T11:38:05.973 に答える
0

あなたができる主なことは、データのコピーをあまり行わないようにすることです。現在、insert()が呼び出されると、データがバッファにコピーされます。次に、getbuff()が呼び出されると、データを(うまくいけば)指定されたバッファーにコピーします。したがって、外部からデータを取得するには、各バイトを2回コピーします。

この部分:

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

arOutObjは実際にはベクトルであると想定しているようです。もしそうなら、getbuffを、実際には1つのタイプのパラメーターに対してのみ機能するテンプレートではなく、(への)ベクトルをとる通常の関数として書き直す方がはるかに良いでしょう。

そこから、データの1つのコピーを完全に削除することはかなり簡単な問題になります。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を実装します。

これは、元のコードでは得られなかったもう1つの詳細にも対応しています(そう思われます)。以前のように、getbuffは一部のデータを返しますが、もう一度呼び出すと、(明らかに、すでに返されたデータを追跡していないため、すべてを再度返します。データの割り当てを継続しますが、データを削除することはありません)スワップの目的は次のとおりです。空のベクトルを作成し、それをbuffContainerによって維持されているベクトルと交換するため、buffContainerには空のベクトルがあり、満たされたベクトルはgetbuff()と呼ばれるものに渡されます。

別の方法は、スワップをさらに一歩進めることです。基本的に、2つのバッファーがあります。

  1. buffContainerが所有するもの
  2. getbuffer()を呼び出すものが所有するもの

通常の過程では、バッファサイズがすぐに最大サイズに達することが予想されます。そこからは、そのスペースを単純に再利用したいと考えています。データを一方に読み取り、それを渡して処理し、その間にデータをもう一方に読み取ります。

たまたま、それもとても簡単です。getbuff()を次のように変更します。

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

これにより、速度が大幅に向上するはずです。データを前後にコピーする代わりに、データへの1つのベクトルのポインタを他のベクトルと交換するだけです(現在の割り当てサイズやベクトルの使用サイズなど、他のいくつかの詳細とともに)。クリアは通常、非常に高速です。ベクトル(またはdtorのないタイプ)の場合、ベクトル内のアイテムの数をゼロに設定するだけです(もちろん、アイテムにdtorがある場合は、それらを破棄する必要があります)。そこから、次にinsert()が呼び出されると、新しいデータがベクターがすでに所有しているメモリにコピーされます(ベクターが割り当てたよりも多くのスペースが必要になるまで)。

于 2009-09-28T04:47:50.553 に答える