如何有效地包装固定大小的循环缓冲区的索引

Kir*_*ril 5 c# c++ java optimization circular-buffer

我有一个固定大小的循环缓冲区(实现为数组):初始化时,缓冲区将填充指定的最大元素数,允许使用单个位置索引,以便跟踪我们在圆圈中的当前位置.

访问循环缓冲区中元素的有效方法是什么?这是我目前的解决方案:

int GetElement(int index)
{
    if (index >= buffer_size || index < 0)
    {
        // some code to handle the case
    }
    else
    {
        // wrap the index
        index = end_index + index >= buffer_size ? (index + end_index) - buffer_size : end_index + index;
    }

    return buffer[index];
}
Run Code Online (Sandbox Code Playgroud)

一些定义:
end_index是紧跟在圆圈中最后一个元素之后的元素的索引(它也被认为与start_index或圆的第一个元素相同).
buffer_size是缓冲区的最大大小.

mpe*_*pen 12

我想出的最好的是:

public static int Wrap(int index, int n)
{
    return ((index % n) + n) % n;
}
Run Code Online (Sandbox Code Playgroud)

(假设你需要使用负数)

  • 这对我来说并不明显,所以这里有一点澄清:`a` 是需要包装的索引,`n` 是数组大小。 (2认同)
  • 我只是在使用这样的代码时被一个错误咬了。问题是如果 index 接近 int 的大小,这可能会溢出并返回负数。例如:`((872415600 % 1275068416) + 1275068416) % 1275068416)` 等于 `-872414864`。 (2认同)

Pet*_*lor 10

确保缓冲区始终为2的幂,并掩盖顶部位.

  • @Johnatan:过早优化的模运算,是的. (4认同)

Jer*_*fin 5

它在某种程度上取决于处理器,但它可能至少值得尝试类似的东西 return (end_index + index) % buffer_size;


Kir*_*ril 5

我测试了所有3个版本

// plain wrap
public static int WrapIndex(int index, int endIndex, int maxSize)
{
    return (endIndex + index) > maxSize ? (endIndex + index) - maxSize : endIndex + index;
}

// wrap using mod
public static int WrapIndexMod(int index, int endIndex, int maxSize)
{
    return (endIndex + index) % maxSize;
}

// wrap by masking out the top bits
public static int WrapIndexMask(int index, int endIndex, int maxSize)
{
    return (endIndex + index) & (maxSize - 1);
}
Run Code Online (Sandbox Code Playgroud)

性能结果(滴答声):

Plain: 25 Mod: 16 Mask: 16 (maxSize = 512)
Plain: 25 Mod: 17 Mask: 17 (maxSize = 1024)
Plain: 25 Mod: 17 Mask: 17 (maxSize = 4096)
Run Code Online (Sandbox Code Playgroud)

因此,似乎模数是更好的选择,因为它不需要对缓冲区的大小进行任何限制。