什么是一起计算罪和cos的最快方法?

我想同时计算一个值的正弦和同正弦(例如创build一个旋转matrix)。 当然,我可以像a = cos(x); b = sin(x);那样分别计算它们a = cos(x); b = sin(x); a = cos(x); b = sin(x); ,但我不知道是否有一个更快的方式,当需要这两个值。

编辑:总结到目前为止的答案:

  • 弗拉德说,有一个命令FSINCOS的asm命令计算他们两个(几乎在同一时间呼叫FSIN单独)

  • Chi注意到的那样,这个优化有时候已经被编译器完成了(当使用优化标志的时候)。

  • sincosf指出,函数sincosfsincosf可能是可用的,可以直接通过包括math.h来调用

  • 讨论使用查找表的tanascius方法是有争议的。 (然而,在我的电脑和基准testing场景中,它比sincos运行速度快3倍,对于32位浮点运算精度几乎相同。)

  • 乔尔·古德温(Joel Goodwin)把一个有趣的方法与一个相当不错的快速逼近技术联系起来(对于我来说,这个速度甚至比查表更快)

现代英特尔/ AMD处理器具有用于同时计算正弦和余弦函数的FSINCOS指令。 如果你需要强大的优化,也许你应该使用它。

这是一个小例子: http : //home.broadpark.no/~alein/fsincos.html

这里是另一个例子(MSVC): http : //www.codeguru.com/forum/showthread.php? t=328669

这是又一个例子(用gcc): http : //www.allegro.cc/forums/thread/588470

希望其中一个帮助。 (我自己没有使用这个指令,对不起。)

由于它们在处理器级别上得到了支持,所以我期望它们比表查找要快得多。

编辑:
维基百科build议在387处理器上增加FSINCOS ,所以你几乎找不到一个不支持它的处理器。

编辑:
英特尔的文档指出, FSINCOSFDIV (即浮点除法)慢大约5倍。

编辑:
请注意,并不是所有的现代编译器都可以通过调用FSINCOS来优化正弦和余弦的计算。 特别是,我的VS 2008没有这样做。

编辑:
第一个例子链接已经死了,但在Wayback机器上仍然有一个版本 。

现代的x86处理器有一个fsincos指令,它可以完成你所要求的 – 同时计算sin和cos。 一个好的优化编译器应该检测计算相同值的sin和cos的代码,并使用fsincos命令来执行此操作。

编译器标志花了一些时间才能工作,但是:

 $ gcc --version i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5488) Copyright (C) 2005 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ cat main.c #include <math.h> struct Sin_cos {double sin; double cos;}; struct Sin_cos fsincos(double val) { struct Sin_cos r; r.sin = sin(val); r.cos = cos(val); return r; } $ gcc -c -S -O3 -ffast-math -mfpmath=387 main.c -o main.s $ cat main.s .text .align 4,0x90 .globl _fsincos _fsincos: pushl %ebp movl %esp, %ebp fldl 12(%ebp) fsincos movl 8(%ebp), %eax fstpl 8(%eax) fstpl (%eax) leave ret $4 .subsections_via_symbols 

多田,它使用fsincos指令!

当你需要表演的时候,你可以使用一个预先计算的罪恶/成本表(一个表格将被存储为一个词典)。 那么,这取决于你需要的准确性(也许桌子会很大),但它应该是非常快的。

从技术上讲,你可以通过使用复数和欧拉公式来实现 。 因此,像(C ++)

 complex<double> res = exp(complex<double>(0, x)); // or equivalent complex<double> res = polar<double>(1, x); double sin_x = res.imag(); double cos_x = res.real(); 

应该给你正弦和余弦一步。 内部如何完成是编译器和库被使用的问题。 它可能(也可能)需要更长的时间来这样做(只是因为欧拉公式主要用来计算使用sincos的复数exp ,而不是相反的方式),但是可能会有一些理论上的优化。


编辑

GNU C ++ 4.2的<complex>的头文件使用了在polar内的sincos显式计算,所以对于那里的优化来说它看起来不太好,除非编译器做了一些魔术(参见-ffast-math-mfpmath开关写在Chi的答案 )。

你可以计算出来然后使用这个标识:

  cos(x) 2 = 1  -  sin(x) 2 

但@tanascius说,预先计算的表格是要走的路。

如果你使用GNU C库,那么你可以这样做:

 #define _GNU_SOURCE #include <math.h> 

你将得到sincosf() sincos()sincosf()sincosl()函数的声明,这两个函数可以同时计算两个值 – 大概是以你的目标架构最快的方式。

