Java 的快速超越/三角函数
由于 java.lang.Math 中的三角函数非常慢:是否有一个库可以快速且良好地近似? 似乎可以在不损失太多精度的情况下以数倍的速度进行计算。 (在我的机器上,乘法需要 1.5ns,而 java.lang.Math.sin 需要 46ns 到 116ns)。 不幸的是,目前还没有办法使用硬件功能。
更新:这些函数应该足够准确,例如,对于 GPS 计算。 这意味着您需要至少 7 位十进制数字的精度,这排除了简单的查找表。 在你的基本 x86 系统上它应该比 java.lang.Math.sin 快得多。 否则的话就没有意义了。
对于超过 pi/4 的值,除了硬件之外,Java 还会进行一些昂贵的计算功能。 这样做有充分的理由,但有时您更关心的是速度而不是最后一位的准确性。
Since the trigonometric functions in java.lang.Math are quite slow: is there a library that does a quick and good approximation? It seems possible to do a calculation several times faster without losing much precision. (On my machine a multiplication takes 1.5ns, and java.lang.Math.sin 46ns to 116ns). Unfortunately there is not yet a way to use the hardware functions.
UPDATE: The functions should be accurate enough, say, for GPS calculations. That means you would need at least 7 decimal digits accuracy, which rules out simple lookup tables. And it should be much faster than java.lang.Math.sin on your basic x86 system. Otherwise there would be no point in it.
For values over pi/4 Java does some expensive computations in addition to the hardware functions. It does so for a good reason, but sometimes you care more about the speed than for last bit accuracy.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我很惊讶内置的 Java 函数会这么慢。 当然,JVM 正在调用 CPU 上的本机三角函数,而不是在 Java 中实现算法。 您确定您的瓶颈是对三角函数的调用而不是一些周围的代码吗? 也许一些内存分配?
你能用 C++ 重写代码中进行数学运算的部分吗? 仅调用 C++ 代码来计算三角函数可能不会加快速度,但将某些上下文(如外循环)也移动到 C++ 可能会加快速度。
如果您必须推出自己的三角函数,请不要单独使用泰勒级数。 除非你的参数非常小,否则 CORDIC 算法要快得多。 您可以使用 CORDIC 开始,然后使用简短的泰勒级数完善结果。 请参阅此 StackOverflow 问题,了解如何实现三角函数。
I'm surprised that the built-in Java functions would be so slow. Surely the JVM is calling the native trig functions on your CPU, not implementing the algorithms in Java. Are you certain your bottleneck is calls to trig functions and not some surrounding code? Maybe some memory allocations?
Could you rewrite in C++ the part of your code that does the math? Just calling C++ code to compute trig functions probably wouldn't speed things up, but moving some context too, like an outer loop, to C++ might speed things up.
If you must roll your own trig functions, don't use Taylor series alone. The CORDIC algorithms are much faster unless your argument is very small. You could use CORDIC to get started, then polish the result with a short Taylor series. See this StackOverflow question on how to implement trig functions.
在 x86 上,java.lang.Math sin 和 cos 函数不直接调用硬件函数,因为 Intel 在实现它们方面并不总是做得那么好。 bug #4857011 中有一个很好的解释。
https://bugs.java.com/bugdatabase/view_bug?bug_id=4857011
您可能需要认真考虑不精确的结果。 有趣的是我经常花时间在其他代码中找到这个。
“但是评论说罪……”
On the x86 the java.lang.Math sin and cos functions do not directly call the hardware functions because Intel didn't always do such a good job implimenting them. There is a nice explanation in bug #4857011.
https://bugs.java.com/bugdatabase/view_bug?bug_id=4857011
You might want to think hard about an inexact result. It's amusing how often I spend time finding this in others code.
"But the comment says Sin..."
如果您只需要一些近似值,您可以将 sin 和 cos 预先存储在数组中。
例如,如果要存储从 0° 到 360° 的值:
则可以使用度/整数而不是弧度/双精度来使用此数组。
You could pre-store your sin and cos in an array if you only need some approximate values.
For example, if you want to store the values from 0° to 360°:
you then use this array using degrees/integers instead of radians/double.
我没有听说过任何库,可能是因为很少见到触发重型 Java 应用程序。 使用 JNI(相同的精度,更好的性能)、数值方法(可变精度/性能)或简单的近似表来推出自己的方法也很容易。
与任何优化一样,最好在重新发明轮子之前测试这些功能实际上是否是瓶颈。
I haven't heard of any libs, probably because it's rare enough to see trig heavy Java apps. It's also easy enough to roll your own with JNI (same precision, better performance), numerical methods (variable precision / performance ) or a simple approximation table.
As with any optimization, best to test that these functions are actually a bottleneck before bothering to reinvent the wheel.
三角函数是查找表的经典示例。 请参阅
如果您正在搜索 J2ME 库,您可以尝试:
Trigonometric functions are the classical example for a lookup table. See the excellent
If you're searching a library for J2ME you can try:
java.lang.Math 函数调用硬件函数。 您应该可以做出简单的估算,但它们不会那么准确。
在我的实验室电脑上,sin 和 cos 大约需要 144 ns。
The java.lang.Math functions call the hardware functions. There should be simple appromiations you can make but they won't be as accurate.
On my labtop, sin and cos takes about 144 ns.
在正弦/余弦测试中,我对整数 0 到 100 万进行测试。 我认为 144 ns 对您来说还不够快。
您对所需的速度有具体要求吗?
您能否在每次操作的时间方面满足您的要求?
In the sin/cos test I was performing for integers zero to one million. I assume that 144 ns is not fast enough for you.
Do you have a specific requirement for the speed you need?
Can you qualify your requirement in terms of time per operation which is satisfactory?
如果您想使用现有的东西,请查看 Apache Commons Math 包。
如果性能确实至关重要,那么您可以使用标准数学方法(特别是泰勒/麦克劳林级数)自行实现这些函数。
例如,以下是一些可能有用的泰勒级数展开式(取自 wikipedia):
< img src="https://upload.wikimedia.org/math/d/0/d/d0d68a0e34cc259212ab6e4506e3d99c.png" alt="替代文本">
Check out Apache Commons Math package if you want to use existing stuff.
If performance is really of the essence, then you can go about implementing these functions yourself using standard math methods - Taylor/Maclaurin series', specifically.
For example, here are several Taylor series expansions that might be useful (taken from wikipedia):
您能否详细说明一下,如果这些例程太慢,您需要做什么? 您也许可以通过某种方式提前进行一些坐标转换。
Could you elaborate on what you need to do if these routines are too slow. You might be able to do some coordinate transformations ahead of time some way or another.
Hart 的计算机近似值。 将一系列不同精度函数的切比雪夫经济近似公式制成表格。
编辑:将我的副本下架,结果是一本不同的书,只是听起来很相似。 这是使用其表格的 sin 函数。 (在 C 中测试,因为这对我来说更方便。)我不知道这是否会比 Java 内置更快,但至少保证它不太准确。 :) 您可能需要首先缩小参数的范围; 请参阅John Cook 的建议。 书中还有反正弦和反正切。
Computer Approximations by Hart. Tabulates Chebyshev-economized approximate formulas for a bunch of functions at different precisions.
Edit: Getting my copy off the shelf, it turned out to be a different book that just sounds very similar. Here's a sin function using its tables. (Tested in C since that's handier for me.) I don't know if this will be faster than the Java built-in, but it's guaranteed to be less accurate, at least. :) You may need to range-reduce the argument first; see John Cook's suggestions. The book also has arcsin and arctan.
这里是快速逼近三角函数的低级技巧的集合。 有 C 语言的示例代码,我发现很难理解,但这些技术在 Java 中也很容易实现。
这是我在 Java 中对 invsqrt 和 atan2 的等效实现。
我本可以对其他三角函数做类似的事情,但我发现没有必要,因为分析表明只有 sqrt 和 atan/atan2 是主要瓶颈。
Here is a collection of low-level tricks for quickly approximating trig functions. There is example code in C which I find hard to follow, but the techniques are just as easily implemented in Java.
Here's my equivalent implementation of invsqrt and atan2 in Java.
I could have done something similar for the other trig functions, but I have not found it necessary as profiling showed that only sqrt and atan/atan2 were major bottlenecks.
这可能会成功:http://sourceforge.net/projects/jafama/
That might make it : http://sourceforge.net/projects/jafama/