TL; DR:System.Numerics.Vectorsタイプを高価なものにする理由は何ですか?System.Numerics.VectorXをラップするために高価です - なぜですか?
次のコードを考えてみましょう:
[MethodImpl(MethodImplOptions.NoInlining)]
private static long GetIt(long a, long b)
{
var x = AddThem(a, b);
return x;
}
private static long AddThem(long a, long b)
{
return a + b;
}
これは(x64)のにJITます:
00007FFDA3F94500 lea rax,[rcx+rdx]
00007FFDA3F94504 ret
およびx86:
今00EB2E20 push ebp
00EB2E21 mov ebp,esp
00EB2E23 mov eax,dword ptr [ebp+10h]
00EB2E26 mov edx,dword ptr [ebp+14h]
00EB2E29 add eax,dword ptr [ebp+8]
00EB2E2C adc edx,dword ptr [ebp+0Ch]
00EB2E2F pop ebp
00EB2E30 ret 10h
、私はこの中を包む場合構造体、例えば
public struct SomeWrapper
{
public long X;
public SomeWrapper(long X) { this.X = X; }
public static SomeWrapper operator +(SomeWrapper a, SomeWrapper b)
{
return new SomeWrapper(a.X + b.X);
}
}
および変更GetIt
、たとえば、
private static long GetIt(long a, long b)
{
var x = AddThem(new SomeWrapper(a), new SomeWrapper(b)).X;
return x;
}
private static SomeWrapper AddThem(SomeWrapper a, SomeWrapper b)
{
return a + b;
}
JITコンパイルされた結果はまだ正確直接(AddThem
、およびSomeWrapper
過負荷演算子とコンストラクタは、すべてのインライン化さ)ネイティブ型を使用した場合と同じ。予想通り。
これをSIMD対応のタイプ、たとえばSystem.Numerics.Vector4
:
[MethodImpl(MethodImplOptions.NoInlining)]
private static Vector4 GetIt(Vector4 a, Vector4 b)
{
var x = AddThem(a, b);
return x;
}
それがにJITコンパイルされています。私は(最初の例と同様に)構造体でVector4
を包む場合、しかし
00007FFDA3F94640 vmovupd xmm0,xmmword ptr [rdx]
00007FFDA3F94645 vmovupd xmm1,xmmword ptr [r8]
00007FFDA3F9464A vaddps xmm0,xmm0,xmm1
00007FFDA3F9464F vmovupd xmmword ptr [rcx],xmm0
00007FFDA3F94654 ret
:
public struct SomeWrapper
{
public Vector4 X;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SomeWrapper(Vector4 X) { this.X = X; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SomeWrapper operator+(SomeWrapper a, SomeWrapper b)
{
return new SomeWrapper(a.X + b.X);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static Vector4 GetIt(Vector4 a, Vector4 b)
{
var x = AddThem(new SomeWrapper(a), new SomeWrapper(b)).X;
return x;
}
私のコードは今あります全体的に多くのJITted:
00007FFDA3F84A02 sub rsp,0B8h
00007FFDA3F84A09 mov rsi,rcx
00007FFDA3F84A0C lea rdi,[rsp+10h]
00007FFDA3F84A11 mov ecx,1Ch
00007FFDA3F84A16 xor eax,eax
00007FFDA3F84A18 rep stos dword ptr [rdi]
00007FFDA3F84A1A mov rcx,rsi
00007FFDA3F84A1D vmovupd xmm0,xmmword ptr [rdx]
00007FFDA3F84A22 vmovupd xmmword ptr [rsp+60h],xmm0
00007FFDA3F84A29 vmovupd xmm0,xmmword ptr [rsp+60h]
00007FFDA3F84A30 lea rax,[rsp+90h]
00007FFDA3F84A38 vmovupd xmmword ptr [rax],xmm0
00007FFDA3F84A3D vmovupd xmm0,xmmword ptr [r8]
00007FFDA3F84A42 vmovupd xmmword ptr [rsp+50h],xmm0
00007FFDA3F84A49 vmovupd xmm0,xmmword ptr [rsp+50h]
00007FFDA3F84A50 lea rax,[rsp+80h]
00007FFDA3F84A58 vmovupd xmmword ptr [rax],xmm0
00007FFDA3F84A5D vmovdqu xmm0,xmmword ptr [rsp+90h]
00007FFDA3F84A67 vmovdqu xmmword ptr [rsp+40h],xmm0
00007FFDA3F84A6E vmovdqu xmm0,xmmword ptr [rsp+80h]
00007FFDA3F84A78 vmovdqu xmmword ptr [rsp+30h],xmm0
00007FFDA3F84A7F vmovdqu xmm0,xmmword ptr [rsp+40h]
00007FFDA3F84A86 vmovdqu xmmword ptr [rsp+20h],xmm0
00007FFDA3F84A8D vmovdqu xmm0,xmmword ptr [rsp+30h]
00007FFDA3F84A94 vmovdqu xmmword ptr [rsp+10h],xmm0
00007FFDA3F84A9B vmovups xmm0,xmmword ptr [rsp+20h]
00007FFDA3F84AA2 vmovups xmm1,xmmword ptr [rsp+10h]
00007FFDA3F84AA9 vaddps xmm0,xmm0,xmm1
00007FFDA3F84AAE lea rax,[rsp]
00007FFDA3F84AB2 vmovupd xmmword ptr [rax],xmm0
00007FFDA3F84AB7 vmovdqu xmm0,xmmword ptr [rsp]
00007FFDA3F84ABD vmovdqu xmmword ptr [rsp+70h],xmm0
00007FFDA3F84AC4 vmovups xmm0,xmmword ptr [rsp+70h]
00007FFDA3F84ACB vmovupd xmmword ptr [rsp+0A0h],xmm0
00007FFDA3F84AD5 vmovupd xmm0,xmmword ptr [rsp+0A0h]
00007FFDA3F84ADF vmovupd xmmword ptr [rcx],xmm0
00007FFDA3F84AE4 add rsp,0B8h
00007FFDA3F84AEB pop rsi
00007FFDA3F84AEC pop rdi
00007FFDA3F84AED ret
JITは何らかの理由でレジスタを使用できず、代わりに一時変数を使用するようになったようですが、理由を理解できません。最初に私はそれが整列の問題かもしれないと思ったが、なぜそれがxmm0に最初にロードされているのか理解できず、メモリへのラウンドトリップを決める。
ここでは何が起こっていますか?さらに重要なことに、私はそれを修正することができますか?
私がこのような構造をラップしたいのは、実装に若干のSIMDのメリットがあるAPIを使用するのレガシーコードのです。
EDIT:だから、coreclr sourceで掘り下げた後、実際にはSystem.Numericsクラスは特別なものではないことがわかりました。私はちょうど私のメソッドにSystem.Numerics.JitIntrinsic
属性を追加する必要があります。 JITは私の実装を独自のものに置き換えます。 JitIntrinsic
はプライベートですか?問題はありません。コピーして貼り付けてください。元の質問はまだ残っています(私は今回避策がある場合でも)。
すべてのフィールドは浮動小数点です。構造体の折り返しは、SIMDの場合を除いてインライン展開されます。あなたの例のコードは一般的には必要ありません。私の質問は、なぜそれがSIMDのケースで壊れているのですか? (私のアップデートで書いたように、私は許容可能な回避策を見つけることができた) – Krumelur