Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/65.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ “快速到达”的方法;改善;单位长度向量的长度_C++_C_Optimization_Precision_Intrinsics - Fatal编程技术网

C++ “快速到达”的方法;改善;单位长度向量的长度

C++ “快速到达”的方法;改善;单位长度向量的长度,c++,c,optimization,precision,intrinsics,C++,C,Optimization,Precision,Intrinsics,当已知向量已经接近单位长度时,为性能关键代码中的完全向量规范化付费似乎是浪费 有人知道一种快速、实用的方法可以使双精度3D矢量的长度接近1吗?我在想象一种基于牛顿-拉斐逊迭代或1附近的有限泰勒展开的迭代方法 这是一种实际情况,在这种情况下,这样的例行程序可能是有用的。传入的向量几乎已经是单位长度,但如果没有显式的规范化,它仍然会触发后续的断言 使用SSE 2、SSE 4.2或AVX intrinsics是可以的。手头的问题归结为查找(近似值) SSE和AVX包含一个近似的平方根倒数机器指令,rs

当已知向量已经接近单位长度时,为性能关键代码中的完全向量规范化付费似乎是浪费

有人知道一种快速、实用的方法可以使双精度3D矢量的长度接近1吗?我在想象一种基于牛顿-拉斐逊迭代或1附近的有限泰勒展开的迭代方法

这是一种实际情况,在这种情况下,这样的例行程序可能是有用的。
传入的
向量几乎已经是单位长度,但如果没有显式的规范化,它仍然会触发后续的断言


使用SSE 2、SSE 4.2或AVX intrinsics是可以的。

手头的问题归结为查找(近似值)

SSE和AVX包含一个近似的平方根倒数机器指令,
rsqrt
,特别适合于此。根据原始数据,倒数平方根变量的最大相对误差最大为1.5×2-12,或小于0.0004

如果使用GCC,可以使用SSE内置函数计算向量平方长度的倒数平方根,并将向量分量乘以结果,得到“几乎单位”向量

请注意,SSE和AVX都提供了加速平方长度计算以及将每个分量相乘的函数。(不过,您需要将比例因子复制到大小相同的向量。)


如果没有SSE/AVX,一般的问题是我们希望将向量分量乘以f(S)≃ sqrt(1/S)=1/sqrt(S),其中S是向量与其自身的内积(点积),即其长度的平方;但是sqrt()被认为太慢,并且已知S已经接近1

任何函数f(s),其值在1和平方rt(1/s)之间,在我们认为“接近1”的范围内,将起作用。我能想到的最简单的函数形式是f(S)=(C+1-S)/C。对于S=0.52到22(即长度在1/2和2之间的向量),C是6

如果我们没有任何对平方根倒数的硬件支持,我将尝试的第一种近似方法如下:

  • 计算向量的平方长度S

  • 计算M=0.125*(9-S)

    请注意,任何常数对C1和C2=1+1/C1都应该起作用,只有收敛范围和速度不同。在本例中,我选择C1=1/8是因为它在IEEE-754浮点表示法中是精确的,并且通常乘法比除法快得多。其他值(如上面提到的0.5到2范围内的1/6i)不精确,可能需要手动细化(以某种方式调整两个常数中的最低有效单位)

  • 将向量的每个分量乘以M


  • 如果这不能产生足够好的结果,我就不再担心它,而是使用(硬件)平方根。(在某些体系结构上,将平方长度转换为单精度以计算比例因子可以产生显著的加速。但是,在x86/AMD64上不是这样。)

    我不确定一点平方根是否如此昂贵。但无论如何,在x=1附近,x^2的图有导数2,也就是说,它就像一条直线,在x方向上每向前上升2个单位。所以你可以通过减去1,除以2,再加1来估计这个区域的x(从欧几里得距离的平方)。我还没有检查这将如何在性能方面发挥作用,只有您才能知道您的“已接近”是否足够接近。请尝试进行基准测试,然后询问详细信息。因此,这不是一个讨论论坛。请注意,@cheerrandhth.-Alf建议的是
    x
    附近平方根函数的一阶泰勒展开式,尽管显然是通过不同的途径实现的。您可以通过添加更多的术语来改进它(下一个术语是-((x-1)^2)/8,但我预计您很快就会通过
    sqrt()这一点
    函数的效率更高。另外,请注意,您可以使用泰勒级数误差界项来确定这些近似值是否足够接近您。@JohnBollinger:谢谢,我没有这样想。但现在您提到它,正如我所记得的,麦克劳林级数是通过考虑导数得出的,而泰勒级数是通过用偏移量计算麦克劳林级数?很久以前,我几乎什么都不记得了,除了在课堂上,当一位讲师与一名学生交换时,我感到多么困惑。我想知道他怎么能做到这一点,那有多棒。现在,我想他可能已经记住了这一点……)当然,我应该说
    sqrt(x)
    在1附近的扩展。“近x”没有任何意义。谢谢你的详细回答,非常感谢。我用一个简单的Newton-Raphson迭代得到了非常好的结果:M=(3-S)*0.5。这似乎比M=(9-S)*0.125收敛得快得多。经常让人认为sqrt速度慢的一件事是,他们没有将
    -fno math errno
    用于gcc(或其他编译器上的任何等价物),因此编译器生成对libc函数的代价高昂的调用,以测试退化情况,不要直接使用硬件指令。@marglisse不是因为
    sqrt
    太慢;只是它比,比如说,几次加法和一次乘法要慢。我在帖子中链接的软件在一次运行的过程中计算出数千亿平方根。每一点优化都有帮助。使用一次牛顿-拉斐逊迭代调整一个接近单位长度的3D双精度向量的长度似乎比普通标准化快约2.7倍。@Marglisse:完全同意。此外,GCC仍然不能很好地对操作进行矢量化,因此,如果您可以以两个、四个、八个或十六个为一组(取决于精度以及是否使用AVX256或更早版本)对向量进行规范化,那么您通常可以在大致相同的时间内进行规范化