这里是一个真实世界的例子:固定点乘法。
这些不仅在没有浮点的设备上很方便,它们在精度方面表现得很好,因为它们提供32位精度,带有可预测的误差(浮点数只有23位,难以预测精度损失)
在32位架构上写固定点乘法的一种方法如下:
int inline FixedPointMul (int a, int b)
{
long long a_long = a; // cast to 64 bit.
long long product = a_long * b; // perform multiplication
return (int) (product >> 16); // shift by the fixed point bias
}
这个代码的问题是我们做一些不能用C语言直接表达的东西。我们要将两个32位数相乘,得到一个64位结果,我们返回中间的32位。然而,在C中这个乘法不存在。所有你可以做的是将整数提升到64位,并做一个64 * 64 = 64乘法。
然而,x86(ARM,MIPS和其他)可以在单个指令中进行乘法。许多编译器仍然忽略这个事实,并生成调用运行时库函数来执行乘法的代码。 16的移位也通常由库程序完成(x86也可以做这样的移位)。
所以我们留下一两个库调用只是一个乘法。这有严重的后果。不仅移动速度较慢,寄存器必须保留在函数调用之间,它也不帮助内联和代码展开。
如果你在汇编器中重写相同的代码,你可以获得显着的速度提升。
除此之外:使用ASM不是解决问题的最好方法。大多数编译器允许你使用内部形式的一些汇编指令,如果你不能在C中表达它们。VS.NET2008编译器例如公开32 * 32 = 64位mul为__emul和64位移位为__ll_rshift。
使用内在函数可以重写函数,C编译器有机会了解发生了什么。这允许代码被内联,寄存器分配,公共子表达式消除和常量传播。相比手写的汇编代码,你将获得巨大的性能提升。
供参考:VS.NET编译器的定点mul的最终结果是:
int inline FixedPointMul (int a, int b)
{
return (int) __ll_rshift(__emul(a,b),16);
}
Btw – 固定点除法的性能差别更糟。我通过编写几个asm行的改进达到10倍的分区重定点代码。
编辑:
使用Visual c 2013提供了两种方法的相同的汇编代码。