我正在使用环形缓冲区来保存流式音频应用程序的样本。我复制了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);
}
答案 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。