许多Cmath库,正如caf所示,已经有了sincos()。 值得注意的例外是MSVC。

  • Sun自从1987年(至less二十三年;我有一个硬拷贝手册页)以来就有sincos()
  • HPUX 11在1997年(但不是在HPUX 10.20)
  • 版本2.1(1999年2月)添加到glibc
  • 成为gcc 3.4(2004)中的一个内置函数__builtin_sincos()。

关于查找,Eric S. Raymond在“ Unix程序devise艺术” (2004)(第12章)中明确表示这是一个糟糕的想法(在当前的时刻):

“另一个例子是预先计算小型表 – 例如,为了优化3Dgraphics引擎中的旋转,一个sin(x)表将在现代机器上花费365×4字节。在处理器获得足够快的内存来caching需求之前,这是一个明显的速度优化,现在每次重新计算可能会更快,而不是支付由表引起的额外caching未命中的百分比。

“但是在未来,随着caching规模的扩大,这种情况可能会再次出现,更为普遍的是,许多优化措施是暂时的,随着成本比率的变化,很容易变成悲观的现象,唯一的方法就是测量和观察。 (来自Unix编程艺术

但是,从上面的讨论来看,并不是每个人都同意的。

我不认为查找表对于这个问题是一个好主意。 除非您的准确度要求非常低,否则表格需要非常大。 而现代的CPU可以做大量的计算,而从主内存中获取一个值。 这不是那些可以通过论证(甚至是我的)不能正确回答的问题之一,testing和测量并考虑数据。

但是我期待在诸如AMD的ACML和Intel的MKL等库中find的SinCos的快速实现。

在这个论坛页面上有非常有趣的东西,其重点是find快速的近似值: http : //www.devmaster.net/forums/showthread.php? t= 5784

免责声明:我自己没有使用任何这些东西。

注意:链接的网站已经移到这里http://devmaster.net/posts/9648/fast-and-accurate-sine-cosine

本文展示了如何构build一个同时产生正弦和余弦的抛物线algorithm:

DSP技巧:Sin和Cos的同时抛物逼近

http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos

当性能对这种事情至关重要时,引入查找表并不罕见。

对于创造性的方法,如何扩展泰勒级数? 既然他们有类似的术语,你可以做一些类似下面的伪命令:

 numerator = x denominator = 1 sine = x cosine = 1 op = -1 fact = 1 while (not enough precision) { fact++ denominator *= fact numerator *= x cosine += op * numerator / denominator fact++ denominator *= fact numerator *= x sine += op * numerator / denominator op *= -1 } 

这意味着你做这样的事情:从x和1开始为罪和余弦,按照模式 – 减去x ^ 2/2! 从余弦减去x ^ 3/3! 从正弦,加上x ^ 4/4! 余弦,加上x ^ 5/5! 正弦

我不知道这是否是高性能的。 如果你所需要的精度低于内build的sin()和cos()给你的精度,那么它可能是一个选项。

如果您愿意使用商业产品,并同时计算多个正弦/余弦计算(因此您可以使用vector函数),则应该查看英特尔的math核心函数库。

它有一个sincosfunction

根据这个文档,它在高精度模式下的核心2 duo平均为13.08个时钟/单元,我认为它比fsincos更快。

我已经发布了一个涉及内联ARM汇编的解决scheme,可以同时计算两个angular度的正弦和余弦: ARMv7 + NEON的快速正弦/余弦

在CEPHES库中有一个很好的解决scheme,它可以非常快速,并且可以相当灵活地添加/删除准确性,从而使CPU时间更less/更less。

请记住,cos(x)和sin(x)是exp(ix)的实部和虚部。 所以我们要计算exp(ix)来得到两者。 我们预先计算exp(iy)对于0到2pi之间的y的离散值。 我们将x移到区间[0,2pi)。 然后我们select最接近x的y并写入
EXP(ⅸ)= EXP(IY +(IX-IY))= EXP(IY)EXP(I(XY))。

我们从查找表中得到exp(iy)。 由于| xy | 是小的(至多是y值之间的距离的一半),泰勒级数将在几个项中很好地收敛,所以我们使用exp(i(xy))。 然后我们只需要一个复数乘法来得到exp(ix)。

另一个不错的特性是你可以使用SSE进行vector化。

在javascript中同时准确而快速地近似sin和cos函数,可以在这里find: http : //danisraelmalta.github.io/Fmath/ (很容易导入到c / c ++)

你可能想看看http://gruntthepeon.free.fr/ssemath/ ,它提供了一个由CEPHES库启发的SSE向量化实现。 它具有良好的准确性(与sin / cos的最大偏差在5e-8左右)和速度(在单个呼叫的基础上稍微优于fsincos,胜过多个值)。

你有没有想过为这两个函数声明查找表? 你仍然需要“计算”sin(x)和cos(x),但是如果你不需要很高的准确度,它肯定会更快。