C ++中的高速缓冲

时间:2016-12-16 10:10:39

标签: c++ multithreading stream buffer

我一直在编写一个从SDR设备高速接收缓冲区的工具(每秒1000万个复杂样本(样本是短类型))。但是根据我编写的代码,每当我回顾已写的内容时,我都会丢失小块。

我尝试缓解此问题的方法是使用两个相同大小的缓冲区并在它们之间交换,以避免遗漏任何样本。每当我完成交换缓冲区并将样本卸载到后台缓冲区(其大小是采样率的3倍)的过程中,并且如果需要调用新线程将新数据写入磁盘时,块就会丢失。 / p>

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

BackBuffer写入和相关的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);
    }
}

2 个答案:

答案 0 :(得分:1)

一个可能的来源是为每个“数据转储”创建一个新线程。根据缓冲区大小,您可以每秒创建数千个线程,这可能导致严重的性能下降,不仅是您的程序,而且是整个计算机。创建单个线程是一项昂贵的操作,更不用说操作必须在所有线程之间循环 plus 系统上的所有其他线程。

相反,我建议一个不同的设计,你有一个已经运行的线程池(搜索c++ thread pools)你要求转储缓冲区。然后你可以有一个圆形缓冲环,每个螺纹一个加上你当前写的一个。

答案 1 :(得分:1)

  1. 最大的代价是在>>> 3 + 4 + 5e >>> obvious type error 中产生一个主题。不要这样做,只需运行一个持久的后台线程并发送消息。

  2. 当前设置损坏输出缓冲区存在风险(在线程完成第一个缓冲区之前填充第二个缓冲区允许您再次开始覆盖第一个缓冲区)。您可以通过使用完整缓冲区队列和空缓冲区队列来处理任意数量的缓冲区,以便在线程之间循环它们。

    让后台线程负责创建新的缓冲区,如果你低于一些最低级别的免费缓冲区,以保持动态分配不在关键循环之外。

  3. 正如Voo所说,只是直接读入你的大缓冲区(并避免使用中间BackBuffer::write等)更简单。它确实比缓冲列表方法具有更小的弹性,但是在这里你需要灵活性并不明显。

  4. 有一些较小的优化(例如,不要每次迭代通过间接增加缓冲区计数,只需存储正确的值一次),但该线程是主要问题。