5

SSE を使用した次の 2 つの関数について考えてみましょう。

#include <xmmintrin.h>

int ftrunc1(float f) {
    return _mm_cvttss_si32(_mm_set1_ps(f));
}

int ftrunc2(float f) {
    return _mm_cvttss_si32(_mm_set_ss(f));
}

両方とも、入力に対する動作はまったく同じです。しかし、アセンブラーの出力は異なります。

ftrunc1:
    pushl   %ebp
    movl    %esp, %ebp
    cvttss2si   8(%ebp), %eax
    leave
    ret

ftrunc2:
    pushl   %ebp
    movl    %esp, %ebp
    movss   8(%ebp), %xmm0
    cvttss2si   %xmm0, %eax
    leave
    ret

つまり、 1 つの命令を余分にftrunc2使用します。movss

これは正常ですか?それは問題ですか?一番下の要素のみを設定する必要がある場合は、_mm_set1_ps常に優先する必要がありますか?_mm_set_ss


使用したコンパイラは GCC 4.5.2 with -O3 -msse.

4

2 に答える 2

5

_mm_set_ssアセンブリ命令 ( ) に直接マップされますmovss。しかし、_mm_set1_psそうではありません。

GCC、MSVC、および ICC で見たものから:

アセンブリ命令に 1 対 1 でマップする SSE 組み込み関数は、通常、「現状のまま」、つまりブラック ボックスとして扱われます。したがって、コンパイラは、命令自体全体に適用される最適化のみを行います。ただし、個々のベクター要素に対するデータフロー/依存関係の分析を必要とする最適化は行われません。

_mm_set1_ps組み込み関数と_mm_set_ps組み込み関数は単一の命令にマップされず、ほとんどのコンパイラで特殊なケースを処理します。私が見た限りでは、上に挙げた 3 つのコンパイラはすべて、個々の要素に対してデータフロー分析の最適化を実行しようとします


すべてをまとめるとmovss、コンパイラは上位 3 つの要素が重要ではないことを認識しないため、2 番目の例は を残します。_mm_set_ss(組み込みを「オープン」しようとはしません。)

于 2012-08-30T19:14:42.813 に答える
0

あなたはのぞき穴オプティマイザーの癖に遭遇しています。mov何らかの理由で、最初のケースでは、に折りたたむことができcvttss2si、2番目のケースでは失敗することがわかります。問題は、それは重要ですか?余分な移動命令はほとんどですfree-命令ストリームと追加のデコードスロットで余分な4バイトを使用しますが、両方のシーケンスには同じ数の実行スロットと同じ数のロード/ストアスロットが必要です(これは通常重要です)。唯一の潜在的な問題点は、ifetchの4バイトの追加ですが、ftrunc1は10バイトを使用し、ftrunc2は14バイトを使用するため、どちらも1つのキャッシュラインに収まるため、違いはわかりません。そのオーバーヘッドを最小限に抑えるために、私は不要な%ebp cruftについてはるかに心配します(-fno-omit-frame-pointerでコンパイルしていますか?-O3にはデフォルトで-fomit-frame-pointerが含まれています)。この関数をインライン化することで、さらにうまくいくでしょう。これにより、のぞき穴オプティマイザーの表示が完全に変わる可能性があります。そのため、どちらの場合でも機能が向上する可能性があります(または、機能が向上する場合は逆になります)。

結論として、2つの間に測定可能な速度差はありそうにありません...

于 2012-08-30T18:38:11.333 に答える