2016-12-16 98 views
1

我一直在編寫一個工具,它可以從SDR設備以高速緩存(每秒1000萬個複雜採樣(樣本是短類型))。但是,隨着我寫的代碼,每當我回頭看寫的內容時,我都會看到一些小塊。C++中的高速緩衝

我試圖緩解這個問題的方法是使用兩個相同大小的緩衝區並在它們之間交換以避免丟失任何樣本。每當我經歷交換緩衝區並將樣本卸載到後臺緩衝區(其大小是採樣速率的3倍)並且如果需要時調用新線程以將新數據寫入到磁盤時,塊會丟失。

SDR設備本身將自己的內部緩衝區大小通告爲2016年的奇怪事物,它給出了實數和虛數採樣數組的兩個指針。很明顯,我想避免這樣的小數組在這個採樣率上的開銷,所以通過實現更大尺寸的交換緩衝區65536,希望我希望能避免這樣的問題,但無濟於事。

我已經指出問題最有可能在回調函數,因爲當我減少交換緩衝區的大小失蹤塊變得更加頻繁。

我是否正在做這個錯誤的方法,或者有更明顯的東西在我的解決方案中缺少,或者我沒有正確書寫一些東西?

我儘可能地避免了標準庫,因爲它對於這種數據速度來說太慢了,因此需要memmove和memcpy。唯一的例外是緩衝區指針交換和創建線程。

交換緩衝器被實現爲:

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

IQType是:

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裝置回調函數卸載SDR的示例數據:

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功能:

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來移動backbuffer,但這樣做需要5秒鐘才能存儲3000萬個樣本。 memmove做這幾百倍更快。只是爲了澄清我用backbuffer中的數據做其他事情而不是記錄。後緩衝器也用於觀察記錄數據的最後3秒。它的處理方式與隊列類似,但可以選擇查看任何位置和任何長度,直到backbuffer的大小。 – Gelion

回答

1
  1. 最大的成本是在BackBuffer::write產卵線程。不要這樣做,只需運行一個持久後臺線程併發送消息。

  2. 當前的破壞輸出緩衝區的設置存在風險(在線程完成第一個緩衝區之前填充第二個緩衝區允許您再次開始覆蓋第一個緩衝區)。您可以通過使用完整緩衝區隊列和空緩衝區隊列在您的線程之間循環來處理任意數量的緩衝區。

    使後臺線程負責創建新緩衝區,如果您低於最低級別的空閒級別以保持動態分配不在關鍵循環中。

  3. 正如Voo所說,直接讀入您的大緩衝區(並避免中間memcpy等)仍然更簡單。它的確具有比緩衝列表方法更小的彈性,但這並不明顯,您需要靈活性。

有一些小的優化(如不增加緩衝通過間接計算每次迭代,只是存儲正確的值一次),但該線程是主要問題。

1

一個可能的來源是您爲每個「數據轉儲」創建一個新線程。根據緩衝區大小,您可以每秒創建數千個線程,這會導致嚴重的性能下降,不僅是程序而且是整個計算機。創建單個線程是一項昂貴的操作,更不用說操作必須在系統上的所有其他線程之間循環。

相反,我建議一個不同的設計,在那裏你有一個已經運行的線程池(搜索c++ thread pools),你要求做一個緩衝區轉儲。然後你可以有一個循環的緩衝區,每個線程加一個你正在寫的地方。

+0

我剛剛檢查了它使用了多少個線程,並且它保持在15左右(根據windows)。有沒有更準確的方法來查看有多少產生? – Gelion