2013-06-22 34 views
3

有誰知道是否存在一個用於交換uint中字節順序的.NET Framework等價物?.NET等價的_byteswap_ulong/OSSwapInt32 /或bswap32

我試圖移植一些使用MSFTs _byteswap_ulong(相當於* Nix世界中的Apples OSSwapInt32或Bswap32)的c#散列代碼。我可以手動編寫這個函數,但我懷疑它會利用任何編譯器優化(例如,c/C++編譯器提供難以超越的內建值,我希望運行時對內置函數的作用相同)。如果重要,我不關心保留字節順序。

我試過一個基於通用的解決方案,但我不相信這將是最優的。

BitConverter.ToUInt32(BitConverter.GetBytes(g).Reverse().ToArray<byte>(),0); 

編輯:

所以我想通了這個特殊的函數被調用多少次在10分鐘內的平均(對於我們的哈希消費國之一)。這個函數被調用10000000000次。因此,我設置了一些微分析來查看c代碼的性能與下面提供的解決方案(以及上面提出的解決方案)的性能。

該C代碼運行了許多操作(使用內在)在約。我可靠的筆記本電腦上有1500毫秒。 我上面介紹的C#代碼幾乎運行在2689581毫秒。巨大的差異。 Matthew Watson提供的c#代碼運行時間接近36000毫秒。 Caramiriel提供的第一個解決方案運行時間接近115014毫秒,提供的第二個解決方案運行速度接近36000.

雖然這些解決方案都沒有接近觸及內部呼叫的速度,但它們比我的原始解決方案好得多(對於許多計算,從44分鐘到36秒)。這對我的應用程序來說完全可以接受雖然如果.NET編譯器提供了一些與本地編譯器相同的內置功能,那將會很不錯。

爲了完整這裏是爲microbenchmarking我的C代碼:

#include "stdafx.h" 
#include "windows.h" 

unsigned long Swap(unsigned int value) 
{ 
    return _byteswap_uint64(value); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    unsigned int value = 0x01020304; 
    unsigned long NUMITER = 10000000000; 
    unsigned long a=0; 
    unsigned long z=0; 
    int throwAwayLoopCount = 5; 

    for (int k = 0; k < throwAwayLoopCount; ++k) 
    { 
     a = GetTickCount(); 
     for (unsigned long i = 0; i < NUMITER; ++i) 
     { 
      value = Swap(value); 
     } 
     z = GetTickCount(); 
     printf("Baseline, Cached: time is %4lld milliseconds: value%4lld\n", z-a,value); 
    } 

    printf("Baseline, Cached: time is %4lld milliseconds\n", z-a); 

    return 0; 
} 

這裏是基準提供的解決方案的C#代碼:

namespace ByteSwapProfiler 
{ 
    using System.Runtime.InteropServices; 
    using System.Diagnostics; 

    [StructLayout(LayoutKind.Explicit)] 
    internal struct UInt32Union 
    { 
     [FieldOffset(0)] 
     public UInt32 Value; 
     [FieldOffset(0)] 
     public byte Byte1; 
     [FieldOffset(1)] 
     public byte Byte2; 
     [FieldOffset(2)] 
     public byte Byte3; 
     [FieldOffset(3)] 
     public byte Byte4; 
    } 


    class Program 
    { 

     static uint ByteSwapNaive(uint g) 
     { 
      return BitConverter.ToUInt32(BitConverter.GetBytes(g).Reverse().ToArray<byte>(), 0); 
     } 

     static uint ByteSwapCaramiriel1(uint value) 
     { 
      unchecked 
      { 
       return ((value & 0xff000000) >> 24) | 
         ((value & 0x00ff0000) >> 8) | 
         ((value & 0x0000ff00) << 8) | 
         ((value & 0x000000ff) << 24); 
      } 
     } 

     static uint ByteSwapCaramiriel2(UInt32Union src) 
     { 
      UInt32Union dest = new UInt32Union 
       { 
        Byte1 = src.Byte4, 
        Byte2 = src.Byte3, 
        Byte3 = src.Byte2, 
        Byte4 = src.Byte1 
       }; 

      return dest.Value; 
     } 

     static uint ByteSwapMatthewWatson(uint word) 
     { 
      return ((word >> 24) & 0x000000FF) | ((word >> 8) & 0x0000FF00) | ((word << 8) & 0x00FF0000) | ((word << 24) & 0xFF000000);    
     } 

