c++ SSE内在函数atan2
Posted
技术标签:
【中文标题】c++ SSE内在函数atan2【英文标题】:c++ SSE intrinsics atan2 【发布时间】:2015-07-31 08:25:16 【问题描述】:我需要一个非常快的 atan2 来从 sobel 值中获取梯度(我正在实施精明的边缘算法。)。有谁知道一个非常快速的实现,最好是内在函数(SIMD)或非常快速的近似值。 (我认为一个近似值就足够了,因为这些值四舍五入到 0°、45°、90°、135°)
提前致谢
添加:我知道英特尔在 SVML 中的 IPP atan2 很遗憾我无法使用它。
【问题讨论】:
如果您只四舍五入到几个角度值,只需检查参数的符号并将它们的大小比率与已知阈值进行比较。 为什么你认为std::atan2()
很慢?你测量过它的CPU时间吗?你看过 C++ 编译器在 Release 模式下生成的反汇编吗? atan2
不是已经是一条 FPU 指令了吗?
慢是一种观点。第一个 std::atan2 对我的目的来说太准确了,它不使用 simd 指令。
【参考方案1】:
您似乎想四舍五入到一个八分圆数,大概是从-22.5°
到337.5°
,以45°
为增量。
八分圆由四行通过原点用方程隔开
Y = X tan(Θ),
或
a.Y - b.X = 0.
具有合适的比例因子。
通过计算四个所需角度的这些表达式的符号,您将找到八分圆。通过巧妙的组合,您可以限制为三个符号评估,因为有8=2³
的可能性。
这很可能可以通过 SIMD 指令通过计算判别表达式、它们的符号以及它们的符号的适当组合来评估,但这并非易事。
可能不需要转换为 45° 的倍数,甚至不需要顺序编号。这完全取决于您如何处理八分圆信息。
其他 SIMD 建议:
使用预加载的系数,您可以使用 16 位整数算术(可能使用乘加法)一次性计算 (X, Y) 对的所有四个线方程。然后获取标志并使用_mm_movemask_epi8
将它们打包成四位。使用四位值作为小型查找表的输入。
【讨论】:
@user1235183:查看附录。【参考方案2】:如前所述,hereatan2()
已经是一条 FPU 指令:x87 FPU 操作码 FPATAN
。只需查看调用std::atan2()
时编译器生成的反汇编。如果不是那条 FPU 指令,那么你可以试试 GCC 内联汇编中的this:
inline double my_atan2 (double y, double x)
double result;
asm (
"fpatan\n\t"
: "=t" (result) // outputs; t = top of fpu stack
: "0" (x), // inputs; 0 = same as result
"u" (y) // u = 2nd floating point register
);
return result;
【讨论】:
FPATAN的延迟可以是150个周期或更多。 大多数数学库实现不使用fpatan
,因为没有它你可以做得更好。此外,在 XMM 寄存器和 x87 堆栈之间获取数据的速度很慢,而且一次只能得到一个 atan2
结果。未来的读者,请参阅agner.org/optimize 以获取说明表。以上是关于c++ SSE内在函数atan2的主要内容,如果未能解决你的问题,请参考以下文章