Tôi đang cố triển khai Fast Inverse Square Root trên java để tăng tốc độ chuẩn hóa vectơ. Tuy nhiên, khi tôi thực hiện phiên bản đơn chính xác trong Java, tôi nhận được tốc độ về giống như 1F/(float)Math.sqrt()
lúc đầu, sau đó nhanh chóng giảm xuống một nửa tốc độ. Điều này là thú vị, bởi vì trong khi Math.sqrt sử dụng (tôi giả sử) một phương pháp bản địa, điều này liên quan đến việc phân chia điểm trôi nổi, mà tôi đã nghe là rất chậm. Mã của tôi để tính toán các con số như sau:Tại sao căn bậc hai nghịch đảo nhanh nên kỳ lạ và chậm trên Java?
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;
}
Sử dụng một chương trình ngắn tôi đã viết để lặp mỗi 16 triệu lần, sau đó kết quả tổng hợp, và lặp lại, tôi nhận được kết quả như thế này:
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()
Tôi liên tục nhận được các con số có cùng tốc độ cho cả hai, theo sau là một lần lặp mà Fast Inverse Square Root tiết kiệm khoảng 60% thời gian cần thiết bởi 1F/Math.sqrt()
, tiếp theo là một số lần lặp lại mất khoảng gấp đôi so với Fast Inverse Square Root để chạy như kiểm soát. Tôi đang bối rối tại sao FISR sẽ đi từ Same -> 60 phần trăm nhanh hơn -> 100 phần trăm chậm hơn, và nó sẽ xảy ra mỗi khi tôi chạy chương trình của tôi.
EDIT: Dữ liệu trên là khi tôi chạy nó trong nhật thực. Khi tôi chạy chương trình với javac/java
tôi nhận được dữ liệu hoàn toàn khác nhau:
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: Sau một vài phản ứng, có vẻ như tốc độ ổn định sau nhiều lần lặp lại, nhưng số nó ổn định để là rất dễ bay hơi. Bất cứ ai có bất kỳ ý tưởng tại sao?
Dưới đây là mã của tôi (không hẳn là chính xác, nhưng đây là toàn bộ sự việc):
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();
}
}
Có lẽ các chuyển đổi bit thô chỉ chậm trong Java? (Hoặc thêm đủ chi phí mà bất kỳ lợi ích nào nhìn thấy việc triển khai C bị thổi bay đi.) Có phải sự chậm lại * luôn tăng * trong các lần chạy trong cùng một phiên không? – user2246674
Trên địa phương của tôi, một số fastinverse chạy đầu tiên chậm hơn, nhưng sau đó nó chạy nhanh hơn nhiều. Vì vậy, có lẽ JIT đang làm điều gì đó. –
Bạn nên làm điều này trong hội đồng để có được điều đó nhanh chóng từ đầu. Chỉ trong thời gian trình biên dịch đưa ra quyết định để có được nhanh chóng. –