2011-12-20 90 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);