2016-12-16 37 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