Java 使用;sincos“;在爪哇
在很多情况下,我不仅需要正弦,还需要同一参数的余弦 对于C,在通用unixJava 使用;sincos“;在爪哇,java,math,optimization,trigonometry,Java,Math,Optimization,Trigonometry,在很多情况下,我不仅需要正弦,还需要同一参数的余弦 对于C,在通用unixm数学库中有sincos函数。实际上,至少在i386上,这应该是一条汇编指令,fsincos sincos,sincosf,sincosl-同时计算sin和cos 我想这些好处的存在是因为在计算正弦和余弦时有明显的重叠:sin(x)^2+cos(x)^2=1。但是,如果尝试将此快捷方式设置为cos=Math.sqrt(1-sin*sin),则不会有任何回报,因为sqrt函数的成本类似 有没有办法在Java中获得同样的好处?
m
数学库中有sincos
函数。实际上,至少在i386上,这应该是一条汇编指令,fsincos
sincos,sincosf,sincosl-同时计算sin和cos
我想这些好处的存在是因为在计算正弦和余弦时有明显的重叠:sin(x)^2+cos(x)^2=1
。但是,如果尝试将此快捷方式设置为cos=Math.sqrt(1-sin*sin)
,则不会有任何回报,因为sqrt
函数的成本类似
有没有办法在Java中获得同样的好处?我想我会为双倍[]付出代价;由于增加了垃圾收集,这可能使所有的努力都没有意义
或者Hotspot编译器是否足够聪明,能够识别出我需要这两种编译器,并将其编译为sincos
命令?我是否可以测试它是否识别它,以及我是否可以帮助它识别它,例如,通过确保Math.sin
和Math.cos
命令在我的代码中是直接连续的?从Java语言的角度来看,这实际上是最有意义的:让comiler对此进行优化,以使用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
应该需要一个额外的pop,但应该在1个时钟周期内完成。假设CPU也没有对此进行优化,sincos
的速度应该是调用sin
两次的两倍(第二次计算余弦;因此我认为它需要进行加法)sqrt
在某些情况下可能更快,但sine可能更快
更新:我在C语言中做了一些实验,但没有结论。有趣的是,
sincos
似乎比sin
(没有cos
)还要快一点,当您同时计算sin
和cos
时,GCC编译器将使用fsincos
,所以它做了我希望Hotspot做的事情(或者Hotspot也做了?)。我还不能通过使用fsincos
来阻止编译器胜过我,除非不使用cos
。然后它将返回到Csin
,而不是fsin
大多数sin和cos计算直接调用硬件。没有比这更快的计算方法了。具体来说,在+-pi/4范围内,速率非常快。如果您一般使用硬件加速,并尝试将这些值限制为指定的值,则应该可以 您始终可以配置文件
但是,一般来说,sqrt的速度应该与division相同,因为div和sqrt的内部实现非常相似
Sin和cosine,OTOH是用高达10度的多项式计算的,没有任何公共系数,可能还有一个困难的模2pi约化——这是sincos中唯一共享的公共部分(不使用CORDIC时)
编辑修改后的配置文件(已更正打字错误)显示
sin+cos: 1.580 1.580 1.840 (time for 200M iterations, 3 successive trials)
sincos: 1.080 0.900 0.920
sin+sqrt: 0.870 1.010 0.860
查看热点代码,我相当确信Oracle热点VM不会将sin(a)+cos(a)优化为fsincos:请参阅,第7482ff行 然而,我怀疑单独使用fsin和fcos所增加的机器周期数很容易被其他操作(如运行GC)所掩盖。我将使用标准Java特性并分析应用程序。只有当配置文件运行表明在sin/cos调用中花费了大量时间时,我才会冒险去做一些事情
在本例中,我将创建一个JNI包装器,该包装器使用2元素jdoublearray作为out参数。如果只有一个线程使用sincos JNI操作,那么可以在Java代码中使用静态初始化的double[2]数组,该数组将被反复使用。我已经用caliper执行了一些微基准测试。在-4*pi.范围内的(预计算)随机数数组上进行10000000次迭代。。4*pi。我尽了最大努力获得了我能想到的最快的JNI解决方案-很难预测您是否会得到
fsincos
或一些模拟的sincos
。报告的数字是10次卡尺试验中最好的一次(试验由3-10次试验组成,报告平均值)。因此,大致上每个内部循环运行30-100次
我已经对几个变体进行了基准测试:
仅限(参考)Math.sin
仅限(参考)Math.cos
+Math.sin
Math.cos
通过JNIsincos
+cos通过Math.sin
+符号重建Math.sqrt((1+sin)*(1-sin))
+sinviaMath.cos
+符号重建Math.sqrt((1+cos)*(1-cos))
(1+sin)*(1-sin)=1-sin*sin
数学上,但如果sin接近1,它应该更精确?运行时差异最小,您只需保存一个添加项
通过x%=TWOPI进行符号重建;如果(x)
测试。我认为我的结果不支持sin+sqrt
或cos+sqrt
显然更可取,但它们确实比sin
然后cos
节省了大约40%的时间
如果我们将Java扩展为具有内部优化的sincos,那么这可能会更好。这是一个常见的用例,例如在图形中。当用于AWT、Batik等时,许多应用程序都可以从中受益
如果我再运行一次,我还将添加JNIsin
和noop
来估计JNI的成本。也许还可以通过JNI对sqrt
技巧进行基准测试。只是为了确保我们确实需要一个内在的sincos
。没有Sin 1,30 ==============
Cos 1,29 ==============
Sin, Cos 2,52 ============================
JNI sincos 1,77 ===================
SinSqrt 1,49 ================
CosSqrt 1,51 ================
Size test: 10000000
Angles range: [0.0...45.0]
Time in ms
Trial | Math.sin() | Lut sin() | LUT90.sin() | Lut sin2() [interpolation]
0 312,5879 25,2280 27,7313 36,4127
1 12,9468 19,5467 21,9396 34,2344
2 7,6811 16,7897 18,9646 32,5473
3 7,7565 16,7022 19,2343 32,8700
4 7,6634 16,9498 19,6307 32,8087
Size test: 10000000
Angles range: [-360.0...360.0]
Time in ms
Trial|Math.sin() | Lut sin() | LUT90.sin() | Lut.sin2() [interpolation]
0 942,7756 35,1488 47,4198 42,9466
1 915,3628 28,9924 37,9051 41,5299
2 430,3372 24,8788 34,9149 39,3297
3 428,3750 24,8316 34,5718 39,5187