2016-03-26 1 views
-3

私の使命は、C#のスワップアルゴリズム任意のクラスやデータタイプT上で動作し、可能な限り効率的であるC#で最適なスワップ(T a、T b)アルゴリズムを書くにはどうすればよいですか?この孤独な金曜日の夜のため

public void Swap<T> (ref T a, ref T b) 
{ 
    // ... 
} 

に書くことです。これまで構築してきた方法を批評するのに役立ちます。まず第一に、正しいのでしょうか?どのようにSkeet認定を受けることができますか?

public void Swap<T> (ref T a, ref T b) 
{ 
    // Not sure yet if T is a data or value type. 
    // Will handle swapping algorithm differently depending on each case. 
    Type type = T.GetType(); 
    if ((type.IsPrimitive()) || (type == typeof(Decimal))) 
    { 
     // this means the we can XOR a and b, the fastest way of swapping them 
     a ^= b ^= a ^= b; 
    } 
    else if (type.IsValueType()) 
    { 
     // plain old swap in this case 
     T temp = a; 
     a = b; 
     b = temp; 
    } 
    else // is class type 
    { 
     // plain old swap???   
    } 
} 

その他の質問:

  • は型チェックのオーバーヘッドは、XORスワップのいずれかのパフォーマンス上の利点を相殺だろうか?
  • 各ケースのパフォーマンスを最適化したい場合は、値タイプとクラスタイプの違いはどのように扱われますか?
  • 2つの要素がXORableかどうかを確認する良い方法はありますか?

のように、のは、私は値の型をとり、このメソッドのバージョンを望んでいたとしましょう:彼らは非常に大きないる場合には

明らか
public void Swap<T> (ref T a, ref T b) where T : struct 
{ 
    // 
} 

、私は全体の構造体のコピーを作成する必要はありません。だから私はスニペット++このCの同等やりたい:

template <typename T> 
void PointerSwap<T> (T * a, T * b) 
{ 
    T * temp = a; 
    a = b; 
    b = temp; 
} 

を、私はC#であなたがそれらをクラス(参照型)にボクシングによって構造体で得ることができることを知っているが、ISN」それには大きなオーバーヘッドがありますか?単純な整数型参照(いわゆるC++の "メモリアドレス")をパラメータとして使用する方法はありませんか?

C++は

+6

タイプチェックは、通常のスワップの代わりにXORを使用することによるメリットを確実に取り消します。 – MarcinJuraszek

+2

参照型に非常に効率的な 'Interlocked.Exchange (T、T)'を使用するだけでなく、ラッパーを作成するのではなく、明示的な値型のオーバーロードを使用するのはなぜですか? https://msdn.microsoft.com/en-us/library/system.threading.interlocked.exchange(v=vs.110).aspx。あなたのラッパーは、循環的な複雑さにおいて測定可能なオーバーヘッドを持っています。 – MickyD

+0

@MickyD Interlockedはアトミックです。原子操作はよりコストがかかる傾向があります。 –

答えて

9

... C#のよりそんなに多く理にかなっておそらくそのような書き込み「最適化」のコードに対する最善の引数は、それが単一の文だということであり、あなたはそれが間違っていました(することになっていますかC#またはC++、ただしそれぞれの理由により異なる)。これはスワップ二つの変数はありません:

a ^= b ^= a ^= b; 

これが行われます。この回答の残りの部分については

a ^= b; 
b ^= a; 
a ^= b; 

を、私はあなたが後者を意味と仮定するつもりです。


私は、彼らは非常に大きないる場合には、全構造体のコピーを作成する必要はありません。だから私はこのC++スニペットと同等のものをやりたい:

template <typename T> 
void PointerSwap<T> (T* a, T* b) { 
    T* temp = a; 
    a = b; 
    b = temp; 
} 

[...]単にパラメータとして整数型の参照を(Cで「メモリアドレス」++いわゆる)を使用する方法はありませんか?

C++スニペットは、2つの外部変数をスワップできません。代わりに、2つのローカル変数の値を単純に入れ替えるだけで、外部コードに関する限り、それはノーオペレーションです。

C#では、refは、ここで試みたのと同じことを達成する最も簡単な方法であり、最初にこの間違いを許さなかったでしょう。

いずれにしても、このようなポインタを含むと何らかの理由で何もコピーせずに値を入れ替えることができると思うのはなぜかわかりません。あなたのスニペットであっても、変数がどこにあるかは変わっていません。変数に含まれているデータを引き続き移動しています。


