昨天我看到一个问题,为什么Math.pow(int,int)
这么慢,但这个问题措辞不佳,没有显示研究工作,所以很快就关闭了。为什么Math.pow(int,int)比我的天真执行慢?
我做了一些自己的测试,发现Math.pow
方法实际上运行速度相对于我自己的天真实现(甚至不是一个特别高效的实现)在处理整数参数时运行得非常慢。下面是我跑测试这个代码:
class PowerTest {
public static double myPow(int base, int exponent) {
if(base == 0) return 0;
if(exponent == 0) return 1;
int absExponent = (exponent < 0)? exponent * -1 : exponent;
double result = base;
for(int i = 1; i < absExponent; i++) {
result *= base;
}
if(exponent < 1) result = 1/result;
return result;
}
public static void main(String args[]) {
long startTime, endTime;
startTime = System.nanoTime();
for(int i = 0; i < 5000000; i++) {
Math.pow(2,2);
}
endTime = System.nanoTime();
System.out.printf("Math.pow took %d milliseconds.\n", (endTime - startTime)/1000000);
startTime = System.nanoTime();
for(int i = 0; i < 5000000; i++) {
myPow(2,2);
}
endTime = System.nanoTime();
System.out.printf("myPow took %d milliseconds.\n", (endTime - startTime)/1000000);
}
}
在我的电脑(在Intel CPU x86_64的Linux)的,输出几乎总是报道,Math.pow
了10毫秒,而myPow
了2ms的。这偶尔会在这里或那里出现一毫秒的波动,但Math.pow
的运行速度大约是,比平均速度慢了5倍。
我做了一些研究,并根据grepcode,Math.pow
只提供与(double, double)
类型签名的方法,它是推迟到StrictMath.pow
方法,它是一个本地方法调用。
Math
库只提供处理双打的pow
函数的事实似乎表示可能的回答此问题。显然,一个必须处理double类型的基类或指数的可能性的幂运算法则比我的只处理整数的算法需要更长的时间来执行。但最终归结为依赖于体系结构的本机代码(它几乎总是比JVM字节代码运行得更快,在我的情况下可能是C或汇编)。 似乎在这个级别上,如果可能的话,将优化检查数据类型并运行更简单的算法。
鉴于此信息,为什么本机Math.pow
方法的运行速度比给定整数参数时未优化和天真的myPow
方法慢得多?
我对此评论: int absExponent =(exponent <0)?指数* -1:指数; 你不需要'*': int absExponent =(exponent <0)? - 指数:指数; –
这甚至不是最优(整数)功率算法 - 有效的算法是if(absExponent&1 == 0){result * = result; absExponent >> = 1}' - 即它运行在'O(log n)'而不是'O(n)'时间。 – Alnitak
由于你的实现在指数上使用循环,所以计算正方形是一个非常偏颇的基准。你为什么不在2⁸中尝试这两种方法:)(当然,你可以用一种不那么幼稚的算法做得更好,但即使如此,2 15也可能会给你一个不同的结果。) – rici