哪个更快:
private static boolean isFinite(float x) {
return !(x != x || x == Float.POSITIVE_INFINITY || x == Float.NEGATIVE_INFINITY);
}
或
private static boolean isFinite(float x) {
return Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY;
}
?
我尝试了一些微基准测试,但结果似乎很可疑。
最佳答案
我希望第二个更快,因为它进行的比较较少。然而差异非常小,计时结果将很大程度上取决于您的基准测试方式。
我会选择您认为最清晰、最简单的方法,并且 JVM 可能会对此进行最佳优化。
编辑:微基准测试的问题在于测试方式会影响结果。
private static boolean isFinite1(float x) {
return Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY;
}
private static boolean isFinite2(float x) {
return !(x != x || x == Float.POSITIVE_INFINITY || x == Float.NEGATIVE_INFINITY);
}
public static void main(String[] args) {
int nums = 10000;
int runs = 10000;
float[] floats = new float[nums];
for (int i = 0; i < nums; i++) {
double d = Math.random();
floats[i] = d < 0.01 ? Float.NaN :
d < 0.02 ? Float.NEGATIVE_INFINITY :
d < 0.03 ? Float.POSITIVE_INFINITY : (float) d;
}
for (int n = 0; n < 10; n++) {
{
int count1 = 0, count2 = 0;
long timeA = System.nanoTime();
for (int i = 0; i < runs; i++)
for (float f : floats)
if (isFinite1(f)) count1++;
long timeB = System.nanoTime();
for (int i = 0; i < runs; i++)
for (float f : floats)
if (isFinite2(f)) count2++;
long timeC = System.nanoTime();
long total1 = timeB - timeA;
long total2 = timeC - timeB;
assert count1 == count2;
System.out.printf("1,2: isFinite1 took %.1f ns and isFinite2 took %.1f ns on average%n", (double) total1 / runs / nums, (double) total2 / runs / nums);
}
{
int count1 = 0, count2 = 0;
long timeA = System.nanoTime();
for (int i = 0; i < runs; i++)
for (float f : floats)
if (isFinite2(f)) count1++;
long timeB = System.nanoTime();
for (int i = 0; i < runs; i++)
for (float f : floats)
if (isFinite1(f)) count2++;
long timeC = System.nanoTime();
long total1 = timeB - timeA;
long total2 = timeC - timeB;
assert count1 == count2;
System.out.printf("2,1: isFinite1 took %.1f ns and isFinite2 took %.1f ns on average%n", (double) total1 / runs / nums, (double) total2 / runs / nums);
}
}
}
打印
1,2: isFinite1 took 1.5 ns and isFinite2 took 5.1 ns on average
2,1: isFinite1 took 3.6 ns and isFinite2 took 4.4 ns on average
1,2: isFinite1 took 1.5 ns and isFinite2 took 5.1 ns on average
2,1: isFinite1 took 3.6 ns and isFinite2 took 4.4 ns on average
1,2: isFinite1 took 1.5 ns and isFinite2 took 5.2 ns on average
2,1: isFinite1 took 3.6 ns and isFinite2 took 4.4 ns on average
正如您所看到的,即使我测试这些的顺序也会产生很大的差异。
比所涉及的操作更重要的是优化分支数量以及分支预测的工作效果。 http://www.agner.org/optimize/microarchitecture.pdf
假设我将不同值的可能性提高了 25 倍,因此每个范围的可能性相同。
floats[i] = d < 0.25 ? Float.NaN :
d < 0.5 ? Float.NEGATIVE_INFINITY :
d < 0.75 ? Float.POSITIVE_INFINITY : (float) d;
这一切都增加了代码通过不同路径的机会。
1,2: isFinite1 took 8.5 ns and isFinite2 took 14.2 ns on average
2,1: isFinite1 took 10.9 ns and isFinite2 took 11.5 ns on average
1,2: isFinite1 took 7.2 ns and isFinite2 took 14.4 ns on average
2,1: isFinite1 took 11.0 ns and isFinite2 took 11.5 ns on average
1,2: isFinite1 took 7.3 ns and isFinite2 took 14.2 ns on average
2,1: isFinite1 took 10.8 ns and isFinite2 took 11.5 ns on average
我再说一遍,更清晰的代码应该是您的目标! ;)
关于java浮点有限性检查性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5744044/