7

Cortex-M ベースのマイクロコントローラーで -127 から 127 の間の値をクランプしようとしています。

競合する 2 つの関数があり、1 つは条件を使用し、もう 1 つは分岐のないハックを使用します

// Using conditional statements
int clamp(int val) { return ((val > 127) ? 127 : (val < -127) ? -127 : val); }

// Using branchless hacks
int clamp(int val) {
    val -= -127;
    val &= (~val) >> 31;
    val += -127;
    val -= 127;
    val &= val >> 31;
    val += 127;

    return val;
}

場合によっては、これらの方法の1つが他の方法よりも高速である可能性があり、その逆もあることがわかりましたが、一般的には、どちらを使用してもかまわないので、ブランチレス技術を使用する価値があります。私の場合はうまくいきますか?

マイクロコントローラの背景を少し説明すると、これは 90 MIPS で動作する ARM ベースのマイクロコントローラで、3 段階のパイプライン、フェッチ、デコード、実行を備えており、ある種の分岐予測子があるようですが、詳細を掘り下げることはできませんでした。

4

3 に答える 3

4

ARM コード (GCC 4.6.3 with -O3):

clamp1:
    mvn r3, #126
    cmp r0, r3
    movlt   r0, r3
    cmp r0, #127
    movge   r0, #127
    bx  lr

clamp2:
    add r0, r0, #127
    mvn r3, r0
    and r0, r0, r3, asr #31
    sub r0, r0, #254
    and r0, r0, r0, asr #31
    add r0, r0, #127
    bx  lr

親指コード:

clamp1:
    mvn r3, #126
    cmp r0, r3
    it  lt
    movlt   r0, r3
    cmp r0, #127
    it  ge
    movge   r0, #127
    bx  lr

clamp2:
    adds    r0, r0, #127
    mvns    r3, r0
    and r0, r0, r3, asr #31
    subs    r0, r0, #254
    and r0, r0, r0, asr #31
    adds    r0, r0, #127
    bx  lr

ARM の条件付き実行設計により、どちらもブランチレスです。基本的にパフォーマンスは同等であると確信しています。

于 2013-02-17T01:59:11.663 に答える
3

認識すべきことは、分岐命令に関しては、ARM と x86 のアーキテクチャは大きく異なるということです。ジャンプするとパイプラインがクリアされ、スループットの点で「元の場所に戻る」ためだけに、多数のクロック サイクルが必要になる可能性があります。

先日ダウンロードしたpdf(http://simplemachines.it/doc/arm_inst.pdfのpg14 )を引用すると、

条件付き実行

  • ほとんどの命令セットでは、条件付きでのみ分岐を実行できます。
  • ただし、条件評価ハードウェアを再利用することで、ARM は効果的に命令数を増やします。
  • すべての命令には、CPU が命令を実行するかどうかを決定する条件フィールドが含まれています。
  • 実行されない命令は 1 サイクル吸収します。– 次の命令のフェッチとデコードを可能にするために、サイクルを完了する必要があります。
  • これにより、パイプラインを失速させる多くの分岐が不要になります (補充に 3 サイクル)。
  • ブランチなしで、非常に密度の高いインライン コードを使用できます。
  • いくつかの条件付き命令を実行しないことによるタイム ペナルティは、分岐またはサブルーチン呼び出しのオーバーヘッドよりも少ないことがよくあります。
于 2013-02-17T01:59:58.117 に答える
0

いいえ、C 言語には速度がありません。これは、C の実装によって導入された概念です。完全に最適なコンパイラは、これらの両方を同じマシン コードに変換します。

C コンパイラは、一般的なスタイルに準拠し、適切に定義されたコードを最適化できる可能性が高くなります。2 番目の関数は明確に定義されていません。

これらの加算と減算により、整数オーバーフローが発生する可能性があります。整数オーバーフローは未定義の動作であるため、プログラムが誤動作する可能性があります。楽観的には、ハードウェアがラッピングまたはサチュレーションを実装している可能性があります。やや楽観的ではありませんが、OS またはコンパイラがシグナルを実装したり、整数オーバーフローの表現をトラップしたりする場合があります。整数のオーバーフローを検出すると、変数を変更するパフォーマンスに影響を与える可能性があります。最悪のケースは、プログラムが完全性を失うことです。

& および >> 演算子には、符号付き型の実装定義の側面があります。それらは、トラップ表現の例である負のゼロになる場合があります。トラップ表現の使用は未定義の動作であるため、プログラムの整合性が失われる可能性があります。

OS またはコンパイラが int オブジェクトのパリティ ビット チェックを実装している可能性があります。この場合、変数が変更されるたびにパリティ ビットを再計算し、変数が読み取られるたびにパリティ ビットを検証することを想像してみてください。パリティ チェックが失敗すると、プログラムの整合性が失われる可能性があります。

最初の関数を使用します。少なくともそれは明確に定義されています。プログラムの実行速度が遅いように見える場合、このコードを最適化してもプログラムの速度は大幅に向上しない可能性があります。プロファイラーを使用して、より重要な最適化を見つけたり、より最適な OS やコンパイラを使用したり、より高速なハードウェアを購入したりします。

于 2013-02-17T04:49:23.147 に答える