2016-09-04 3 views
4

私は特別な使用事例を念頭に置いていません。私はこれが本当にインテルの組み込み関数の設計上の瑕疵/制限であるのか、それとも何か不足しているのかどうかを尋ねています。コンパイラが上位要素をゼロにする命令を無駄にすることなく、スカラーをベクトルにマージする方法はありますか?インテルの組み込み関数の設計上の制限?

スカラー浮動小数点を既存のベクトルに結合する場合、インテルの組み込み関数を使用して、要素の高精度化やベクトルへのスカラーのブロードキャストを行わない方法はありません。私はGNU Cのネイティブベクトル拡張と関連する組み込み関数については調べていません。

余分な組み込み関数は最適化されていても、gcc(5.4または6.2)では最適化されていないと悪くありません。関連する理由から、その組み込み関数がベクトルargsだけを取るという理由で、pmovzxまたはinsertpsをロードとして使う良い方法もありません。 (とgccはASM命令にスカラー値>ベクトルロードを折るしません。)

__m128 replace_lower_two_elements(__m128 v, float x) { 
    __m128 xv = _mm_set_ss(x);  // WANTED: something else for this step, some compilers actually compile this to a separate insn 
    return _mm_shuffle_ps(v, xv, 0); // lower 2 elements are both x, and the garbage is gone 
} 

のgcc 5.3 -march =のNehalemの-O3の出力を、そのインテルのCPUのためのSSE4.1とチューニングを可能にするために:(それはですさらに悪いことに、SSE4.1なしで、上位要素をゼロにするための複数の命令)。

insertps xmm1, xmm1, 0xe # pointless zeroing of upper elements. shufps only reads the low element of xmm1 
    shufps xmm0, xmm1, 0  # The function *should* just compile to this. 
    ret 

TLは:DR:あなたが実際にこれを効率的に行うことができれば、この質問の残りの部分は、ちょうど求めているし、そうでない場合その理由ではありません。


打ち鳴らすのシャッフルオプティマイザはこの権利を取得し、高い要素(_mm_set_ss(x)を)ゼロ、またはそれら(_mm_set1_ps(x))にスカラを複製する手順を無駄にしません。コンパイラが最適化しなければならないものを書くのではなく、最初にC言語で "効率的に"書く方法はありませんか?最近のgcc でさえ、を最適化していないので、これは実際の問題です。


__m256 _mm256_castps128_ps256 (__m128 a)の> 128B同等のスカラー値があった場合、これは可能であろう。つまり、上位要素に定義されていないゴミを持つ__m128を生成し、下位要素の浮動小数点を生成します。スカラーfloat/doubleが既にxmmレジスタにあった場合は、asm命令をゼロにコンパイルします。

次の組み込み関数はありませんが、である必要があります。

  • 上記の通り_mm256_castps128_ps256のスカラー - > __ m128に相当します。スカラー・イン・レジスターの場合の最も一般的な解決策。
  • __m128 _mm_move_ss_scalar (__m128 a, float s):ベクトルaの下位要素をスカラーsに置き換えます。これは汎用スカラ - > __ m128(前の箇条書きの点)がある場合は実際には必要ありません。 (movssのreg-reg形式は、ゼロのロード形式とは異なり、マージします。どちらの場合も、上位要素の値が0になるmovdとは異なります。偽の依存関係のないスカラ浮動小数点を含むレジスタをコピーするには、movapsを使用します)。不都合な安全な方法でgccを最適化しないため、
  • __m128i _mm_loadzxbd (const uint8_t *four_bytes)と他のサイズPMOVZX/PMOVSX:AFAICT, there's no good safe way to use the PMOVZX intrinsics as a loadがあります。
  • __m128 _mm_insertload_ps (__m128 a, float *s, const int imm8)INSERTPSは、ロードとは異なる動作をします。imm8の上位2ビットは無視され、(メモリ内のベクトルからの要素ではなく)実効アドレスで常にスカラーをとります。これにより、マップされていないページの直前にある場合は、16Bで整列されていないアドレスでも動作し、エラーが発生していなくても動作します。float

    PMOVZXと同様に、gccは上部要素ゼロ化_mm_load_ss()をINSERTPSのメモリオペランドに折りたたまない。 (imm8の上位2ビットが両方ともゼロでない場合、0xはinsertps xmm0,xmm0,fooにコンパイルできます。src要素が実際にはMOVSSによってメモリから生成されたゼロの場合、vecの要素と異なるimm8がコンパイルされます。実際


の両方が安全であるもののいずれかをエミュレートする任意の実行可能な回避方法はあります)、その場合にはXORPS/BLENDPSを使用しています(触れるかもしれない例えば負荷16Bによって-O0で壊れません次のページとsegfault)、そして効率的な(-O3での現在のgccと少なくともclangの無駄な命令はなく、他のメジャーなコンパイラも好むでしょう)?好ましくはまた可読な方法でもよいが、必要ならば __m128 float_to_vec(float a){ something(a); }のようなインラインラッパー関数の後ろに置くことができる。

インテルがそのようなイントリンシックスを導入しない理由はありますか? _mm256_castps128_ps256を追加すると同時に、未定義の上位要素を持つfloat - > __ m128を追加できました。 これはコンパイラの内部で実装するのが難しいのですか?具体的には、具体的にはICC内部? x86-64の上


