FULL PRODUCT VERSION :
Tested on both JDK 1.5.0_04 and 1.6.0-ea-b42.
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
A DESCRIPTION OF THE PROBLEM :
Seems that Math.round() is extremely slow.
On my Centrino laptop it takes about 200 cycles to complete, when resonable value is about 5 cycles, I would say.
It is so slow, that doing it manually, in Java, using Double.doubleToRawLongBits, actually makes it 10 times faster!
Normally I would not submit a performance issue as a bug, but this one really extreme.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Just call Math.round, and see how slow it is.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
That calling it will take ~5 machine cycles.
ACTUAL -
It takes ~200 machine cycles.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
/**
* Demo program, that shows how slow {@link Math#round(double)} is.
*
* @author Doron Rajwan, 2005.
*/
public class TestRound {
private static final double x0 = 500000.0, y0=3000000.0, inverseBinSizeX=1.0/30.0, inverseBinSizeY=1.0/20.0;
private static final int samples = 12345678;
private static final double deltaX = 12345.0 / (5 + samples);
private static final double deltaY = 23456.0 / (5 + samples);
private static void performanceTestStandard() {
long start = System.nanoTime();
int sum = 0;
double xx = 777.1, yy = 888.1;
for (int i = 0; i < samples; ++i) {
xx += deltaX; yy += deltaY;
int binI = (int) Math.round((xx - x0) * inverseBinSizeX);
int binJ = (int) Math.round((yy - y0) * inverseBinSizeY);
sum += binI + binJ;
}
long time = System.nanoTime() - start;
System.out.println("Using standard round: time per iteration=" + ((double)time / samples) + " ns, sum=" + sum);
if (sum != 1734030338)
throw new AssertionError(); // force assertions here...
}
private static void performanceTestFast() {
long start = System.nanoTime();
int sum = 0;
double xx = 777.1, yy = 888.1;
for (int i = 0; i < samples; ++i) {
xx += deltaX; yy += deltaY;
int binI = fastRound((xx - x0) * inverseBinSizeX);
int binJ = fastRound((yy - y0) * inverseBinSizeY);
sum += binI + binJ;
}
long time = System.nanoTime() - start;
System.out.println("Using FAST round: time per iteration=" + ((double)time / samples) + " ns, sum=" + sum);
if (sum != 1734030338)
throw new AssertionError(); // force assertions here...
}
private static final double twoToThe52 = (double)(1L << 52); // 2^52
// Works like round(), but with the following differences:
// 1. more than x10 faster.
// 2. rounds halfs towards even.
private static int fastRound(double a) {
double dd = twoToThe52 + Math.abs(a);
int ll = (int)Double.doubleToRawLongBits(dd);
int signMask = (int)(Double.doubleToRawLongBits(a) >> 63); // 0 or -1.
return (ll ^ signMask) - signMask;
}
public static void main(String[] args) {
performanceTestStandard(); performanceTestFast();
performanceTestStandard(); performanceTestFast();
performanceTestStandard(); performanceTestFast();
performanceTestStandard(); performanceTestFast();
performanceTestStandard(); performanceTestFast();
}
// Sample output on my 1.5GHz Centrino laptop, using "-server -Xbatch" command line:
// Using standard round: time per iteration=223.8108062594861 ns, sum=1734030338
// Using FAST round: time per iteration=22.15142999841726 ns, sum=1734030338
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
See my sample code for workaround...
###@###.### 2005-07-14 00:29:52 GMT