【发布时间】:2017-09-09 21:06:03
【问题描述】:
这是一个 64 位 masm 程序,将两个 64 位数字相乘,再将一个 64 位数字相加得到 128 位结果(使用标准的 64 位调用约定):
; public static extern ulong MulAdd64(ulong U, ulong V, ref ulong k);
; Return (U*V + k) % ß and set k = (U*V + k) / ß.
; U in rcx, V in rdx, &k in r8
; Note 0 <= 0*0 + 0 <= (ß-1)*(ß-1) + (ß-1) = ß*(ß-1) < ß^2
MulAdd64 proc public
mov rax,rcx
mul rdx
add rax,qword ptr [r8] ; low part of product
adc rdx,0
mov qword ptr [r8],rdx ; high part of product
ret
MulAdd64 endp
这是通过以下方式导入到 C# 代码中的:
[DllImport(@"C:\path\MulAdd64.dll")]
public static extern ulong MulAdd64 (ulong U, ulong V, ref ulong k);
下面是用 C# 编写的同一个函数以及一个测试程序:
public static void TestCS_Masm_Speed ()
{
ulong x = 3141592653589793238, y = 2718281828459045, aux = 1234567890123456789, lo = 0;
// just in case the first invocation is excessivly slow
//lo = MulAdd64(x, y, ref aux);
lo = CS_MulAdd64(x, y, ref aux);
Stopwatch sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < 10000000; i++) {
//lo = MulAdd64(x, y, ref aux);
lo = CS_MulAdd64(x, y, ref aux);
}
Console.WriteLine("Ticks = {0}", sw.ElapsedTicks);
// verify low-order results hi:lo = x*y + aux;
if (x*y+aux != lo) Console.WriteLine("Error in low order result");
else Console.WriteLine("Low order result is OK");
}
[DllImport(@"C:\path\MulAdd64.dll")]
public static extern ulong MulAdd64 (ulong U, ulong V, ref ulong k);
/*
Multiplication. We need to multiply two unsigned 64-bit integers, obtaining an
unsigned 128-bit product. Using Algorithm 4.3.1M of Seminumerical Algorithms,
with ß = 2^32, the following subroutine computes hi:lo = y*z + aux . Then
sets aux to hi and return lo.
*/
public static ulong CS_MulAdd64 (ulong y, ulong z, ref ulong aux)
{
ulong[] u = new ulong[2], v = new ulong[2], w = new ulong[4];
// Unpack the multiplier, multiplicand, and aux to u, v, and w
u[1] = (ulong)y >> 32; u[0] = (ulong)y & 0xFFFFFFFF;
v[1] = (ulong)z >> 32; v[0] = (ulong)z & 0xFFFFFFFF;
w[1] = (ulong)aux >> 32; w[0] = (ulong)aux & 0xFFFFFFFF;
// Multiply
for (int j = 0; j < 2; j++) {
ulong k = 0;
for (int i = 0; i < 2; i++) {
ulong t = u[i] * v[j] + w[i+j] + k;
w[i+j] = t & 0xFFFFFFFF; k = t >> 32;
}
w[j+2] = k;
}
// Pack w into the outputs aux and return w
aux = ((ulong)w[3] << 32) + (ulong)w[2];
return ((ulong)w[1] << 32) + (ulong)w[0];
}
优化后的 C# 代码比 masm 代码长得多(153 条指令对 6 条指令),但运行速度几乎是两倍(941694 滴答对 1722289 滴答)!怎么会这样?一切都在寄存器中传递,没有内存可以固定!显然,在 C# 的调用和 masm 中的执行之间发生了一些事情,但是什么?我无法进入该代码。
【问题讨论】:
-
源代码的长度与执行时间并没有很好的相关性。这个基本假设就是这里的问题所在。
-
but runs faster。发布您如何衡量它的代码。 -
据我所知,跨越托管/本机边界时会产生开销......此外,也许编译器能够比您想象的更优化您的长代码,因此实际代码可能是比你写的更优化。
-
通常在循环中进行此类性能测试,您可能会通过对任一侧的循环进行计时来了解托管 本机转换。
-
我同意@Servy 的观点,即源代码与执行时间并没有很好的相关性。我的观点是,masm 中的 6 条指令乘加远没有解包、执行 4 次乘法、重新打包和执行各种开销所需的 C# 代码那么复杂。