なぜ私のデータが揃っていないように見えるのですか?
-
24-10-2019 - |
質問
いくつかの罪とコサインの価値を事前に計算し、それらを整列ブロックに保存し、後でSSE計算に使用する方法を理解しようとしています。
プログラムの開始時に、メンバーと一緒にオブジェクトを作成します。
static __m128 *m_sincos;
次に、コンストラクターのそのメンバーを初期化します。
m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++)
m_sincos[t] = _mm_set_ps(cos(t), sin(t), sin(t), cos(t));
M_SINCOSを使用するとき、私は3つの問題に遭遇します。
- データは調整されていないようです
movaps xmm0, m_sincos[t] //crashes
movups xmm0, m_sincos[t] //does not crash
- 変数が正しくないようです
movaps result, xmm0 // returns values that are not what is in m_sincos[t]
//Although, putting a watch on m_sincos[t] displays the correct values
- 本当に私を混乱させるのは、これがすべてを機能させるということです(しかし遅すぎます):
__m128 _sincos = m_sincos[t];
movaps xmm0, _sincos
movaps result, xmm0
解決
m_sincos[t]
C式です。ただし、アセンブリの指示では(__asm
?)、それはx86アドレス指定モードとして解釈され、結果がまったく異なります。たとえば、VS2008 SP1コンパイル:
movaps xmm0, m_sincos[t]
into :(アプリがデバッグモードでクラッシュしたときの分解ウィンドウを参照)
movaps xmm0, xmmword ptr [t]
その解釈は、変数のアドレスに保存されている128ビット値をコピーしようとします t
xmm0に。 t
, ただし、整理されていないアドレスでは、32ビット値です。命令を実行することは、アライメントの障害を引き起こす可能性が高く、奇妙なケースで誤った結果を得るでしょう t
のアドレスが揃っています。
適切なx86アドレス指定モードを使用して、これを修正できます。これが遅いが明確なバージョンです:
__asm mov eax, m_sincos ; eax <- m_sincos
__asm mov ebx, dword ptr t
__asm shl ebx, 4 ; ebx <- t * 16 ; each array element is 16-bytes (128 bit) long
__asm movaps xmm0, xmmword ptr [eax+ebx] ; xmm0 <- m_sincos[t]
サイドノート:
これを完全なプログラムに入れたとき、奇妙なことが起こります:
#include <math.h>
#include <tchar.h>
#include <xmmintrin.h>
int main()
{
static __m128 *m_sincos;
int Bins = 4;
m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++) {
m_sincos[t] = _mm_set_ps(cos((float) t), sin((float) t), sin((float) t), cos((float) t));
__asm movaps xmm0, m_sincos[t];
__asm mov eax, m_sincos
__asm mov ebx, t
__asm shl ebx, 4
__asm movaps xmm0, [eax+ebx];
}
return 0;
}
これを実行すると、レジスタウィンドウに目を向けると、奇妙なことに気付くかもしれません。結果は正しいですが、 xmm0
正しい値を取得しています 前 movaps
命令が実行されます。それはどのように起こりますか?
生成されたアセンブリコードを見ると、それが示されています _mm_set_ps()
sin/cosの結果をロードします xmm0
, 、次に、メモリアドレスに保存します m_sincos[t]
. 。しかし、値はそこに残ります xmm0
それも。 _mm_set_ps
関数呼び出しではなく、「本質的」です。使用した後に使用するレジスタの値を復元しようとはしません。
これから取るべき教訓がある場合、SSE本質的な関数を使用する場合、全体にそれらを使用して、コンパイラがあなたのために物事を最適化できるようにするかもしれません。それ以外の場合は、インラインアセンブリを使用している場合は、それを全体的に使用してください。
他のヒント
明示的にコーディングするのではなく、常にInstrinsicsを使用するか、そのままにしておく必要があります。これは、__asmが64ビットコードにポータブルではないためです。