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的主要内容,如果未能解决你的问题,请参考以下文章

用于灰度到 ARGB 转换的 C++ SSE2 或 AVX2 内在函数

VC++ 2K8 中 SSE 编码的内在函数与内联 ASM

SSE 内在函数优化

数组乘法与 sse 内在函数乘法的时序?

用 sse 执行内在函数

SSE 内在函数检查零标志