2016-01-04 15 views
25

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はプライベートですか?問題はありません。コピーして貼り付けてください。元の質問はまだ残っています(私は今回避策がある場合でも)。

答えて

0

この問題は、Vector4に4つのlongが含まれ、DirectX Vector4に4つのFloatが含まれているという事実からくるものです。いずれの場合も、ベクターを渡すだけでXを追加するだけで、W、Y、Zは変更しなくてもコピーする必要があるため、コードがはるかに複雑になります。ベクトルは、各「新しいSomeWrapper(v)」の間および最後に関数外にコピーされ、変数に結果が反映されます。

構造コードの最適化は非常に難しいです。構造体では、ヒープ割り当て時間を節約できますが、複数のコピーがあるため、コードはより長くなります。

2つのことはあなたを助けることができる:

1)ラッパーを使用するが、拡張メソッドは、ラッパーにコピーを回避しないでください。

2)戻り値に新しいベクトルを割り当てるのではなく、できるだけそれらのうちの1つを使用します(コードを最適化しますが、他の算術型のように型を変えないように注意してください)。

サンプル:

struct Vector 
{ 
    public long X; 
    public long Y; 
} 

static class VectorExtension 
{ 
    public static void AddToMe(this Vector v, long x, long y) 
    { 
     v.X += x; 
     v.Y += y; 
    } 

    public static void AddToMe(this Vector v, Vector v2) 
    { 
     v.X += v2.X; 
     v.Y += v2.Y; 
    } 
} 
+0

すべてのフィールドは浮動小数点です。構造体の折り返しは、SIMDの場合を除いてインライン展開されます。あなたの例のコードは一般的には必要ありません。私の質問は、なぜそれがSIMDのケースで壊れているのですか? (私のアップデートで書いたように、私は許容可能な回避策を見つけることができた) – Krumelur

1

Numerics.Vectorをラップすることは、コンパイラの問題だったと修正は2017年1月20日に習得することを約束したパフォーマンスの低下:

https://github.com/dotnet/coreclr/issues/7508

私にはわかりませんどのように伝播がこのプロジェクトで正確に機能するかは、修正が2.0.0 releaseの一部と思われるようです。

関連する問題