如何提高环形缓冲区代码的性能?

时间:2011-08-23 08:36:18

标签: c++ audio optimization buffer

我正在使用环形缓冲区来保存流式音频应用程序的样本。我复制了Ken Greenebaum的Audio Anecdotes 2一书中的ringbuffer实现。

在我的代码上运行英特尔的Vtune分析器之后,它告诉我大多数时间花在getSamplesAvailable()getSpaceAvailable()函数上。

有人可以建议我如何优化这些功能吗?

RingBuffer::getSamplesAvailable(void)
{
   int count = (mTail - mHead + mSize) % mSize;
   return(count);
}

unsigned int RingBuffer::getSpaceAvailable(void)
{
   int free = (mHead - mTail + mSize - 1)%mSize;
   int underMark = mHighWaterMark - getSamplesAvailable();
   int spaceAvailable = min(underMark, free);
   return(spaceAvailable);
}

int RingBuffer::push(int value)
{
   int status = 1;
   if(getSpaceAvailable()) {
      // next two operations do NOT have to be atomic!
      // do NOT have to worry about collision with _tail
      mBuffer[mTail] = value;   // store value
      mTail = ++mTail % mSize;  // increment tail
  } else {
     status = 0;
  }
  return(status);
}

int RingBuffer::pop(int *value)
{
   int status = 1;
   if(getSamplesAvailable()) {
       *value = mBuffer[mHead];
       mHead = ++mHead % mSize;  // increment head
   } else {
      status = 0;
   }
   return(status);
}

4 个答案:

答案 0 :(得分:3)

如果您可以使mSize为2的幂,则可以替换

(mTail - mHead + mSize) % mSize

通过

(mTail - mHead) & (mSize-1)

(mHead - mTail + mSize - 1) % mSize

通过

(mHead - mTail - 1) & (mSize - 1)

答案 1 :(得分:2)

我认为问题不在于它们的复杂性,它们只是基本的整数算术,而是它们被调用的次数。

是否有可能在缓冲区中执行“批处理”(一次插入或检索各种值)更新?这样你可以节省一些计算。

答案 2 :(得分:0)

使用Henrik提出的2的幂是第一件事。还可以更改mTail和mHead索引的编码方式。而不是将它们保存在[0,mSize [范围中,你可以让它们像uint32_t一样自由运行。

访问元素时,您需要执行模数mSize,这将减慢每次访问的速度。

mBuffer[mTail % mSize] = value;

但它会简化样本的数量(即使你的索引包裹在uint32_t最大值上):

int count = mTail - mHead;

它还允许您完全使用环形缓冲区,而不是丢失一个元素来区分缓冲区已满或空的情况。

答案 3 :(得分:0)

如果速度对你来说是最重要的事情,你可以忍受这样一个事实:它是a)非便携式(只有Windows,虽然linux具有相同的基本功能,所以也应该在那里工作)和b)仅适用于发布版本(与调试模式中VC ++如何分配内存有关 - 可能还有一些编译标志?)您可以使用以下内容:

DWORD size = 64 * 1024;  // HAS to be a multiple of 64k due to how win allocates memory
HANDLE mapped_memory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, NULL);
int *p1 = (int*)MapViewOfFile(mapped_memory, FILE_MAP_WRITE, 0, 0, size);
int *p2 = (int*)MapViewOfFile(mapped_memory, FILE_MAP_WRITE, 0, 0, size);
// p1 and p2 should be adjacent in memory, if not try again.. no idea if there's some
// better method under windows

基本上,您现在在虚拟内存中有两个相邻的内存块,它们指向相同的物理内存。即如果你通过pdw1写作,你会看到pdw2的变化,反之亦然。

优势在于,您现在可以更有效地读取和写入缓冲区,并且一次只能读取大于一个字的数量。你只需要正确地减少指针 - 不应该太难实现。

编辑:现在看到 - 甚至还有一个POSIX实现on wiki