我正在实现相干噪声函数,并惊讶地发现使用梯度噪声(即 Perlin 噪声)实际上比值噪声稍快。分析显示,其原因是将随机 int 值转换为 -1.0 到 1.0 范围内的双精度值所需的除法:
static double noiseValueDouble(int seed, int x, int y, int z) {
return 1.0 - ((double)noiseValueInt(seed, x, y, z) / 1073741824.0);
}
梯度噪声需要更多的乘法,但由于预先计算的梯度表使用
noiseValueInt
直接计算表中的索引,并且不需要任何除法。所以我的问题是,考虑到除法是按 2 的幂 (2^30) 进行除法,如何才能使上述除法更有效。
理论上,需要做的就是从双精度数的指数中减去 30,但是通过强力(即位操作)来做到这一点会导致各种极端情况(INF、NAN、指数溢出等)。 x86 汇编解决方案就可以了。
static const double div_2_pow_30 = 1.0 / 1073741824.0;
另一种方法(利用数字是2的幂的性质)是通过位运算修改指数。这样做将使代码依赖于使用 IEEE 标准存储的双精度数,这可能不太可移植。
循环运行
noiseValueDouble
和相应的替代方案以获得更好的数字。一个 x86 汇编器解决方案
是一个位摆弄解决方案,您也可以在 C 中进行位摆弄。快速二的幂除法指令(位移位)仅适用于整数。
如果你真的想使用特殊指令,MMX 或其他什么。
double divide(int i) {
return 1.0 - (double)i / 1073741824.0;
}
使用
-O3
,它被编码为
FMULS
指令,使用
-O3 -mfpmath=sse -march=core2
,它使用 SSE 指令集并将其编码为
MULSD
。我不知道什么是最快的,但函数调用本身可能比实际除法慢几个数量级。