2011-12-20 94 views
2

在兩個long之間執行&操作似乎需要與4 32位int s中的等效操作相同的時間量。64位上的按位與長整數與整數的性能

例如

long1 & long2 

注意到只要

int1 & int2 
int3 & int4 

這是在64位操作系統上運行和針對64位.NET。

理論上,這應該快兩倍。有沒有人以前遇到過這個?

EDIT

作爲簡化,假設我有兩個批次的64個比特的數據。我拿這64位並將它們放入一個long,並在這兩者上執行一個按位&

我還採取這些兩組數據,並把64位分成兩個32位值int並執行 &秒。我期望看到long&操作運行速度比操作更快。

+1

「理論上,這應該快兩倍」 - 你基於什麼?兩者都將在相同的64位寄存器中完成,不是嗎? – 2011-12-20 05:15:14

+2

我很**懷疑你如何衡量這一點。我也認爲這是一個相當疏忽,當提問有關你的計算機的64位性能的問題時不提及你的處理器... – Mania 2011-12-20 05:20:52

+0

@Mitch - 好吧,如果我把一個long的內容分成兩個int32s,那麼它只需要一個&與2. – Khanzor 2011-12-20 05:27:11

回答

6

我無法重現該問題。

我的測試結果如下(如圖INT版):

// deliberately made hard to optimise without whole program optimisation 
public static int[] data = new int[1000000]; // long[] when testing long 

// I happened to have a winforms app open, feel free to make this a console app.. 
private void button1_Click(object sender, EventArgs e) 
{ 
    long best = long.MaxValue; 
    for (int j = 0; j < 1000; j++) 
    { 
     Stopwatch timer = Stopwatch.StartNew(); 
     int a1 = ~0, b1 = 0x55555555, c1 = 0x12345678; // varies: see below 
     int a2 = ~0, b2 = 0x55555555, c2 = 0x12345678; 
     int[] d = data; // long[] when testing long 
     for (int i = 0; i < d.Length; i++) 
     { 
      int v = d[i]; // long when testing long, see below 
      a1 &= v; a2 &= v; 
      b1 &= v; b2 &= v; 
      c1 &= v; c2 &= v; 
     } 
     // don't average times: we want the result with minimal context switching 
     best = Math.Min(best, timer.ElapsedTicks); 
     button1.Text = best.ToString() + ":" + (a1 + a2 + b1 + b2 + c1 + c2).ToString("X8"); 
    } 
} 

爲了測試多頭a1a2等合併,贈送:

long a = ~0, b = 0x5555555555555555, c = 0x1234567812345678; 

運行在我的筆記本電腦兩個方案(I7 Q720)作爲發佈構建VS(NET 4.5)的外部我得到以下時間:

INT:長: 1924年

現在考慮有一個巨大的循環開銷的量,並且該long版本正在與兩倍的數據(8MB VS 4MB),它仍然出來明顯領先。所以我沒有理由相信C#沒有充分利用處理器的64位bithop。

但是,我們真的不應該在一開始就改變它。如果有問題,只需檢查jited代碼(Debug - > Windows - > Disassembly)。確保編譯器使用您期望使用的指令,然後繼續。

嘗試測量處理器上這些單獨指令的性能(這可能是特定於處理器模型的),而彙編器以外的任何其他指令都是非常糟糕的想法 - 而且從C#等JIT編譯語言中,超越徒勞的。但無論如何,因爲它全部在Intel's optimisation handbook,所以你需要知道。

爲此,這裏的a &=用於在x64的long版本的程序的拆裝(釋放,但調試器內部的 - 不能確定這是否會影響組件,但它肯定會影響性能):

00000111 mov   rcx,qword ptr [rsp+60h] ; a &= v 
00000116 mov   rax,qword ptr [rsp+38h] 
0000011b and   rax,rcx 
0000011e mov   qword ptr [rsp+38h],rax 

正如您所看到的那樣,只有一個64位和預期的操作,以及三個64位移動。到目前爲止好,和的int版本OPS的正好一半數量:

00000122 mov   ecx,dword ptr [rsp+5Ch] ; a1 &= v 
00000126 mov   eax,dword ptr [rsp+38h] 
0000012a and   eax,ecx 
0000012c mov   dword ptr [rsp+38h],eax 
00000130 mov   ecx,dword ptr [rsp+5Ch] ; a2 &= v 
00000134 mov   eax,dword ptr [rsp+44h] 
00000138 and   eax,ecx 
0000013a mov   dword ptr [rsp+44h],eax 

我只能說,你看到的問題是具體到一些有關你的測試套件,編譯選項,處理器..或者很有可能,&不是你認爲的爭論點。 HTH。

+1

我無法重現我的時間。我只是用一個更簡單的版本重寫了它,並且無法重現。我看了一下這個反彙編,它確實只是表演一個單獨的和。 – Khanzor 2011-12-20 21:50:42

5

我無法重現您的時間。以下代碼將生成兩個數組:1,000,000個長整數中的一個,另一個包含2,000,000個整數。然後它遍歷數組,將&運算符應用於連續的值。它保持運行總和並輸出它,只是爲了確保編譯器不會完全刪除循環,因爲它沒有做任何事情。

超過數十個連續運行,long循環至少是int循環的兩倍。這是在Windows 8 Developer Preview和Visual Studio 11 Developer Preview的Core 2 Quad上運行的。程序使用「任何CPU」進行編譯,並以64位模式運行。所有測試都使用Ctrl + F5完成,以避免調試器參與。

 int numLongs = 1000000; 
     int numInts = 2*numLongs; 
     var longs = new long[numLongs]; 
     var ints = new int[numInts]; 
     Random rnd = new Random(); 
     // generate values 
     for (int i = 0; i < numLongs; ++i) 
     { 
      int i1 = rnd.Next(); 
      int i2 = rnd.Next(); 
      ints[2 * i] = i1; 
      ints[2 * i + 1] = i2; 
      long l = i1; 
      l = (l << 32) | (uint)i2; 
      longs[i] = l; 
     } 

     // time operations. 
     int isum = 0; 
     Stopwatch sw = Stopwatch.StartNew(); 
     for (int i = 0; i < numInts; i += 2) 
     { 
      isum += ints[i] & ints[i + 1]; 
     } 
     sw.Stop(); 
     Console.WriteLine("Ints: {0} ms. isum = {1}", sw.ElapsedMilliseconds, isum); 

     long lsum = 0; 
     int halfLongs = numLongs/2; 
     sw.Restart(); 
     for (int i = 0; i < halfLongs; i += 2) 
     { 
      lsum += longs[i] & longs[i + 1]; 
     } 
     sw.Stop(); 
     Console.WriteLine("Longs: {0} ms. lsum = {1}", sw.ElapsedMilliseconds, lsum);