- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
背景
注意到我正在处理的 Java 程序的执行速度比预期的慢,我决定修改我认为可能导致问题的代码区域 - 调用 Math.pow(x, 2)从 for 循环中。与another questions on this site相反,我创建的一个简单基准测试(最后的代码)发现用 x*x 替换 Math.pow(x, 2) 实际上使循环加速了近 70 倍:
x*x: 5.139383ms
Math.pow(x, 2): 334.541166ms
请注意,我知道该基准并不完美,并且肯定应该对这些值持保留态度 - 基准的目的是获得一个大概的数字。
问题
虽然基准测试给出了有趣的结果,但它并没有准确地对我的数据建模,因为我的数据主要由 0 组成。因此,更准确的测试是在没有标记为可选的 for 循环的情况下运行基准测试。根据 Math.pow() 的 javadoc
If the first argument is positive zero and the second argument is greater than zero, or the first argument is positive infinity and the second argument is less than zero, then the result is positive zero.
所以预计该基准测试会运行得更快,对吧!?然而实际上,这又要慢得多:
x*x: 4.3490535ms
Math.pow(x, 2): 3082.1720006ms
当然,人们可能认为 math.pow() 代码比简单的 x*x 代码运行得慢一点,因为它需要在一般情况下工作,但慢了 700 倍?到底是怎么回事!?为什么 0 的情况比 Math.random() 的情况慢得多?
更新:根据@Stephen C 的建议更新了代码和时间。然而,这没什么区别。
用于基准测试的代码
请注意,重新排序两个测试的差异可以忽略不计。
public class Test {
public Test(){
int iterations = 100;
double[] exampleData = new double[5000000];
double[] test1Results = new double[iterations];
double[] test2Results = new double[iterations];
//Optional
for (int i = 0; i < exampleData.length; i++) {
exampleData[i] = Math.random();
}
for (int i = 0; i < iterations; i++) {
test1Results[i] = test1(exampleData);
test2Results[i] = test2(exampleData);
}
System.out.println("x*x: " + calculateAverage(test1Results) / 1000000 + "ms");
System.out.println("Math.pow(x, 2): " + calculateAverage(test2Results) / 1000000 + "ms");
}
private long test1(double[] exampleData){
double total = 0;
long startTime;
long endTime;
startTime = System.nanoTime();
for (int j = 0; j < exampleData.length; j++) {
total += exampleData[j] * exampleData[j];
}
endTime = System.nanoTime();
System.out.println(total);
return endTime - startTime;
}
private long test2(double[] exampleData){
double total = 0;
long startTime;
long endTime;
startTime = System.nanoTime();
for (int j = 0; j < exampleData.length; j++) {
total += Math.pow(exampleData[j], 2);
}
endTime = System.nanoTime();
System.out.println(total);
return endTime - startTime;
}
private double calculateAverage(double[] array){
double total = 0;
for (int i = 0; i < array.length; i++) {
total += array[i];
}
return total/array.length;
}
public static void main(String[] args){
new Test();
}
}
最佳答案
虽然这是一个糟糕的基准,但幸运的是它揭示了一个有趣的效果。
这些数字表明您显然是在“客户端”VM 下运行基准测试。它没有非常强大的 JIT 编译器(称为 C1 编译器),缺乏很多优化。难怪它的效果不如预期。
Math.pow
调用,即使它没有副作用也是如此。Y=2
和 X=0
,它都没有专门的快速路径。至少,在 Java 9 之前它没有。这最近已在 JDK-8063086 中修复。然后在JDK-8132207进一步优化.但有趣的是 Math.pow
对于 X=0
和 C1 编译器确实更慢!
但是为什么?由于实现细节。
x86 架构不提供计算 X^Y 的硬件指令。但是还有其他有用的说明:
FYL2X
计算 Y * log₂XF2XM1
计算 2^X - 1因此,X^Y = 2^(Y * log₂X)。由于 log2X 仅针对 X > 0 定义,FYL2X
以 X=0
的异常结束并返回 -Inf
。因此,X=0
是在一条缓慢的异常路径中处理的,而不是在专门的快速路径中处理的。
那怎么办?
首先,停止使用 Client VM,尤其是在您关心性能的情况下。切换到最新的 64 位 JDK 8,您将获得最佳的 C2 优化 JIT 编译器。当然,它可以很好地处理 Math.pow(x, 2)
等。然后写一个 correct benchmark使用适当的工具,如 JMH .
关于java - 当 x = 0 时,Java 的 Math.pow(x, 2) 性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34189749/
这个问题在这里已经有了答案: Converting result of Math.sin(x) into a result for degrees in java (4 个答案) 关闭 5 年前。
我在学习 Kotlin 并在数学课上遇到了这个问题: java.lang.Math 和 kotlin.math 不兼容。这对我来说有点尴尬和困惑,因为 Kotlin 声称它与 Java 100% 兼容
我在其他问题中读到,例如由于浮点表示,sin(2π) 不为零,但非常接近。这个非常小的错误在我的代码中不是问题,因为例如我可以四舍五入 5 位小数。 但是当2π乘以一个非常大的数时,误差就会放大很多。
我正在用 C# 编写一个计算器。 textBoxResult 是我显示数字的文本框 recount 是一个以度为单位的角度并以弧度为单位返回的函数 我从 texBoxInput 获取角度 public
首先,让我们从我的数学背景开始。我已经学习了微积分 I - IV 和微分方程。我参加了第一学期的计算机图形类(class),在该类(class)中我们实现了几乎我们自己的图形管道,包括使用 Phong
早上好! 我只是想磨练我的数学能力,我特别有一些关于 Cocos2D 的问题。由于 Cocos2D 想要“简化”事物,所有 Sprite 都有一个旋转属性,范围从 0-360(359?)CW。这迫使你
是否有人对Intel Math Kernel Library和AMD Math Core Library都有编程经验?我正在建立一台用于高性能统计计算的个人计算机,并对正在购买的组件进行辩论。 AMD
函数的反函数是什么 math.atan2 我在 Lua 中使用它,我可以通过 math.tan 获得 math.atan 的逆。 但我在这里迷路了。 编辑 好的,让我向您提供更多详细信息。 我需要计算
我有一道等轴测投影的数学题。我读了一篇文章:Axonometric projections - a technical overview .对于等距投影部分,它给出了将 x 部分的 3D 点转换为 2
在 MySQL (5.1) 数据库表中,有数据表示: 用户执行任务需要多长时间 用户在任务中处理了多少项目。 MySQL 是否支持关联数据,还是我需要使用 PHP/C# 来计算? 我在哪里可以找到计算
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 9 年前。 Improv
我正在尝试使用这两种方法在 C# 中解决这个问题: public double NormalPowerMethod(double x, double toPower) { return Mat
如何分配: var randomNumber = Math.random()*50 + Math.random()*20; 比较: var randomNumber = Math.random()*7
我正在查看我的代码,希望提高它的性能,然后我看到了这个: int sqrt = (int) Math.floor(Math.sqrt(n)); 哦,好的,我真的不需要调用 Math.floor,因为转
尝试调用 math.h 中的函数时, 我收到如下链接错误 undefined reference to sqrt 但我正在做一个 #include 我正在使用 gcc 并编译如下: gcc -Wall
祝大家有个愉快的一天,我有话要问你,为了更好地理解这里是我的代码: {math equation=((($order_total-$commission)+$discount+$delivery_ch
我尝试学习一些Clojure,因为该语言看起来不错。 但是似乎没有关于如何安装/使用库的信息,例如clojure.math.numeric-tower。 现在,我通过在Linux shell中键入以下
As Math.sign() 接受数字参数或数字作为字符串,如 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Gl
如何将scala.math.BigDecimal转换为java.math.BigDecimal? 最佳答案 无需在字符串之间进行双重转换。 val sb = scala.math.BigDecimal
为什么下面的 JavaScript 会这样 Math instanceof Math 抛出错误 TypeError: Expecting a function in instanceof check,
我是一名优秀的程序员,十分优秀!