10

インライン アセンブリを使用してコンパイラを打ち負かすのに苦労しています。

コンパイラが本当に、本当に速くてシンプルにするのに苦労している関数の、不自然ではない良い例は何ですか? しかし、インライン アセンブリを使用すると、比較的簡単に作成できます。

4

7 に答える 7

7

これは iPhone とアセンブリ コードに関連しているため、iPhone の世界に関連する例を示します (一部の sse や x86 asm ではありません)。実世界のアプリのアセンブリ コードを作成する場合、おそらく何らかのデジタル信号処理または画像操作になります。例: RGB ピクセルの色空間の変換、画像の jpeg/png 形式へのエンコード、音声の mp3、amr、または voip アプリケーション用の g729 へのエンコード。サウンド エンコーディングの場合、コンパイラによって効率的な asm コードに変換できないルーチンが多数あります。これらのルーチンは、単に C に相当するものがありません。サウンド処理で一般的に使用されるものの例: 飽和数学、積和ルーチン、行列乗算。

飽和加算の例: 32 ビット符号付き整数の範囲は 0x8000 0000 <= int32 <= 0x7fff ffff です。2 つの int を追加すると結果がオーバーフローする可能性がありますが、これはデジタル信号処理の特定のケースでは受け入れられない可能性があります。基本的に、結果のオーバーフローまたはアンダーフローが飽和した場合、add は 0x8000 0000 または 0x7fff ffff を返す必要があります。それはそれをチェックするための完全なc関数になります。飽和加算の最適化されたバージョンは次のようになります。

int satated_add(int a, int b)
{
    int 結果 = a + b;

    if (((a^b) & 0x80000000) == 0)
    {
        if ((結果 ^ a) & 0x80000000)
        {
            結果 = (a < 0) ? 0x80000000 : 0x7ffffff;
        }
    }
    結果を返します。
}

複数の if/else を実行してオーバーフローをチェックすることも、x86 ではオーバーフロー フラグをチェックすることもできます (これには asm も使用する必要があります)。iPhone は、dsp asm を持つ armv6 または v7 CPU を使用します。したがって、saturated_add複数のブランチ (if/else ステートメント) と 2 つの 32 ビット定数を含む関数は、1 つの CPU サイクルのみを使用する 1 つの単純な asm 命令になる可能性があります。したがって、saturated_add を使用して asm 命令を使用するだけで、アルゴリズム全体が 2 ~ 3 倍高速になります (サイズが小さくなります)。QADD のマニュアルは次のとおりです: QADD

長いループで実行されることが多いコードの他の例は次のとおりです。

res1 = a + b1*c1;
res2 = a + b2*c2;
res3 = a + b3*c3;

ここでは最適化できないものは何もないように思えますが、ARM CPU では、単純な乗算を行うよりも少ないサイクルで済む特定の dsp 命令を使用できます! そうです、特定の命令を含む a+b * c は、単純な a*b よりも高速に実行できます。このような場合、コンパイラはコードのロジックを理解できず、これらの dsp 命令を直接使用することはできません。そのため、コードを最適化するために手動で asm を記述する必要がありますが、必要なコードの一部のみを手動で記述する必要があります。最適化されました。単純なループを手動で書き始めると、ほぼ確実にコンパイラーに勝てなくなります! Web には、fir フィルター、amr エンコード/デコードなどをコード化するためのインライン アセンブリに関する優れた論文が複数あります。

于 2009-07-28T21:24:38.307 に答える
6

あなたがアセンブリの第一人者でない限り、コンパイラを打ち負かす可能性は非常に低いです。

上記のリンクからの断片、

たとえば、ビット指向の「XOR%EAX、%EAX」命令は、x86の初期世代でレジスタをゼロに設定する最速の方法でしたが、ほとんどのコードはコンパイラによって生成され、コンパイラがXOR命令を生成することはめったにありません。そのため、IA設計者は、頻繁に発生するコンパイラ生成命令を組み合わせデコードロジックの前に移動して、リテラルの「MOVL $ 0、%EAX」命令をXOR命令よりも高速に実行することにしました。

于 2009-07-16T17:40:16.993 に答える
5

一般的な「Strait C」実装を使用して、単純な相互相関を実装しました。そして、利用可能なタイムスライスよりも時間がかかると、アルゴリズムの明示的な並列化に頼り、プロセッサ組み込みを使用して、特定の命令を計算で使用するように強制しました。この特定のケースでは、計算時間が 30 ミリ秒以上から 4 ミリ秒強に短縮されました。次のデータ取得が行われる前に、処理を完了するための 15 ミリ秒のウィンドウがありました。

これは、VLWI プロセッサでの SIMD タイプの最適化です。これには、基本的にソース コードで関数呼び出しの外観を与えるアセンブリ言語命令であるプロセッサ組み込み関数が 4 つほど必要です。インライン アセンブリでも同じことができますが、構文とレジスタの管理は、プロセッサの組み込み関数を使用した方が少し優れています。

それ以外は、サイズが重要な場合は、アセンブラーが王様です。私は 512 バイト未満でフルスクリーンのテキスト エディタを書いた男と一緒に学校に行きました。

于 2009-07-16T17:45:33.677 に答える
5

単語を特定のビット数だけローテーションする必要があるチェックサム アルゴリズムがあります。それを実装するために、私はこのマクロを持っています:

//rotate word n right by b bits
#define ROR16(n,b) (((n)>>(b))|(((n)<<(16-(b)))&0xFFFF))

//... and inside the inner loop: 
sum ^= ROR16(val, pos);

VisualStudio リリース ビルドは次のように展開されます: ( valis in ax, posis in dx, sumis in bx)

mov         ecx,10h 
sub         ecx,edx 
mov         ebp,eax 
shl         ebp,cl 
mov         cx,dx 
sar         ax,cl 
add         esi,2 
or          bp,ax 
xor         bx,bp 

より効率的な同等の手動生成アセンブリは次のようになります。

 mov       cl,dx
 ror       ax,cl
 xor       bx,ax

ror純粋な「c」コードから命令を発行する方法がわかりません。しかし...
これを書きながら、コンパイラの組み込み関数を思い出しました。次のコマンドを使用して、2 番目の命令セットを生成できます。

sum ^= _rotr16(val,pos);

したがって、私の答えは次のとおりです。純粋な C コンパイラに勝てると思っていても、インライン アセンブリに頼る前に組み込み関数を確認してください。

于 2009-07-16T21:20:05.617 に答える