主要な呼び出し規約(SysVのまたはMS __vectorcall)がXMM0のArg最初のFPを取得し、未定義の上部要素と、XMM0にスカラーFPの引数を返します。 (ABIドキュメントについては、タグwikiを参照してください)。これは、コンパイラが未知の上位要素を持つレジスタにスカラーfloat/doubleを持つことは珍しくないことを意味します。これはベクター化された内部ループではまれであるため、これらの無駄な命令を避けることはほとんどがちょっとしたコードサイズを節約すると思います。

内部ループで使用する場合があります(たとえば、VPERMDシャッフルマスクのLUTの場合、キャッシュフットプリントに4倍の係数を保存する代わりに、メモリに32ビットを埋め込んだ各インデックスを保存するなど)。 )。


pmovzx-AS-負荷の問題は、今しばらくの間、私を悩ませてきた、とthe original version of this questionはXMMレジスタ内のスカラ浮動小数点を使用することの関連問題について考えて私を得ました。おそらくpmovzxの負荷としてスカラー - > __ m128よりも多くのユースケースがあります。

+1

__m128 float_to_vec(float x){ return _mm_set_ss(x); }がにコンパイルされます。 '_mm_load_ *'または '_mm_set_ *'の組み込み関数が得られると、本当に偽のコードを生成します。ここでの質問で与えられた例では、4つの命令(!)を得ることができます: 'movaps xmm2、xmm1; xorps xmm3、xmm3; movss xmm3、xmm2; shufps xmm0、xmm3、0'。私は基本的にあきらめました。メモリに流出しないアセンブリを生成することができれば、それを勝利と呼びます。 –

答えて

3

これはGNU Cのインラインasmで実行できますが、これは醜いものであり、定数伝播(https://gcc.gnu.org/wiki/DontUseInlineAsm)を含む多くの最適化を無効にします。 これは受け入れられる回答ではありません。質問の一部ではなく答えとして追加するので、という短い質問は巨大ではありません。

// don't use this: defeating optimizations is probably worse than an extra instruction 
#ifdef __GNUC__ 
__m128 float_to_vec_inlineasm(float x) { 
    __m128 retval; 
    asm ("" : "=x"(retval) : "0"(x)); // matching constraint: provide x in the same xmm reg as retval 
    return retval; 
} 
#endif 

これは、必要に応じて、単一retにコンパイルしないと、ベクターにあなたshufpsスカラーをできるようにインライン化されます:

gcc5.3 
float_to_vec_and_shuffle_asm(float __vector(4), float): 
    shufps xmm0, xmm1, 0  # tmp93, xv, 
    ret 

Godbolt compiler explorerにこのコードを参照してください。

これは明らかに、純粋なアセンブリ言語では些細なことですが、コンパイラを使って欲しくない、あるいは必要のない命令を出さないように戦う必要はありません。


私はちょうどret命令にコンパイル__m128 float_to_vec(float a){ something(a); }を書くための任意の実際の方法を発見していません。 doubleの試みは、_mm_undefined_pd()_mm_move_sd()を使用すると実際にはgccでコードが悪化します(上のゴッドボルトのリンクを参照)。 the existing float->__m128 intrinsicsのいずれのヘルプもありません。オフトピック


:実際の_mm_set_ss()コード-GEN戦略:あなたは、上の要素をゼロに持ってコードを書く行うと、コンパイラは、戦略の興味深い範囲から選びます。いくつかの良い、いくつか奇妙な。上記のGodboltのリンクで見られるように、同じコンパイラ(gccまたはclang)のdoubleとfloatの違いも同じです。

一つの例:私はMSVC数え切れないほどにこれと密接に関連した問題と格闘してきた

# gcc5.3 -march=core2 
    movd eax, xmm0  # movd xmm0,xmm0 would work; IDK why gcc doesn't do that 
    movd xmm0, eax 
    ret 

# gcc5.3 -march=nehalem 
    insertps  xmm0, xmm0, 0xe 
    ret 

# clang3.8 -march=nehalem 
    xorps xmm1, xmm1 
    blendps xmm0, xmm1, 14   # xmm0 = xmm0[0],xmm1[1,2,3] 
    ret 
+0

MSVCでは 'r.f32 [0] = x;'を使用し、clangでは 'r [0] = x;'を使用すると 'asm(" ":" x " (retval): "0"(x));、定数フォールドを失うことなく。 – plasmacel

+0

@plasmacel:良いアイデアですが、gccは '_mm_set_ss'と同じようにコンパイルしますが、ICCは完全に混乱します。 https://godbolt.org/g/RC6CWb。 gccはPXORや整数XORとの偽の依存関係を壊すのが本当に好きです。初期化されていない変数に使用されているレジスタをゼロにし、*同じ*レジスタの残りの部分をゼロにすることが本当に熱心であることに関係しているのだろうかと思います。 –

+0

はい、GCCとICCの場合は、asmバージョンに戻ります。ちなみに、clang 3.9では、おそらくコンパイラのバグに関係するasmバージョンもコンパイルされません。 – plasmacel

関連する問題