似乎在两个&
之间执行long
操作时,它需要与4个32位int
内的等效操作相同的时间。
例如
long1 & long2
只要
int1 & int2
int3 & int4
这是在64位操作系统上运行,目标是64位.net。
理论上,这应该快两倍。有没有人以前遇到过这个?
修改
作为简化,假设我有两个64位数据。我取64位并将它们放入long
,然后对这两位执行按位&
。
我还获取这两组数据,并将64位放入两个32位int
值并执行两个 &
s。我希望看到long
&
操作比int
&
操作运行得更快。
答案 0 :(得分: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");
}
}
为了测试多头a1
和a2
等合并,给出:
long a = ~0, b = 0x5555555555555555, c = 0x1234567812345678;
在我的笔记本电脑上运行这两个程序(i7 Q720)作为VS(.NET 4.5)的外部版本构建我得到以下时间:
int: 2238,长: 1924
现在考虑到有大量的循环开销,并且long
版本正在使用两倍的数据(8mb对4mb),它仍然明显领先。所以我没有理由相信C#没有充分利用处理器的64位bitops。
但我们真的不应该把它放在第一位。如果有问题,只需检查jited代码(Debug - &gt; Windows - &gt; Disassembly)。确保编译器使用您期望它使用的指令,然后继续。
尝试在处理器上测量那些单独指令的性能(这可能是你的处理器模型特有的),除了汇编程序之外的任何东西都是一个非常糟糕的主意 - 并且从像C#这样的jit编译语言中,超越徒劳。但是无论如何都没有必要,因为如果你需要知道它就在Intel's optimisation handbook。{/ p>
为此,这里是x64上程序的a &=
版本的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
版本的操作数量只有一半:
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 :(得分:5)
我无法重现你的时间。以下代码生成两个数组:一个1,000,000个long,一个具有2,000,000个int。然后它循环遍历数组,将&
运算符应用于连续值。它保持运行总和并输出它,只是为了确保编译器不会决定完全删除循环,因为它没有做任何事情。
经过几十次连续运行,long
循环的速度至少是int
循环的两倍。这是在带有Windows 8开发人员预览版和Visual Studio 11开发人员预览版的Core 2 Quad上运行的。程序使用“Any 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);