11

循環バッファ コードの効率を改善するための助けが必要です。

stackoverflow を調べたところ、循環バッファーに関する (ほぼ) すべてのトピックが、そのようなバッファーの使用または循環バッファーの基本的な実装に関するものであることがわかりました。超効率的にする方法についての情報が本当に必要です。

このバッファは、単一の高精度 FPU を備えた STM32F4 マイクロコントローラで使用する予定です。特に write() と readn() 関数を多用する予定です。ここでは文字通り 1 秒間に数百万回の呼び出しを行っているため、数クロック サイクルを削ることで実際に違いが生まれます。

最も重要なコードをここに置きます。完全なバッファ コードは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];
}
4

5 に答える 5

15

「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 します。

于 2012-03-15T11:05:46.193 に答える
12

バッファ サイズを 2 のべき乗にすることができれば、ゼロに対するチェックを無条件のビット マスキングに置き換えることができます。ほとんどのプロセッサでは、これはより高速です。

于 2012-03-15T10:46:38.607 に答える
2

これはエレガントに見えないかもしれませんが、効率的です。ポインターを介して構造要素にアクセスすると、多くの命令が必要になります。構造体を完全に削除して、グローバル変数として作成bufferしてみませんか? これにより、および関数writeIndexのサイズが大幅に縮小されます。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

構造なし。つまり、作成bufferおよびwriteIndexグローバルとして

_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
于 2012-03-15T11:43:55.700 に答える
2

ポインターを使用して循環バッファーの開始と終了を追跡すると、配列のインデックス付けよりも少し高速になる可能性があります。これは、後者の場合、実行時にアドレスが計算されるためです。readIndex と writeIndex をfloat*代わりに置き換えてみてください。コードは次のようになります

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

buffer + BUFF_SIZE定数式のままであり、コンパイラはコンパイル時にそれを固定アドレスに変換します。

于 2012-03-15T11:54:06.123 に答える
1

受け入れられた回答には、正しくないコードが含まれており、未定義の動作を引き起こします。以下訂正:

#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 でバイナリ マスクを使用することも賢明ではありません。

于 2019-03-08T01:30:30.623 に答える