2016-12-16 25 views
1

SDRデバイスからバッファを高速で取り込むツールを作成しました(1秒間に1000万の複合サンプル(サンプルは短いタイプです))。しかし、私が書いたコードでは、書かれたことを振り返るたびに、私は小さな塊を見逃しています。C++での高速バッファリング

私がこの問題を軽減しようとしたのは、同じサイズの2つのバッファを使用し、それらの間をスワップしてサンプルを紛失しないようにすることです。バッファを交換してバックバッファ(サンプルサイズの3倍のサイズ)にサンプルをオフロードし、必要に応じて新しいスレッドを呼び出して新しいデータをディスクに書き込むときは、チャンクがなくなります。

SDR装置自体は、2016年のような奇妙なものとして独自の内部バッファサイズを通知し、それは、サンプルの実部と虚アレイへの2つのポインタを与えます。明らかに、私はこのサンプルレートでこのような小さな配列のオーバーヘッドを避けたいので、スワップバッファを65536と大きなサイズで実装することで、うまくいけばこのような問題を避けることができます。

私はピンが欠落しているチャンクがより頻繁になるバッファ私はスワップのサイズを小さくするときので、コールバック関数で最も可能性が高いと問題点を指摘しています。

私はこれを間違った方法で行っているのですか、私の解決策に欠けているものがあるか、何か正しく書かれていませんか?

私はそれが故にデータ速度のこの種のMEMMOVEとmemcpyのための必要性のために遅すぎるという理由だけで、可能な限り標準libaryを避けてきました。唯一の例外は、バッファポインタのスワッピングとスレッドの作成です。 IQTypeは

IQType<short>* bufferA; 
    IQType<short>* bufferB; 

void MiricsDataSource::newSamplesCallBack(short *xi, short *xq, unsigned int firstSampleNum, int grChanged, int rfChanged, int fsChanged, unsigned int numSamples, unsigned int reset, void *cbContext) { 

    MiricsDataSource* mirCtx = static_cast<MiricsDataSource*>(cbContext); 

    for (int i = 0; i < numSamples; ++i) 
    { 
     mirCtx->bufferA[mirCtx->bufferCount] = IQType<short>(xi[i],xq[i]); 
     mirCtx->bufferCount++; 
     if(mirCtx->bufferCount == mirCtx->bufferSize-1) { 
      std::swap(mirCtx->bufferA,mirCtx->bufferB); 
      mirCtx->owner->backBuffer->write(mirCtx->bufferB,mirCtx->bufferSize); 
      mirCtx->bufferCount = 0; 
     } 
    } 
} 

バックバッファ書き込みと関連t_write:SDRサンプルデータをアンロード

template <class T> class IQType { 
public: 
     T inPhaseValue; 
     T quadraturePhaseValue; 

     IQType() : inPhaseValue(0), quadraturePhaseValue(0){}; 
     IQType(T i, T q) : inPhaseValue(i), quadraturePhaseValue(q){}; 
}; 

SDRデバイスコールバック関数としてのバッファをスワップ

が実装されています機能:

void BackBuffer::write(const IQType<short>* buff, size_t bLength) { 
    std::thread dumpThread(&BackBuffer::t_write,this,buff,bLength); 
    dumpThread.detach(); 
} 

void BackBuffer::t_write(const IQType<short>* buff, size_t bLength) { 
    std::lock_guard<std::mutex> lck (bufferMutex); 
    memmove(&backBuffer[0],(&backBuffer[0])+bLength,(sizeof(IQType<short>*)*(length-bLength))); 
    memcpy(&backBuffer[length-bLength],buff,(sizeof(IQType<short>*)*(bLength))); 
    if(dumpToFile) { 
     IQType<short>* toWrite = new IQType<short>[bLength]; 
     memcpy(toWrite,buff,(sizeof(IQType<short>*)*(bLength))); 
     strmDmpMgr->write(toWrite,bLength); 
    } 
} 
+0

'私はこの種のデータ速度には遅すぎるため、できるだけ標準ライブラリを避けました。したがってmemmoveとmemcpyの必要性があります。私は標準ライブラリだけを見ていますmemcpy/memmoveは、あなたの場合のように型が簡単ではないときに行います。本当に測定していない場合は、その声明を削除してください。 – Arunmu

+0

"私は標準ライブラリをできるだけ避けているので[標準ライブラリの機能]の必要性は少し矛盾しているようです。 – user2079303

+0

元々、私はバックバッファーを動かすためにstd :: rotateを使っていましたが、30ミリオンのサンプルが5秒間保存されました。 memmoveはこれを何百回も高速化します。 Justyは、バックバッファのデータを記録する以外のことを明確にするために、バックバッファは、記録されたデータの最後の3秒を観察するためにも使用されます。キューと同様に扱われますが、バックボッファのサイズまでの任意の位置と長さを見るオプションがあります。 – Gelion

答えて

1
  1. BackBuffer::writeでスレッドを生成するのに最も大きなコストがかかります。これをしないでください。単一の永続的なバックグラウンドスレッドを実行し、メッセージを送信してください。

  2. 現在の設定で出力バッファが破壊される危険性があります(スレッドが最初のバッファで終了する前に2番目のバッファを埋めると、最初のバッファを再び上書きすることができます)。フルバッファのキューと空のバッファのキューを使用してスレッド間を循環させるだけで、任意の数のバッファを処理できます。

    ダイナミックアロケーションをクリティカルループから守るために、最低限の空きレベルを下回った場合、新しいバッファを作成するバックグラウンドスレッドを作成します。

  3. Vooによれば、大きなバッファに直接読み込むだけで(中間のmemcpyなどは避けてください)、簡単です。これは、リストオブバッファのアプローチよりも弾力性が劣りますが、ここでは柔軟性が必要であることは明白ではありません。

あり、いくつかの小さな最適化(例えば。一度だけ正しい値を格納し、間接を通じてすべての反復をバッファカウントをインクリメントしません)がありますが、そのスレッドが主な問題です。

1

「データダンプ」ごとに新しいスレッドを作成することが考えられます。バッファサイズによっては、1秒間に何千ものスレッドを作成することができます。これにより、プログラムだけでなくコンピュータ全体のパフォーマンスが大幅に低下する可能性があります。 1つのスレッドを作成するのは高価な操作ですが、操作がすべてのスレッド間を循環する必要はありませんシステム上の他のすべてのスレッド。

は、代わりに私はあなたが尋ねるバッファをダンプ行い、既に実行中のスレッド( c++ thread poolsで検索)のプールを持っている別のデザインを、示唆しています。それからあなたは、あなたが現在書いている場所に加えて、スレッドごとに1つずつ、バッファの循環環を持つことができます。

+0

私はちょうどそれが使用するスレッドの数を確認し、約15(ウィンドウによると)のままです。どれくらい多くが産卵されたかをより正確に見る方法がありますか? – Gelion

関連する問題