     static void Main(string[] args) 
     { 
      uint value= 0x01020304; 
      UInt32Union src = new UInt32Union(); 
      src.Value = value; 

      ulong NUMITER = 10000000000; 
      uint throwAwayLoopCount = 5; 
      var sw = new Stopwatch(); 
      string name = "Naive"; 
      //for (int k = 0; k < throwAwayLoopCount; ++k) 
      { 
       sw = Stopwatch.StartNew(); 
       for (ulong i = 0; i < NUMITER; ++i) 
       { 
        value = ByteSwapNaive(value); 
       } 
       sw.Stop(); 
       Console.Write("{0,-13}, Cached: time is {1,7} milliseconds. Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"),value); 
      } 

      Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0")); 


      name = "MatthewWatson"; 
      for (int k = 0; k < throwAwayLoopCount; ++k) 
      { 
       sw = Stopwatch.StartNew(); 
       for (ulong i = 0; i < NUMITER; ++i) 
       { 
        value = ByteSwapMatthewWatson(value); 
       } 
       sw.Stop(); 
       Console.Write("{0,-13}, Cached: time is {1,7} milliseconds. Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value); 
      } 
      Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0")); 

      name = "Caramiriel2"; 
      for (int k = 0; k < throwAwayLoopCount; ++k) 
      { 
       sw = Stopwatch.StartNew(); 
       for (ulong i = 0; i < NUMITER; ++i) 
       { 
        value = ByteSwapCaramiriel2(src); 
       } 
       sw.Stop(); 
       Console.Write("{0,-13}, Cached: time is {1,7} milliseconds. Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value); 
      } 

      Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0")); 

      name = "Caramiriel1"; 
      for (int k = 0; k < throwAwayLoopCount; ++k) 
      { 
       sw = Stopwatch.StartNew(); 
       for (ulong i = 0; i < NUMITER; ++i) 
       { 
        value = ByteSwapCaramiriel1(value); 
       } 
       sw.Stop(); 
       Console.Write("{0,-13}, Cached: time is {1,7} milliseconds. Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value); 
      } 

      Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0")); 
     } 
    } 
} 
+0

只是出於好奇,那些是什麼內在? – Jon

+1

@Jon _byteswap_uint64是msfts的內在http://msdn.microsoft.com/en-us/library/5704bbxw(v=vs.80).aspx。你可以到這裏http://msdn.microsoft.com/en-us/library/26td21ds(v=vs.80).aspx –

+0

他們的名單你問這個問題,因爲**你不知道**無論C#選擇是否足夠快。我們也沒有,我們不能分析你的代碼。你必須自己做。 –

回答

6

我不認爲你會得到什麼與_byteswap_ulong一樣快,但如果你使用這個:

public static uint SwapBytes(uint word) 
{ 
    return ((word>>24)&0x000000FF) | ((word>>8)&0x0000FF00) | ((word<<8)&0x00FF0000) | ((word<<24)&0xFF000000);    
} 

至少JIT優化器會我可能內聯它。

+0

這是一種犯罪行爲,它並未作爲運行時的基元實現 –

3

一種方法是直接在(U)Int32類型上工作。這爲您提供了最小的開銷,例如Linq中使用的狀態機和涉及的方法調用。

unchecked {  
    return 
      ((value & 0xff000000) >> 24) | 
      ((value & 0x00ff0000) >> 8) | 
      ((value & 0x0000ff00) << 8) | 
      ((value & 0x000000ff) << 24); 
} 

此隔開需要的每個字節在其相應位置的整數和移動(shifting)內到右側位置。最後將移動的字節拼接在一起(OR -ed)。 Unchecked只是抑制可能導致上/下溢的異常,這些異常現在不相關(因爲沒有涉及檢查,因此可以節省性能)。

或C-結合的方式,但更易讀,慢兩倍於我的VM:

[StructLayout(LayoutKind.Explicit)] 
internal struct UInt32Union 
{ 
    [FieldOffset(0)] public UInt32 Value; 
    [FieldOffset(0)] public byte Byte1; 
    [FieldOffset(1)] public byte Byte2; 
    [FieldOffset(2)] public byte Byte3; 
    [FieldOffset(3)] public byte Byte4; 
} 

static UInt32 Swap(UInt32 value) 
{ 
    UInt32Union src = new UInt32Union 
    src.Value = value; 

    UInt32Union dest = new UInt32Union 
     { 
      Byte1 = src.Byte4, 
      Byte2 = src.Byte3, 
      Byte3 = src.Byte2, 
      Byte4 = src.Byte1 
     }; 

    return dest.Value; 
}