二つの要素がXORableているかどうかを確認するためのより良い方法はありますか?

任意のoperatoroperator^(または種類のタイプを説明し何のC#の制約がないためために、(完全にパフォーマンスをあきらめると反射を使用してから除いて)それをチェックする方法はありませんでもですその問題)。あなたがチェックしたことはタイプがBooleanByteSByteInt16UInt16Int32UInt32Int64UInt64IntPtrUIntPtrCharDoubleSingleDecimalであるかどうかです。あなたはoperator^を持つ他のタイプを見逃すでしょう。 Singlefloat)とDecimaldecimal)のようなタイプを含めることにします。これらのタイプには、最初にoperator^がありません。

オペレータを確実に確認できたとしても、operator^への呼び出しが必要な処理を行い、スワップが可能であるという保証はありません。それは単なる任意の方法であり、何かをしている可能性があります。

そして、あなたはあなたの一般的な制約がoperator^との独占的なタイプを記述していることを確認しない限り、このすべてが、でもあなたがいない実際にコールオペレータことができるという事実には触れていません。それができない場合、次の選択肢は、operator^を呼び出すことができるタイプTのそれぞれのバージョンを同じように見せても、特殊バージョンを書くことです。これらの特殊なメソッドを呼び出すには、上記のタイプごとに個別のタイプチェックが必要です。そして私は間違ったキャストは私もボクシングの原因になると推定しています。


型チェックのオーバーヘッドは、XORスワップのいずれかのパフォーマンス上の利点を相殺だろうか?

はい。

しかし、最初に、私は何かを指摘してみましょう:あなたのコードが有効ではありませんので、私はこれがtypeof(T)またはa.GetType()であることを意図しているかどうかはわからないし、あなたのコードは、(それを明確にしない

Type type = T.GetType(); 
if (type.IsPrimitive || type == typeof(Decimal)) { 

をし、いずれかの可能性のように見える)。実際にはGetType()呼び出しで、typeof()演算子ではない場合は、構造体タイプそのもののTの値の場合、typeof(T)演算子とGetType()メソッドで同じ結果が得られるため、ただこれで:

[MethodImpl(MethodImplOptions.AggressiveInlining)] 
static bool IsPrimitiveA<T>(T obj) => typeof (T).IsPrimitive; 

[MethodImpl(MethodImplOptions.AggressiveInlining)] 
static bool IsPrimitiveB<T>(T obj) => obj.GetType().IsPrimitive; 

typeofバージョンのリリース解体(インライン):

いずれの場合においても

var type = typeof(T); 
if(type.IsPrimitive || type == typeof(Decimal)) { 

、これらの2つの方法のいずれかを考えます

55:    var a = IsPrimitiveA(1); 
001E0453 B9 BC F6 B2 71  mov   ecx,71B2F6BCh 
001E0458 E8 E1 A3 76 72  call  7294A83E 
001E045D 8B C8    mov   ecx,eax 
001E045F 8B 01    mov   eax,dword ptr [ecx] 
001E0461 8B 40 6C    mov   eax,dword ptr [eax+6Ch] 
001E0464 FF 10    call  dword ptr [eax] 
001E0466 0F B6 F8    movzx  edi,al 

GetType()バージョンリリースの解体(インライン):

49:    var b = IsPrimitiveB(2); 
00360464 C7 45 F4 02 00 00 00 mov   dword ptr [ebp-0Ch],2 
0036046B B9 BC F6 B2 71  mov   ecx,71B2F6BCh 
00360470 E8 7F 2C E5 FF  call  001B30F4 
00360475 8B D0    mov   edx,eax 
00360477 8B 45 F4    mov   eax,dword ptr [ebp-0Ch] 
0036047A 89 42 04    mov   dword ptr [edx+4],eax 
0036047D 8B CA    mov   ecx,edx 
0036047F 39 09    cmp   dword ptr [ecx],ecx 
00360481 E8 3E C9 62 71  call  7198CDC4 
00360486 8B C8    mov   ecx,eax 
00360488 8B 01    mov   eax,dword ptr [ecx] 
0036048A 8B 40 6C    mov   eax,dword ptr [eax+6Ch] 
0036048D FF 10    call  dword ptr [eax] 
0036048F 0F B6 F0    movzx  esi,al 
00360492 8B CF    mov   ecx,edi 
00360494 E8 5B 24 DC 71  call  721228F4 

いずれの場合も、すでにこのコードの多く - と私たちもまだスワップ開始していません。純粋なスワップが非常に多くの移動や呼び出しを行わない限り、最適化されたバージョンはおそらく最初のステートメントから始まって既に遅いでしょう。


しかし、ナイーブなバージョンが実際に与えるものを見てみましょう。リファレンスソースコード:

static void Main(string[] args) 
{ 
    var a = 5; 
    var b = 10; 
    Swap(ref a, ref b); 
    Console.WriteLine(a); 
    Console.WriteLine(b); 
} 

ジェネリックナイーブスワップソースコード:

static void Swap<T>(ref T a, ref T b) 
{ 
    T temp = a; 
    a = b; 
    b = temp; 
} 

非ジェネリックXORベースのスワップソースコードは、常にフォームのものであろう:

static void Swap(ref ... a, ref ... b) 
{ 
    a ^= b; 
    b ^= a; 
    a ^= b; 
} 

私が表示されます32ビットJITコンパイラによって生成されるリリースの逆アセンブリです。私は64ビットバージョンを省略しています。なぜなら、(時には異なるレジスタの使い方を除いて)意味のある変更を見ることはできませんでしたが、あなた自身でそれらのケースを自由にテストすることができます。また、インライン化されていないメソッドの場合でも、メソッドエントリを省略し、定型文を終了します。ここで

はナイーブSwap<byte>(インライン)です:

32:    Swap<byte>(ref a, ref b); 
00210464 0F B6 55 FC   movzx  edx,byte ptr [ebp-4] 
00210468 0F B6 45 F8   movzx  eax,byte ptr [ebp-8] 
0021046C 88 45 FC    mov   byte ptr [ebp-4],al 
0021046F 88 55 F8    mov   byte ptr [ebp-8],dl 

はXORベースSwap(ref byte, ref byte)(デフォルトではインライン化ない)と比較する:

46:    a ^= b; 
000007FE99F204F0 0F B6 02    movzx  eax,byte ptr [rdx] 
000007FE99F204F3 30 01    xor   byte ptr [rcx],al 
    47:    b ^= a; 
000007FE99F204F5 0F B6 01    movzx  eax,byte ptr [rcx] 
000007FE99F204F8 30 02    xor   byte ptr [rdx],al 
    48:    a ^= b; 
000007FE99F204FA 0F B6 02    movzx  eax,byte ptr [rdx] 
000007FE99F204FD 30 01    xor   byte ptr [rcx],al 

は、ここで(インライン化)Swap<short>ナイーブです。

XORベースSwap(ref short, ref short)(デフォルトではインライン化ない)と比較する:

32:    Swap<int>(ref a, ref b); 
002E0464 8B 55 FC    mov   edx,dword ptr [ebp-4] 
002E0467 8B 45 F8    mov   eax,dword ptr [ebp-8] 
002E046A 89 45 FC    mov   dword ptr [ebp-4],eax 
002E046D 89 55 F8    mov   dword ptr [ebp-8],edx 

XORベースSwap(ref int, ref int)ないと比較する:ここで

53:    a ^= b; 
001E0498 0F BF 02    movsx  eax,word ptr [edx] 
001E049B 66 31 01    xor   word ptr [ecx],ax 
    54:    b ^= a; 
001E049E 0F BF 01    movsx  eax,word ptr [ecx] 
001E04A1 66 31 02    xor   word ptr [edx],ax 
    55:    a ^= b; 
001E04A4 0F BF 02    movsx  eax,word ptr [edx] 
001E04A7 66 31 01    xor   word ptr [ecx],ax 

ナイーブSwap<int>(インライン)がいますデフォルトでインライン):

60:    a ^= b; 
003904A0 8B 02    mov   eax,dword ptr [edx] 
003904A2 31 01    xor   dword ptr [ecx],eax 
    61:    b ^= a; 
003904A4 8B 01    mov   eax,dword ptr [ecx] 
003904A6 31 02    xor   dword ptr [edx],eax 
    62:    a ^= b; 
003904A8 8B 02    mov   eax,dword ptr [edx] 
003904AA 31 01    xor   dword ptr [ecx],eax 

彼ナイーブSwap<long>(インライン):

33:    Swap<long>(ref a, ref b); 
001D047A 8B 75 F0    mov   esi,dword ptr [ebp-10h] 
001D047D 8B 7D F4    mov   edi,dword ptr [ebp-0Ch] 
001D0480 8B 45 E8    mov   eax,dword ptr [ebp-18h] 
001D0483 8B 55 EC    mov   edx,dword ptr [ebp-14h] 
001D0486 89 45 F0    mov   dword ptr [ebp-10h],eax 
001D0489 89 55 F4    mov   dword ptr [ebp-0Ch],edx 
001D048C 89 75 E8    mov   dword ptr [ebp-18h],esi 
001D048F 89 7D EC    mov   dword ptr [ebp-14h],edi 

XORベースSwap(ref long, ref long)(デフォルトではインライン化ない)と比較する:明白なお持ち帰りはXORベースのアプローチを超えているようだということです

68:    a ^= b; 
003104B6 8B 06    mov   eax,dword ptr [esi] 
003104B8 8B 56 04    mov   edx,dword ptr [esi+4] 
003104BB 33 07    xor   eax,dword ptr [edi] 
003104BD 33 57 04    xor   edx,dword ptr [edi+4] 
003104C0 89 06    mov   dword ptr [esi],eax 
003104C2 89 56 04    mov   dword ptr [esi+4],edx 
    69:    b ^= a; 
003104C5 8B 07    mov   eax,dword ptr [edi] 
003104C7 8B 57 04    mov   edx,dword ptr [edi+4] 
003104CA 33 06    xor   eax,dword ptr [esi] 
003104CC 33 56 04    xor   edx,dword ptr [esi+4] 
003104CF 89 07    mov   dword ptr [edi],eax 
003104D1 89 57 04    mov   dword ptr [edi+4],edx 
    70:    a ^= b; 
003104D4 8B 06    mov   eax,dword ptr [esi] 
003104D6 8B 56 04    mov   edx,dword ptr [esi+4] 
003104D9 33 07    xor   eax,dword ptr [edi] 
003104DB 33 57 04    xor   edx,dword ptr [edi+4] 
003104DE 89 06    mov   dword ptr [esi],eax 
003104E0 89 56 04    mov   dword ptr [esi+4],edx 

JITコンパイラがデフォルトでメソッドをインライン化する制限。

[MethodImpl(MethodImplOptions.AggressiveInlining)] 

を今すぐ組み立て自体に:あなたはそれを正しく飾る場合は幸いなことに、それが実際にXORベースの方法をインラインます

  • よりも大きくないタイプの場合dword(32- XORベースのアプローチは合計で3 movと3 xor命令を出すのに対して、ナイーブなアプローチは常に4 mov(ファミリ)命令を発行します。いずれの場合も、引数はレジスタに格納されます。私はいつも測定していませんが、私が推測しなければならないのは、レジスタ間のmovが3 xorの動作よりも遅くならないと思います。

  • dwordよりも大きくなると、操作は個別の操作dwordに分割されます。 long(64ビット符号付き整数)の場合、純粋なアプローチは8 movの演算を発行し、XORベースの手法は12 movの演算と6 xorの演算を発行します。これは特に大きな構造体の場合、素朴なアプローチがよりコンパクトであることを示しているようです。


実験のために、のはdecimalほどの大きさだと、実際にoperator^を宣言します構造体を作成してみましょう:

struct Big 
{ 
    public long U; 
    public long V; 

    public static Big operator ^(Big op1, Big op2) 
    { 
     Big b; 
     b.U = op1.U^op2.U; 
     b.V = op1.V^op2.V; 
     return b; 
    } 
} 

このoperator^は、デフォルトでインライン化されるのに十分わずかに小さいですだから私は分解の呼び出しを見ないと思う。

このタイプのXORベースのバージョンSwapは他のタイプと同じに見えるので、私はそれを繰り返さない。ここで

はナイーブSwap<Big>(インライン)です:

52:    Swap<Big>(ref a, ref b); 
001D0484 8B 4D EC    mov   ecx,dword ptr [ebp-14h] 
001D0487 8B 75 F0    mov   esi,dword ptr [ebp-10h] 
001D048A 8B 45 E8    mov   eax,dword ptr [ebp-18h] 
001D048D 89 45 D0    mov   dword ptr [ebp-30h],eax 
001D0490 8B FB    mov   edi,ebx 
001D0492 8B 45 DC    mov   eax,dword ptr [ebp-24h] 
001D0495 8B 55 E0    mov   edx,dword ptr [ebp-20h] 
001D0498 89 45 EC    mov   dword ptr [ebp-14h],eax 
001D049B 89 55 F0    mov   dword ptr [ebp-10h],edx 
001D049E 8B 45 D4    mov   eax,dword ptr [ebp-2Ch] 
001D04A1 8B 55 D8    mov   edx,dword ptr [ebp-28h] 
001D04A4 89 55 E8    mov   dword ptr [ebp-18h],edx 
001D04A7 8B D8    mov   ebx,eax 
001D04A9 89 4D DC    mov   dword ptr [ebp-24h],ecx 
001D04AC 89 75 E0    mov   dword ptr [ebp-20h],esi 
001D04AF 8B 45 D0    mov   eax,dword ptr [ebp-30h] 
001D04B2 89 7D D4    mov   dword ptr [ebp-2Ch],edi 
001D04B5 89 45 D8    mov   dword ptr [ebp-28h],eax 

は(インライン化ない)XORベースSwap(ref Big, ref Big)と比較する:

94:    a ^= b; 
0027051C 8B 01    mov   eax,dword ptr [ecx] 
0027051E 8B 51 04    mov   edx,dword ptr [ecx+4] 
00270521 89 45 E4    mov   dword ptr [ebp-1Ch],eax 
    94:    a ^= b; 
00270524 89 55 E8    mov   dword ptr [ebp-18h],edx 
00270527 8B 41 08    mov   eax,dword ptr [ecx+8] 
0027052A 8B 51 0C    mov   edx,dword ptr [ecx+0Ch] 
0027052D 89 45 DC    mov   dword ptr [ebp-24h],eax 
00270530 89 55 E0    mov   dword ptr [ebp-20h],edx 
00270533 8B 06    mov   eax,dword ptr [esi] 
00270535 8B 56 04    mov   edx,dword ptr [esi+4] 
00270538 89 45 D4    mov   dword ptr [ebp-2Ch],eax 
0027053B 89 55 D8    mov   dword ptr [ebp-28h],edx 
0027053E 8B 46 08    mov   eax,dword ptr [esi+8] 
00270541 8B 56 0C    mov   edx,dword ptr [esi+0Ch] 
00270544 89 45 CC    mov   dword ptr [ebp-34h],eax 
00270547 89 55 D0    mov   dword ptr [ebp-30h],edx 
0027054A 8B 45 E4    mov   eax,dword ptr [ebp-1Ch] 
0027054D 8B 55 E8    mov   edx,dword ptr [ebp-18h] 
00270550 33 45 D4    xor   eax,dword ptr [ebp-2Ch] 
00270553 33 55 D8    xor   edx,dword ptr [ebp-28h] 
00270556 89 45 F4    mov   dword ptr [ebp-0Ch],eax 
00270559 89 55 F8    mov   dword ptr [ebp-8],edx 
0027055C 8B 45 DC    mov   eax,dword ptr [ebp-24h] 
0027055F 8B 55 E0    mov   edx,dword ptr [ebp-20h] 
00270562 33 45 CC    xor   eax,dword ptr [ebp-34h] 
00270565 33 55 D0    xor   edx,dword ptr [ebp-30h] 
00270568 89 45 EC    mov   dword ptr [ebp-14h],eax 
0027056B 89 55 F0    mov   dword ptr [ebp-10h],edx 
0027056E 8B 45 F4    mov   eax,dword ptr [ebp-0Ch] 
00270571 8B 55 F8    mov   edx,dword ptr [ebp-8] 
00270574 89 01    mov   dword ptr [ecx],eax 
00270576 89 51 04    mov   dword ptr [ecx+4],edx 
00270579 8B 45 EC    mov   eax,dword ptr [ebp-14h] 
0027057C 8B 55 F0    mov   edx,dword ptr [ebp-10h] 
0027057F 89 41 08    mov   dword ptr [ecx+8],eax 
00270582 89 51 0C    mov   dword ptr [ecx+0Ch],edx 
    95:    b ^= a; 
00270585 8B 06    mov   eax,dword ptr [esi] 
00270587 8B 56 04    mov   edx,dword ptr [esi+4] 
    95:    b ^= a; 
0027058A 89 45 B4    mov   dword ptr [ebp-4Ch],eax 
0027058D 89 55 B8    mov   dword ptr [ebp-48h],edx 
00270590 8B 46 08    mov   eax,dword ptr [esi+8] 
00270593 8B 56 0C    mov   edx,dword ptr [esi+0Ch] 
00270596 89 45 AC    mov   dword ptr [ebp-54h],eax 
00270599 89 55 B0    mov   dword ptr [ebp-50h],edx 
0027059C 8B 01    mov   eax,dword ptr [ecx] 
0027059E 8B 51 04    mov   edx,dword ptr [ecx+4] 
002705A1 89 45 A4    mov   dword ptr [ebp-5Ch],eax 
002705A4 89 55 A8    mov   dword ptr [ebp-58h],edx 
002705A7 8B 41 08    mov   eax,dword ptr [ecx+8] 
002705AA 8B 51 0C    mov   edx,dword ptr [ecx+0Ch] 
002705AD 89 45 9C    mov   dword ptr [ebp-64h],eax 
002705B0 89 55 A0    mov   dword ptr [ebp-60h],edx 
002705B3 8B 45 B4    mov   eax,dword ptr [ebp-4Ch] 
002705B6 8B 55 B8    mov   edx,dword ptr [ebp-48h] 
002705B9 33 45 A4    xor   eax,dword ptr [ebp-5Ch] 
002705BC 33 55 A8    xor   edx,dword ptr [ebp-58h] 
002705BF 89 45 C4    mov   dword ptr [ebp-3Ch],eax 
002705C2 89 55 C8    mov   dword ptr [ebp-38h],edx 
002705C5 8B 45 AC    mov   eax,dword ptr [ebp-54h] 
002705C8 8B 55 B0    mov   edx,dword ptr [ebp-50h] 
002705CB 33 45 9C    xor   eax,dword ptr [ebp-64h] 
002705CE 33 55 A0    xor   edx,dword ptr [ebp-60h] 
002705D1 89 45 BC    mov   dword ptr [ebp-44h],eax 
002705D4 89 55 C0    mov   dword ptr [ebp-40h],edx 
002705D7 8B 45 C4    mov   eax,dword ptr [ebp-3Ch] 
002705DA 8B 55 C8    mov   edx,dword ptr [ebp-38h] 
002705DD 89 06    mov   dword ptr [esi],eax 
002705DF 89 56 04    mov   dword ptr [esi+4],edx 
002705E2 8B 45 BC    mov   eax,dword ptr [ebp-44h] 
002705E5 8B 55 C0    mov   edx,dword ptr [ebp-40h] 
002705E8 89 46 08    mov   dword ptr [esi+8],eax 
002705EB 89 56 0C    mov   dword ptr [esi+0Ch],edx 
    96:    a ^= b; 
002705EE 8B 01    mov   eax,dword ptr [ecx] 
002705F0 8B 51 04    mov   edx,dword ptr [ecx+4] 
002705F3 89 45 84    mov   dword ptr [ebp-7Ch],eax 
002705F6 89 55 88    mov   dword ptr [ebp-78h],edx 
002705F9 8B 41 08    mov   eax,dword ptr [ecx+8] 
002705FC 8B 51 0C    mov   edx,dword ptr [ecx+0Ch] 
002705FF 89 85 7C FF FF FF mov   dword ptr [ebp-84h],eax 
00270605 89 55 80    mov   dword ptr [ebp-80h],edx 
00270608 8B 06    mov   eax,dword ptr [esi] 
0027060A 8B 56 04    mov   edx,dword ptr [esi+4] 
0027060D 89 85 74 FF FF FF mov   dword ptr [ebp-8Ch],eax 
00270613 89 95 78 FF FF FF mov   dword ptr [ebp-88h],edx 
00270619 8B 46 08    mov   eax,dword ptr [esi+8] 
0027061C 8B 56 0C    mov   edx,dword ptr [esi+0Ch] 
0027061F 89 85 6C FF FF FF mov   dword ptr [ebp-94h],eax 
00270625 89 95 70 FF FF FF mov   dword ptr [ebp-90h],edx 
0027062B 8B 45 84    mov   eax,dword ptr [ebp-7Ch] 
0027062E 8B 55 88    mov   edx,dword ptr [ebp-78h] 
00270631 33 85 74 FF FF FF xor   eax,dword ptr [ebp-8Ch] 
00270637 33 95 78 FF FF FF xor   edx,dword ptr [ebp-88h] 
0027063D 89 45 94    mov   dword ptr [ebp-6Ch],eax 
00270640 89 55 98    mov   dword ptr [ebp-68h],edx 
00270643 8B 85 7C FF FF FF mov   eax,dword ptr [ebp-84h] 
00270649 8B 55 80    mov   edx,dword ptr [ebp-80h] 
0027064C 33 85 6C FF FF FF xor   eax,dword ptr [ebp-94h] 
00270652 33 95 70 FF FF FF xor   edx,dword ptr [ebp-90h] 
00270658 89 45 8C    mov   dword ptr [ebp-74h],eax 
0027065B 89 55 90    mov   dword ptr [ebp-70h],edx 
0027065E 8B 45 94    mov   eax,dword ptr [ebp-6Ch] 
00270661 8B 55 98    mov   edx,dword ptr [ebp-68h] 
00270664 89 01    mov   dword ptr [ecx],eax 
00270666 89 51 04    mov   dword ptr [ecx+4],edx 
00270669 8B 45 8C    mov   eax,dword ptr [ebp-74h] 
0027066C 8B 55 90    mov   edx,dword ptr [ebp-70h] 
0027066F 89 41 08    mov   dword ptr [ecx+8],eax 
00270672 89 51 0C    mov   dword ptr [ecx+0Ch],edx 

構造体が大きくなるにつれ、それはあなたのものより明確になります素朴なアプローチでより良くなっています。

ここではもっと興味深いことがあります。 Swap<T>のインライン化を防ぐためにしようと考えてみましょう:

[MethodImpl(MethodImplOptions.NoInlining)] 
    static void Swap<T>(ref T a, ref T b) 
    { /* ... */ } 

ここでナイーブSwap<Big>は(抑制インライン展開)です:

93:    Big temp = a; 
004C0502 EC     in   al,dx 
004C0503 57     push  edi 
004C0504 56     push  esi 
004C0505 53     push  ebx 
004C0506 83 EC 10    sub   esp,10h 
004C0509 8B DA    mov   ebx,edx 
004C050B 8B 01    mov   eax,dword ptr [ecx] 
004C050D 8B 51 04    mov   edx,dword ptr [ecx+4] 
004C0510 89 45 EC    mov   dword ptr [ebp-14h],eax 
004C0513 89 55 F0    mov   dword ptr [ebp-10h],edx 
004C0516 8B 41 08    mov   eax,dword ptr [ecx+8] 
004C0519 8B 51 0C    mov   edx,dword ptr [ecx+0Ch] 
004C051C 89 45 E4    mov   dword ptr [ebp-1Ch],eax 
004C051F 89 55 E8    mov   dword ptr [ebp-18h],edx 
    94:    a = b; 
004C0522 8B F9    mov   edi,ecx 
004C0524 8B F3    mov   esi,ebx 
004C0526 F3 0F 7E 06   movq  xmm0,mmword ptr [esi] 
004C052A 66 0F D6 07   movq  mmword ptr [edi],xmm0 
004C052E F3 0F 7E 46 08  movq  xmm0,mmword ptr [esi+8] 
004C0533 66 0F D6 47 08  movq  mmword ptr [edi+8],xmm0 
    95:    b = temp; 
004C0538 8B 45 EC    mov   eax,dword ptr [ebp-14h] 
004C053B 8B 55 F0    mov   edx,dword ptr [ebp-10h] 
004C053E 89 03    mov   dword ptr [ebx],eax 
004C0540 89 53 04    mov   dword ptr [ebx+4],edx 
004C0543 8B 45 E4    mov   eax,dword ptr [ebp-1Ch] 
004C0546 8B 55 E8    mov   edx,dword ptr [ebp-18h] 
004C0549 89 43 08    mov   dword ptr [ebx+8],eax 
004C054C 89 53 0C    mov   dword ptr [ebx+0Ch],edx 

それは今SSE命令を発するように開始します! decimalと他の大きな構造体についても同じ動作が観察されます。

XORベースのアプローチは、私がテストしたいかなる状況下でもSSE命令を生成しません。


のは、クラスで試してみましょう:

public class W<T> 
{ 
    public T Value; 
} 

はここでナイーブSwap<W<T>>(インライン)です:

61:    Swap(ref a, ref b); 
0023047E 8B 55 FC    mov   edx,dword ptr [ebp-4] 
00230481 8B 45 F8    mov   eax,dword ptr [ebp-8] 
00230484 89 45 FC    mov   dword ptr [ebp-4],eax 
00230487 89 55 F8    mov   dword ptr [ebp-8],edx 

これは非常に簡単です - それは、上記のように、効果的にintスワップです。

参考文献は不透明であるため、XORベースのアプローチはこれらのタイプでは無意味です。したがって、これと比較する同等の逆アセンブリはありません。 floatdoubledecimal XOR演算を適用するための何のエレガントな方法がないようなタイプの場合


。この試みその後

[StructLayout(LayoutKind.Explicit)] 
struct XORFloat 
{ 
    [FieldOffset(0)] public int Bits; 
    [FieldOffset(0)] public float Value; 
} 

:すべてでそれを可能にするための一つの(おそらく愚かな)アプローチは、非ジェネリックなければならない労働組合を、作成することです

static void Swap(ref float a, ref float b) 
{ 
    var _a = default(XORFloat); 
    var _b = default(XORFloat); 
    _a.Value = a; 
    _b.Value = b; 
    _a.Bits ^= _b.Bits; 
    _b.Bits ^= _a.Bits; 
    _a.Bits ^= _b.Bits; 
    a = _a.Value; 
    b = _b.Value; 
} 

をしかし、これは敗北に思えますXORベースのアプローチの全体的なポイントは、明らかに多くの操作が含まれているので、mov操作です。ここで

はナイーブSwap<float>(インライン)です:

153:    var _a = default(XORFloat); 
0035049F 33 C0    xor   eax,eax 
003504A1 89 45 F8    mov   dword ptr [ebp-8],eax 
003504A4 89 45 F4    mov   dword ptr [ebp-0Ch],eax 
003504A7 8D 45 F8    lea   eax,[ebp-8] 
003504AA 33 F6    xor   esi,esi 
003504AC 89 30    mov   dword ptr [eax],esi 
    154:    var _b = default(XORFloat); 
003504AE 8D 45 F4    lea   eax,[ebp-0Ch] 
003504B1 89 30    mov   dword ptr [eax],esi 
    155:    _a.Value = a; 
003504B3 D9 01    fld   dword ptr [ecx] 
003504B5 D9 5D F8    fstp  dword ptr [ebp-8] 
    156:    _b.Value = b; 
003504B8 D9 02    fld   dword ptr [edx] 
003504BA D9 5D F4    fstp  dword ptr [ebp-0Ch] 
    157:    _a.Bits ^= _b.Bits; 
003504BD 8D 75 F8    lea   esi,[ebp-8] 
003504C0 8B 45 F4    mov   eax,dword ptr [ebp-0Ch] 
003504C3 31 06    xor   dword ptr [esi],eax 
    158:    _b.Bits ^= _a.Bits; 
003504C5 8D 75 F4    lea   esi,[ebp-0Ch] 
003504C8 8B 45 F8    mov   eax,dword ptr [ebp-8] 
003504CB 31 06    xor   dword ptr [esi],eax 
    159:    _a.Bits ^= _b.Bits; 
003504CD 8D 75 F8    lea   esi,[ebp-8] 
003504D0 8B 45 F4    mov   eax,dword ptr [ebp-0Ch] 
003504D3 31 06    xor   dword ptr [esi],eax 
    160:    a = _a.Value; 
003504D5 D9 45 F8    fld   dword ptr [ebp-8] 
003504D8 D9 19    fstp  dword ptr [ecx] 
    161:    b = _b.Value; 
003504DA D9 45 F4    fld   dword ptr [ebp-0Ch] 
003504DD D9 1A    fstp  dword ptr [edx] 

19:    Swap<float>(ref a, ref b); 
00252DC4 D9 45 FC    fld   dword ptr [ebp-4] 
00252DC7 D9 45 F8    fld   dword ptr [ebp-8] 
00252DCA D9 5D FC    fstp  dword ptr [ebp-4] 
00252DCD D9 5D F8    fstp  dword ptr [ebp-8] 

はXORベース(および組合ベース)Swap(ref float, ref float)(デフォルトではインライン化ない)との比較します

結論として、私は明らかにナイーブなSwapを使用して、コンパイラにあなたが何を意味し、その仕事をしているかを考えさせます。もっと面白いことがあります。

+0

補遺(文字制限による):C#でポインタを使用することはできますが、特定の種類の構造体にしか使用できません。残念ながら、これらの構造体を記述する一般的な制約はないので、 'T'ポインタを持つことはできません。 –

関連する問題