我正在编写一段代码,需要处理不一定在 0 到 1 范围内的 uvs(2D 纹理坐标)。例如,有时我会得到 u 分量为 1.2 的 uv。为了解决这个问题,我正在实现一个包装,通过执行以下操作来导致平铺:
u -= floor(u)
v -= floor(v)
这样做会使 1.2 变成 0.2,这是所需的结果。它还可以处理负面情况,例如 -0.4 变为 0.6。
然而,这些对发言权的调用相当慢。我已经使用 Intel VTune 分析了我的应用程序,并且我花费了大量的周期来执行此底层操作。
在对这个问题进行了一些背景阅读之后,我提出了以下函数,该函数速度更快一些,但仍然有很多不足之处(我仍然会受到类型转换惩罚等)。
int inline fasterfloor( const float x ) { return x > 0 ? (int) x : (int) x - 1; }
我已经看到了一些通过内联汇编完成的技巧,但似乎没有任何东西可以完全正确地工作或有任何显着的速度改进。
有谁知道处理这种情况的任何技巧?
老问题,但我遇到了它,这让我有点抽搐,因为它还没有得到令人满意的答案。
TL;DR:*不要**为此使用内联汇编、内在函数或任何其他给定的解决方案!相反,使用快速/不安全的数学优化进行编译(g++ 中的“-ffast-math -funsafe-math-optimizations -fno-math-errno”)。 Floor() 如此慢的原因是,如果转换溢出(FLT_MAX 不适合任何大小的标量整数类型),它会更改全局状态,这也使得向量化变得不可能,除非禁用严格的 IEEE-754 兼容性,无论如何你可能不应该依赖它。使用这些标志进行编译会禁用问题行为。
一些备注:
带有标量寄存器的内联汇编不可矢量化,这会极大地抑制优化编译时的性能。它还要求当前存储在向量寄存器中的任何相关值溢出到堆栈并重新加载到标量寄存器中,这违背了手动优化的目的。
在我的机器上,使用 SSE cvttss2si 和您概述的方法进行内联汇编实际上比带有编译器优化的简单 for 循环慢。
这可能是因为如果您允许编译器将整个代码块矢量化在一起,那么您的编译器将分配寄存器并更好地避免管道停顿。对于像这样的一小段代码,内部依赖链很少,几乎没有寄存器溢出的可能性,它几乎没有机会比由 asm() 包围的手工优化代码做得更糟。
内联汇编不可移植,在 Visual Studio 64 位版本中不受支持,并且极其难以阅读。内在函数也有与上面列出的相同的警告。
所有其他列出的方法都是不正确的,这可以说比缓慢更糟糕,并且它们在每种情况下都给出了这样的边际性能改进,以至于它不能证明该方法的粗糙性是合理的。 (int)(x+16.0)-16.0 太糟糕了,我什至都不会碰它,但你的方法也是错误的,因为它给了 Floor(-1) 作为-2。当数学代码对性能至关重要以至于标准库无法为您完成这项工作时,在数学代码中包含分支也是一个非常糟糕的主意。所以你的(不正确的)方式应该看起来更像 ((int) x) - (x
我尝试使用按位转换和通过位掩码舍入,就像 SUN 的 newlib 实现在 fmodf 中所做的那样,但它需要很长时间才能正确,并且在我的机器上慢了好几倍,即使没有相关的编译器优化标志。很可能,他们为某些古老的 CPU 编写了该代码,其中浮点运算相对非常昂贵,并且没有矢量扩展,更不用说矢量转换操作了; AFAIK 在任何常见架构上都不再出现这种情况。 SUN 也是 Quake 3 使用的快速逆 sqrt() 例程的诞生地;现在大多数架构上都有相关的说明。微优化的最大陷阱之一是它们很快就会过时。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)