벡터 정규화 속도를 높이기 위해 java에 Fast Inverse Square Root을 구현하려고합니다. 그러나 Java에서 단 정밀도 버전을 구현하면 처음에는 1F/(float)Math.sqrt()
과 비슷한 속도를 얻은 다음 속도가 절반으로 떨어집니다. 이것은 흥미 롭습니다. Math.sqrt가 원시 메소드를 사용하는 반면에, 이것은 들었던 부동 소수점 나누기를 포함하기 때문에 실제로 느립니다. 다음과 같이 숫자를 계산하는 내 코드는 다음과 같습니다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;
}
을 나는 각각 1600 만 다음 번 집계 결과를 반복하고, 반복 작성한 짧은 프로그램을 사용하여, 나는 결과는 다음과 같이 얻을 :
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()
나는 둘 다 똑같은 속도의 숫자를 일관되게 얻고, Fast Inverse Square Root는 1F/Math.sqrt()
이 요구하는 시간의 약 60 %를 저장하고, Fast Inverse Square Root는 약 2 배의 반복을 거친다. 컨트롤로 실행합니다. 나는 FISR이 왜 똑같이 -> 60 % 더 빠르 -> 100 % 더 느리게 갈 것인지 혼란스럽고, 나는 프로그램을 실행할 때마다 일어난다.
EDIT : 위의 데이터는 일식으로 실행 한 것입니다.
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 : 나는 javac/java
으로 프로그램을 실행하면 나는 완전히 다른 데이터를 얻을 수 몇 반응 후에는 속도가 몇 번 반복 한 후 안정 보이지만, 수, 그것은에 매우 휘발성 안정화시킨다. 누구나 왜 그런 생각이 있니?
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 구현에서 보았던 모든 이익을 날려 버릴만큼 충분한 오버 헤드를 추가하십시오.) 같은 세션 내에서 실행 속도가 항상 * 증가합니까? – user2246674
내 로컬에서 fastinverse는 처음에는 더 느리지 만 나중에 실행하면 훨씬 빠릅니다. 아마도 JIT는 뭔가를하고있을 것입니다. –
처음부터 빨리 시작하려면 어셈블리에서이 작업을 수행해야합니다. 시간이 흐를수록 컴파일러가 빨리 결정할 수 있습니다. –