提高了C循环缓冲的效率



我想要一些帮助来提高循环缓冲区代码的效率。

我浏览了stackoverflow,发现(几乎)所有关于循环缓冲区的主题都是关于这样一个缓冲区的使用或循环缓冲器的基本实现。我真的需要关于如何使其超高效的信息。

计划将此缓冲器与STM32F4微控制器一起使用,后者具有一个精确的FPU。我计划大量使用write()和readn()函数。我们在这里说的是每秒几百万个电话,所以在这里和那里减少几个时钟周期真的会有所不同。

我将把最重要的代码放在这里,完整的缓冲区代码可以通过http://dl.dropbox.com/u/39710897/circular%20buffer.rar

有人能为我提供一些关于如何提高缓冲区效率的建议吗?

#define BUFF_SIZE 3             // buffer size set at compile time
typedef struct buffer{
    float buff[BUFF_SIZE];
    int readIndex;
    int writeIndex;
}buffer;
/********************************
* void write(buffer* buffer, float value)
* writes value into the buffer
* @param buffer* buffer
*   pointer to buffer to be used
* @param float value
*   valueto be written in buffer
********************************/
void write(buffer* buffer,float value){
    buffer->buff[buffer->writeIndex]=value;
    buffer->writeIndex++;
    if(buffer->writeIndex==BUFF_SIZE)
        buffer->writeIndex=0;
}
/********************************
* float readn(buffer* buffer, int Xn)
* reads specified value from buffer
* @param buffer* buffer
*   pointer to buffer to be read from
* @param int Xn
*   specifies the value to be read from buffer counting backwards from the most recently written value
*   i.e. the most recently writen value can be read with readn(buffer, 0), the value written before that with readn(buffer, 1)
********************************/
float readn(buffer* buffer, int Xn){
    int tempIndex;
    tempIndex=buffer->writeIndex-(Xn+1);
    while(tempIndex<0){
        tempIndex+=BUFF_SIZE;
    }
    return buffer->buff[tempIndex];
}

正如"Oli Charlesworth"所建议的那样,如果缓冲区大小是2的幂,那么就可以简化事情。我想写读/写函数体,这样目的就更明确了。

#define BUFF_SIZE (4U)
#define BUFF_SIZE_MASK (BUFF_SIZE-1U)
struct buffer {
    float buff[BUFF_SIZE];
    unsigned writeIndex;
};
void write(struct buffer *buffer, float value) {
    buffer->buff[(++buffer->writeIndex) & BUFF_SIZE_MASK] = value;
}
float readn(struct buffer *buffer, unsigned Xn){
    return buffer->buff[(buffer->writeIndex - Xn) & BUFF_SIZE_MASK];
}

一些解释。请注意,根本没有分支(if)。我们不将数组索引限制在数组边界,而是将其与掩码进行AND运算。

如果您可以使缓冲区大小为2的幂,那么可以用无条件的位掩码代替对零的检查。在大多数处理器上,这应该更快。

这可能看起来不优雅,但很有效。通过指针访问结构元素占用了大量指令。为什么不完全删除结构并将bufferwriteIndex作为全局变量?这将大大减小readnwrite函数的大小。

我在gcc中尝试过,这里是有和没有结构的输出

带结构

_write:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %ecx
    movl    8(%ebp), %eax
    movl    16(%eax), %edx
    movl    12(%ebp), %eax
    movl    %eax, (%ecx,%edx,4)
    movl    8(%ebp), %eax
    incl    16(%eax)
    movl    8(%ebp), %eax
    cmpl    $3, 16(%eax)
    jne L1
    movl    8(%ebp), %eax
    movl    $0, 16(%eax)
L1:
    popl    %ebp
    ret

无结构。即使bufferwriteIndex成为全局

_write:
    pushl   %ebp
    movl    %esp, %ebp
    movl    _writeIndex, %edx
    movl    8(%ebp), %eax
    movl    %eax, _buff(,%edx,4)
    incl    _writeIndex
    cmpl    $3, _writeIndex
    jne L1
    movl    $0, _writeIndex
L1:
    popl    %ebp
    ret

用指针跟踪循环缓冲区的开始和结束可能比数组索引快一点,因为在后者的情况下,地址将在运行时计算。请尝试将readIndex和writeIndex替换为float*。然后代码将是

*buffer->writeIndex = value;
buffer->writeIndex++;
if(buffer->writeIndex == buffer + BUFF_SIZE)
  buffer->writeIndex=buffer->buff;

buffer + BUFF_SIZE仍然是一个常量表达式,编译器将在编译时将其转换为固定地址。

接受的答案包含不正确的代码,将调用未定义的行为。以下更正:

#define BUFF_SIZE (4U)
#define BUFF_SIZE_MASK (BUFF_SIZE-1U)
struct buffer {
    float buff[BUFF_SIZE];
    unsigned writeIndex;
};
void write(struct buffer *buffer, float value) {
    buffer->buff[(++buffer->writeIndex) & BUFF_SIZE_MASK] = value;
}
float readn(struct buffer *buffer, unsigned Xn){
    return buffer->buff[(buffer->writeIndex - Xn) & BUFF_SIZE_MASK];
}

原始答案中的错误是假定"int"将换行。将二进制掩码与int一起使用也是不明智的。

最新更新