2013-05-14 30 views
14

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(); 
    } 
} 
+0

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

+0

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ì đó. –

+0

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. –

Trả lời

11

Trình tối ưu hóa JIT dường như đã thực hiện cuộc gọi đến số Math.sqrt.

Với mã chưa sửa đổi của bạn, tôi có

1F/Math.sqrt() took 65358495 nanoseconds. 
Fast Inverse Square Root took 77152791 nanoseconds. 
Fast Inverse Square Root was 18,045544 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 52872498 nanoseconds. 
Fast Inverse Square Root took 75242075 nanoseconds. 
Fast Inverse Square Root was 42,308531 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 23386359 nanoseconds. 
Fast Inverse Square Root took 73532080 nanoseconds. 
Fast Inverse Square Root was 214,422951 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 23790209 nanoseconds. 
Fast Inverse Square Root took 76254902 nanoseconds. 
Fast Inverse Square Root was 220,530610 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 23885467 nanoseconds. 
Fast Inverse Square Root took 74869636 nanoseconds. 
Fast Inverse Square Root was 213,452678 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 23473514 nanoseconds. 
Fast Inverse Square Root took 73063699 nanoseconds. 
Fast Inverse Square Root was 211,260168 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 23738564 nanoseconds. 
Fast Inverse Square Root took 71917013 nanoseconds. 
Fast Inverse Square Root was 202,954353 percent slower than 1F/Math.sqrt() 

lần liên tục chậm cho fastInverseSquareRoot, và thời gian cho điều đó là tất cả trong cùng một bóng công viên, trong khi Math.sqrt cuộc gọi tăng tốc đáng kể.

Thay đổi mã để các cuộc gọi đến Math.sqrt không thể tránh được,

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(); 
    if (result == 0) System.out.println("Wow!"); 

tôi đã

1F/Math.sqrt() took 184884684 nanoseconds. 
Fast Inverse Square Root took 85298761 nanoseconds. 
Fast Inverse Square Root was 53,863804 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 182183542 nanoseconds. 
Fast Inverse Square Root took 83040574 nanoseconds. 
Fast Inverse Square Root was 54,419278 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 165269658 nanoseconds. 
Fast Inverse Square Root took 81922280 nanoseconds. 
Fast Inverse Square Root was 50,431143 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 163272877 nanoseconds. 
Fast Inverse Square Root took 81906141 nanoseconds. 
Fast Inverse Square Root was 49,834815 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 165314846 nanoseconds. 
Fast Inverse Square Root took 81124465 nanoseconds. 
Fast Inverse Square Root was 50,927296 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 164079534 nanoseconds. 
Fast Inverse Square Root took 80453629 nanoseconds. 
Fast Inverse Square Root was 50,966689 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 162350821 nanoseconds. 
Fast Inverse Square Root took 79854355 nanoseconds. 
Fast Inverse Square Root was 50,813704 percent faster than 1F/Math.sqrt() 

nhiều chậm hơn thời gian cho Math.sqrt, và chỉ vừa lần chậm cho fastInverseSqrt (bây giờ nó phải thực hiện phép trừ trong mỗi lần lặp).

+0

Mm, rất đẹp. Tại sao JIT của tôi lại quá tệ về Math.sqrt()? –

+0

Tôi hoàn toàn không biết tại sao. Có thể là Java 7 JIT nói chung là tốt hơn ở những thứ như vậy, có thể là phiên bản cụ thể. –

+0

Nếu bạn biên dịch mã trực tiếp, bạn phải ở trên Java 7 vì 4_000_000F không nên biên dịch trên 1.6. –

0

đầu ra của tôi cho mã đăng là:

1F/Math.sqrt() took 165769968 nanoseconds. 
Fast Inverse Square Root took 251809517 nanoseconds. 
Fast Inverse Square Root was 51.902977 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 162953919 nanoseconds. 
Fast Inverse Square Root took 251212721 nanoseconds. 
Fast Inverse Square Root was 54.161816 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 161524902 nanoseconds. 
Fast Inverse Square Root took 36242909 nanoseconds. 
Fast Inverse Square Root was 77.562030 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 162289014 nanoseconds. 
Fast Inverse Square Root took 36552036 nanoseconds. 
Fast Inverse Square Root was 77.477196 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 163157620 nanoseconds. 
Fast Inverse Square Root took 36152720 nanoseconds. 
Fast Inverse Square Root was 77.841844 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 162511997 nanoseconds. 
Fast Inverse Square Root took 36426705 nanoseconds. 
Fast Inverse Square Root was 77.585221 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 162302698 nanoseconds. 
Fast Inverse Square Root took 36797410 nanoseconds. 
Fast Inverse Square Root was 77.327912 percent faster than 1F/Math.sqrt() 

Dường như JIT đá vào, và performaces tăng gần gấp mười lần. Hy vọng một người nào đó với một nắm giữ tốt hơn của JIT sẽ đến và giải thích điều này. Môi trường của tôi: Java 6, Eclipse.

0

Jit của tôi có 2 bước để nhanh hơn: đầu tiên có thể là tối ưu hóa thuật toán và thứ hai có thể là tối ưu hóa lắp ráp.

1F/Math.sqrt() took 78202645 nanoseconds. 
Fast Inverse Square Root took 79248400 nanoseconds. 
Fast Inverse Square Root was 1,337237 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 76856008 nanoseconds. 
Fast Inverse Square Root took 24788247 nanoseconds. 
Fast Inverse Square Root was 67,747158 percent faster than 1F/Math.sqrt() 

1F/Math.sqrt() took 24162119 nanoseconds. 
Fast Inverse Square Root took 70651968 nanoseconds. 
Fast Inverse Square Root was 192,407996 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 24163301 nanoseconds. 
Fast Inverse Square Root took 70598983 nanoseconds. 
Fast Inverse Square Root was 192,174414 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 24201621 nanoseconds. 
Fast Inverse Square Root took 70667344 nanoseconds. 
Fast Inverse Square Root was 191,994259 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 24219835 nanoseconds. 
Fast Inverse Square Root took 70698568 nanoseconds. 
Fast Inverse Square Root was 191,903591 percent slower than 1F/Math.sqrt() 

1F/Math.sqrt() took 24231663 nanoseconds. 
Fast Inverse Square Root took 70633991 nanoseconds. 
Fast Inverse Square Root was 191,494608 percent slower than 1F/Math.sqrt() 
+0

Um ... Bạn có nghĩa là chậm hơn? –

+0

chạy lần đầu tiên mất 78 ms sau đó thứ hai là 76,8 ms sau đó thứ ba là 24 ms nhanh hơn và nhanh hơn nhưng ofcourse Leo của CPU nhanh hơn –

+0

Đó là sqrt được xây dựng trong Java, nhưng OP đang nói về nghịch đảo nhanh. Nhưng một lần nữa, nghịch đảo nhanh của bạn thực hiện khá eerily. –