Ich versuche Fast Inverse Square Root auf Java zu implementieren, um die Vektornormierung zu beschleunigen. Wenn ich jedoch die Single-Precision-Version in Java implementiere, erreiche ich zuerst die gleichen Geschwindigkeiten wie 1F/(float)Math.sqrt()
, dann fällt die Geschwindigkeit schnell auf die Hälfte ab. Das ist interessant, denn während Math.sqrt eine native Methode verwendet (ich vermute), handelt es sich um eine Gleitkommadivision, von der ich gehört habe, dass sie sehr langsam ist. Mein Code für die Berechnung der Zahlen ist wie folgt:Warum ist die schnelle inverse Quadratwurzel auf Java so merkwürdig und langsam?
public static float fastInverseSquareRoot(float x){
float xHalf = 0.5F * x;
int temp = Float.floatToRawIntBits(x);
temp = 0x5F3759DF - (temp >> 1);
float newX = Float.intBitsToFloat(temp);
newX = newX * (1.5F - xHalf * newX * newX);
return newX;
}
ein kurzes Programm verwenden ich geschrieben habe, die jeweils 16 Millionen mal zu durchlaufen, dann aggregierte Ergebnisse, und wiederholen Sie, ich Ergebnisse wie folgt aus:
1F/Math.sqrt() took 65209490 nanoseconds.
Fast Inverse Square Root took 65456128 nanoseconds.
Fast Inverse Square Root was 0.378224 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 64131293 nanoseconds.
Fast Inverse Square Root took 26214534 nanoseconds.
Fast Inverse Square Root was 59.123647 percent faster than 1F/Math.sqrt()
1F/Math.sqrt() took 27312205 nanoseconds.
Fast Inverse Square Root took 56234714 nanoseconds.
Fast Inverse Square Root was 105.895914 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 26493281 nanoseconds.
Fast Inverse Square Root took 56004783 nanoseconds.
Fast Inverse Square Root was 111.392402 percent slower than 1F/Math.sqrt()
Ich bekomme konsistent Zahlen, die für beide die gleiche Geschwindigkeit haben, gefolgt von einer Iteration, bei der Fast Inverse Quadratwurzel etwa 60 Prozent der Zeit von 1F/Math.sqrt()
benötigt, gefolgt von mehreren Iterationen, die für Fast Inverse Square Root etwa doppelt so lange dauern als Kontrolle laufen. Ich bin verwirrt, warum FISR von Same gehen würde -> 60 Prozent schneller -> 100 Prozent langsamer, und es passiert jedes Mal, wenn ich mein Programm starte.
EDIT: Die obigen Daten sind, wenn ich es in Eclipse ausführen. Wenn ich das Programm mit javac/java
laufen bekomme ich ganz andere Daten:
1F/Math.sqrt() took 57870498 nanoseconds.
Fast Inverse Square Root took 88206794 nanoseconds.
Fast Inverse Square Root was 52.421004 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 54982400 nanoseconds.
Fast Inverse Square Root took 83777562 nanoseconds.
Fast Inverse Square Root was 52.371599 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 21115822 nanoseconds.
Fast Inverse Square Root took 76705152 nanoseconds.
Fast Inverse Square Root was 263.259133 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 20159210 nanoseconds.
Fast Inverse Square Root took 80745616 nanoseconds.
Fast Inverse Square Root was 300.539585 percent slower than 1F/Math.sqrt()
1F/Math.sqrt() took 21814675 nanoseconds.
Fast Inverse Square Root took 85261648 nanoseconds.
Fast Inverse Square Root was 290.845374 percent slower than 1F/Math.sqrt()
EDIT2: Nach ein paar Antworten, wie es scheint die Geschwindigkeit nach mehreren Iterationen stabilisiert, aber die Zahl stabilisiert sie zu sehr volatil ist. Jeder hat eine Idee warum?
Hier ist mein Code (nicht gerade kurz, aber hier ist die ganze Sache):
public class FastInverseSquareRootTest {
public static FastInverseSquareRootTest conductTest() {
float result = 0F;
long startTime, endTime, midTime;
startTime = System.nanoTime();
for (float x = 1F; x < 4_000_000F; x += 0.25F) {
result = 1F/(float) Math.sqrt(x);
}
midTime = System.nanoTime();
for (float x = 1F; x < 4_000_000F; x += 0.25F) {
result = fastInverseSquareRoot(x);
}
endTime = System.nanoTime();
return new FastInverseSquareRootTest(midTime - startTime, endTime
- midTime);
}
public static float fastInverseSquareRoot(float x) {
float xHalf = 0.5F * x;
int temp = Float.floatToRawIntBits(x);
temp = 0x5F3759DF - (temp >> 1);
float newX = Float.intBitsToFloat(temp);
newX = newX * (1.5F - xHalf * newX * newX);
return newX;
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 7; i++) {
System.out.println(conductTest().toString());
}
}
private long controlDiff;
private long experimentalDiff;
private double percentError;
public FastInverseSquareRootTest(long controlDiff, long experimentalDiff) {
this.experimentalDiff = experimentalDiff;
this.controlDiff = controlDiff;
this.percentError = 100D * (experimentalDiff - controlDiff)
/controlDiff;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("1F/Math.sqrt() took %d nanoseconds.%n",
controlDiff));
sb.append(String.format(
"Fast Inverse Square Root took %d nanoseconds.%n",
experimentalDiff));
sb.append(String
.format("Fast Inverse Square Root was %f percent %s than 1F/Math.sqrt()%n",
Math.abs(percentError), percentError > 0D ? "slower"
: "faster"));
return sb.toString();
}
}
Vielleicht sind die rohen Bit-Konvertierungen in Java nur langsam? (Oder fügen Sie genügend Overhead hinzu, dass alle Gewinne, die die C-Implementierungen erfahren, weggeblasen werden.) Steigt die Verlangsamung * in Läufen innerhalb derselben Sitzung immer *? – user2246674
Auf meiner lokalen die ersten mehrere Lauf fastinverse ist langsamer, aber in späteren Läufen ist es viel schneller. Vielleicht macht JIT etwas. –
Sie sollten dies in der Montage tun, um so schnell von Anfang an zu bekommen. Just in Time Compiler trifft Entscheidungen, um schnell zu werden. –