一尘不染

在Java中使用“ sincos”

java

在很多情况下,我不仅需要正弦,而且需要相同参数的余弦。

对于C,sincos通用unix m数学库中有该函数。实际上,至少在i386上,这应该是一条汇编指令fsincos

sincos,sincosf,sincosl-同时计算sin和cos

我猜这些好处存在是因为在计算正弦和余弦时存在明显的重叠:sin(x)^2 + cos(x)^2 = 1。但是AFAIK尝试将其快捷方式化为并没有用cos = Math.sqrt(1 - sin*sin),因为该sqrt功能的费用与此相似。

有什么方法可以在Java中获得相同的好处吗?我想我要为此付出代价double[]。由于增加了垃圾回收,这可能使所有工作变得毫无意义。

还是Hotspot编译器足够聪明,足以识别出我需要两者,并将其编译成sincos命令?我可以测试它是否可以识别它,并且可以例如通过确保Math.sinMath.cos命令在我的代码中直接连续来帮助它识别它?从Java语言的角度来看,这实际上是最有意义的:让编译器对其进行优化以使用fsincos程序集调用。

从一些汇编器文档中收集:

Variations    8087         287        387      486     Pentium
fsin           -            -       122-771  257-354   16-126  NP
fsincos        -            -       194-809  292-365   17-137  NP
 Additional cycles required if operand > pi/4 (~3.141/4 = ~.785)
sqrt        180-186      180-186    122-129   83-87    70      NP

fsincos应该需要一个额外的弹出声,但是应该在1个时钟周期内出现。假设CPU也没有对此进行优化,sincos则其速度应该几乎是调用sin两次的速度的两倍(第二次计算余弦;因此我认为它需要做一个加法运算)。sqrt在某些情况下可能会更快,但正弦可能会更快。

更新
:我已经在C语言中做了一些实验,但是还没有定论。有趣的是,sincos似乎甚至略高于是sin(无cos),以及GCC编译器将使用fsincos,当你同时计算sincos-所以它做什么,我想热点做(或做热点,太?)。fsincos除了不使用之外,我还无法阻止编译器使我胜过我cos。然后它会回落到一个C
sin,没有fsin


阅读 264

收藏
2020-12-03

共1个答案

一尘不染

我用卡尺进行了一些微基准测试。在(预先计算的)随机数数组(-4 * pi .... 4 * pi)上进行10000000次迭代。我尽了最大的努力来获得最快的JNI解决方案-
很难预测您是否会真正得到fsincos仿真sincos。报告的数字是10项卡尺试验中最好的一项(依次包括3到10次试验,报告了平均值)。大约每个内循环运行30-100次。

我已经对几种变体进行了基准测试:

  • Math.sin 仅(参考)
  • Math.cos 仅(参考)
  • Math.sin + Math.cos
  • sincos 通过JNI
  • Math.sin+通过Math.sqrt( (1+sin) * (1-sin) )+符号重建cos
  • Math.cos+通过Math.sqrt( (1+cos) * (1-cos) )+符号重建实现犯罪

(1+sin)*(1-sin)=1-sin*sin从数学上讲,但是如果sin接近1,它应该更精确吗?运行时差异最小,您节省了一个附加项。

通过进行符号重建x %= TWOPI; if (x<0) x+=TWOPI;,然后检查象限。如果您有一个想法,如何用更少的CPU来执行此操作,我将很高兴听到。

sqrt至少对于常见角度而言,数值通孔似乎还可以。从粗略的实验来看,范围为1e-10。

Sin         1,30 ==============
Cos         1,29 ==============
Sin, Cos    2,52 ============================
JNI sincos  1,77 ===================
SinSqrt     1,49 ================
CosSqrt     1,51 ================

sqrt(1-s*s)主场迎战sqrt((1+s)*(1-s))使得约0.01差别。如您所见,sqrt基于方法的方法胜过其他方法(因为我们目前无法sincos在纯Java中访问)。JNI
sincos优于计算sincos,但是该sqrt方法仍然更快。cos本身似乎始终比sick(0,01)更好sin,但是用于区分符号的大小写区分需要额外的>测试。我不认为我的研究结果支持,要么sin+sqrtcos+sqrt明显preferrable,但他们节省约的40%的时间相比,sin然后cos

如果我们将Java扩展为具有 固有的优化sincos
,那么可能会更好。恕我直言,这是一个常见的用例,例如在图形中。当在AWT,蜡染等中使用时,许多应用程序都可以从中受益。

如果再次运行它,我还将添加JNI sin和一个noop以估计JNI的成本。也许还可以sqrt通过JNI
对这个技巧进行基准测试。只是为了确保我们确实希望sincos从长远来看具有内在性。

2020-12-03