昨天我看到一個問題,爲什麼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