Background
Having noticed that the execution of a java program I am working on was slower than expected, I decided to tinker with the area of code which I thought may be causing the issue - a call to Math.pow(x, 2) from within a for loop. Contrary to another questions on this site, a simple benchmark I created (code at end) found that replacing Math.pow(x, 2) with x*x actually sped the loop up by almost 70 times:
x*x: 5.139383ms
Math.pow(x, 2): 334.541166ms
Note that I am aware that the benchmark is not perfect, and that the values should certainly be taken with a pinch of salt - the aim of the benchmark was to get a ballpark figure.
The Question
While the benchmark gave interesting results it did not model my data accurately, as my data consists largely of 0's. Thus a more accurate test was to run the benchmark without the for loop marked optional. According to the javadoc for Math.pow()
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.
So it would be expected that this benchmark would run faster right!? In reality however, this is considerably slower again:
x*x: 4.3490535ms
Math.pow(x, 2): 3082.1720006ms
Sure, one may expect the math.pow() code to run a little slower than the simple x*x code due to the fact that it needs to work for the general case, but 700x slower? What is going on!? And why is the 0 case so much slower than the Math.random() case?
UPDATE: Updated code and times based on @Stephen C's suggestion. This however made little difference.
Code used to benchmark
Note that reordering the two tests makes negligible difference.
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();
}
}