在很多情况我不仅需要正弦,也同样参数的余弦值。
对于C,有sincos
在常见的UNIX功能m
数学库。 而实际上,至少在i386上,这应该是一个单一的汇编指令, fsincos
。
正余弦,sincosf,sincosl - 同时计算正弦和余弦
我想存在这些好处,因为在计算正弦和余弦的明显重叠: sin(x)^2 + cos(x)^2 = 1
。 但据我所知它没有还清,试图走捷径这是cos = Math.sqrt(1 - sin*sin)
因为sqrt
函数是在一个类似的成本。
有什么办法谋取在Java中同样的好处? 我想我会为此付出代价的double[]
即可; 这可能使因为添加的垃圾收集的实际意义所作的一切努力。
或者说是热点编译器足够聪明,意识到我需要两个,并将汇编这一个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
应该需要一个额外的流行,但不应以1个时钟周期。 假设CPU也不会优化此, sincos
应该差不多快两倍,调用sin
两次(第二次来计算余弦,所以我想,这将需要做加法)。 sqrt
可能是在某些情况下会更快,但正弦波可以更快。
更新 :我已经做了一些C的实验,但他们仍未有定论。 有趣的是, sincos
似乎甚至稍快比sin
(没有cos
),以及GCC编译器将使用fsincos
当你同时计算sin
和cos
-因此它希望做什么,我热点做(或做热点,太? )。 我还不能阻止编译器使用智胜我fsincos
除了不使用cos
。 然后它会回落到一个C sin
,不fsin
。
我曾与卡尺进行一些微基准。 千万次迭代过的随机数的范围为-4 * PI一个(预计算)阵列。4 * PI。 我尽我所能,以获得最快的JNI解决方案,我可以拿出去-这是一个有点很难预测是否会真正得到fsincos
或一些模拟的sincos
。 报告的数字是最好的10个卡钳试验(其又包括3-10试验,其中的报道平均值)。 所以大概是30-100每一个内循环的运行。
我已经基准几个变种:
-
Math.sin
只(参考) -
Math.cos
只(参考) -
Math.sin
+ Math.cos
-
sincos
通过JNI -
Math.sin
经由+ COS Math.sqrt( (1+sin) * (1-sin) )
+符号重建 -
Math.cos
经由+罪Math.sqrt( (1+cos) * (1-cos) )
+符号重建
(1+sin)*(1-sin)=1-sin*sin
数学,但如果单是接近于1应该是更精确的? 运行时不同的是最小的,你救一个加法。
注册通过重建x %= TWOPI; if (x<0) x+=TWOPI;
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
比计算更好sin
和cos
,但sqrt
的做法仍然较快。 cos
本身似乎是一致蜱(0,01)胜过sin
,但情况区别重建标志有一个额外>
测试。 我不认为我的研究结果支持,要么sin+sqrt
或cos+sqrt
显然preferrable,但他们并保存时相比40%左右sin
,然后cos
。
如果我们将扩展Java有一个内在的优化正余弦 ,那么这可能会更好。 恕我直言,这是一个常见的情况,例如在图形。 当在AWT中使用,蜡染等各种应用可以从中受益。
如果我再次运行此,我还要补充JNI sin
和noop
估计JNI的成本。 也许还标杆sqrt
通过JNI伎俩。 只是为了确保我们其实也希望有一个内在的sincos
,从长远来看。
大多数的正弦和余弦计算是直接在硬件的调用。 没有太多的更快的方法来计算的话比。 具体而言,在范围+ - π/ 4,该速率是非常快。 如果您使用硬件加速一般,并试图限制值的规定,你应该罚款。 源 。
综观热点代码,我宁愿相信,甲骨文HotSpot虚拟机不优化罪(一)+ COS(一)到FSINCOS:见assembler_x86.cpp ,线7482ff。
然而,我会怀疑机器周期的使用单独FSIN和FCOS数量增加很容易被其它操作,如运行GC outshadowed。 我会用标准的Java功能和分析的应用程序。 只有当一个运行配置文件表明显著时间以度过罪/余弦电话,我敢说出来做一些事情。
在这种情况下,我会创建使用2元件jdoublearray作为输出参数JNI封装。 如果你有一个使用JNI正余弦操作只有一个线程,可以在将被重用一遍一遍Java代码中使用静态初始化双[2]数组。
您可以随时轮廓。
然而,通常开方应该来以相同的速度为师,为内部实现DIV和开方的非常相似。
正弦和余弦函数,与高达10度的多项式计算OTOH没有任何共同的系数和可能的困难模2π减小 - 这是唯一的公共部分中的正余弦共享(未使用CORDIC时)。
EDIT修订分析(与错字校正的)示出了定时差为
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
有没有在普通的Java提供FSINCOS。 另外,JNI版本可能比java.lang.Math.sin()和cos()双呼叫慢。
我猜你是关心的sin(x)的速度/ COS(X)。 所以,我给你快速三角操作的建议,在更换到FSINCOS:查找表。 下面是我原来的职位。 我希望它可以帮助你。
=====
我试图实现对三角函数(正弦和余弦)的最佳性能,使用查找表(LUT)。
我发现:
cos(x) == sin(x + PI/2)
- 也就是说,如果你有SIN(X)的表,你有COS(x)的表是免费的。
我与SIN()的一些试验中范围[0..45]角,使用java.lang.Math.sin(); 360个位置的幼稚查找表,一个优化的LUT90与范围[0..90]表中的值,但扩大了与[0..360]工作; 和查找表interpolation.Note即暖机后,java.lang.Math.sin()是比别人快:
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
可在这里来源的GitHub
但是,如果你在区间[-360..360]需要高性能,java.lang.Math中的LIB较慢。 查找表(LUT)大约快20倍。 如果要求精度高,可以使用带内插LUT,实在是有点慢,但仍比java.lang.Math中的快。 见我在Math2.java SIN2(),在上面的链接。
下面的数字是角度高范围:
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