性能测试独立于迭代次数

试图回答这张票:instanceof和Class.isAssignableFrom(...)有什么区别?

我做了一个性能测试:

class A{}
class B extends A{}

A b = new B();

void execute(){
  boolean test = A.class.isAssignableFrom(b.getClass());
  // boolean test = A.class.isInstance(b);
  // boolean test = b instanceof A;
}

@Test
public void testPerf() {
  // Warmup the code
  for (int i = 0; i < 100; ++i)
    execute();

  // Time it
  int count = 100000;
  final long start = System.nanoTime();
  for(int i=0; i<count; i++){
     execute();
  }
  final long elapsed = System.nanoTime() - start;
System.out.println(count+" iterations took " + TimeUnit.NANOSECONDS.toMillis(elapsed) + "ms.);
}

哪给了我:

  • A.class.isAssignableFrom(b.getClass()) :100000次迭代需要15ms
  • A.class.isInstance(b) :100000次迭代耗时12ms
  • b instanceof A :100000次迭代耗时6ms
  • 但是用迭代的次数来看,我可以看到性能是不变的。 对于Integer.MAX_VALUE:

  • A.class.isAssignableFrom(b.getClass()) :2147483647迭代耗时15ms
  • A.class.isInstance(b) :2147483647迭代需要12ms
  • b instanceof A :2147483647迭代需要6ms
  • 认为这是一个编译器优化(我用JUnit运行这个测试),我将它改为:

    @Test
    public void testPerf() {
        boolean test = false;
    
        // Warmup the code
        for (int i = 0; i < 100; ++i)
            test |= b instanceof A;
    
        // Time it
        int count = Integer.MAX_VALUE;
        final long start = System.nanoTime();
        for(int i=0; i<count; i++){
            test |= b instanceof A;
        }
        final long elapsed = System.nanoTime() - start;
        System.out.println(count+" iterations took " + TimeUnit.NANOSECONDS.toMillis(elapsed) + "ms. AVG= " + TimeUnit.NANOSECONDS.toMillis(elapsed/count));
    
        System.out.println(test);
    }
    

    但是性能仍然与迭代次数“独立”。 有人可以解释这种行为吗?


    JIT编译器可以消除不起任何作用的循环。 这可以在10,000次迭代后触发。

    我怀疑你的时机是JIT需要多长时间才能检测到循环没有做任何事情并将其删除。 这将比做10,000次迭代需要的时间长一点。


  • 一百次迭代对于预热来说还远远不够。 默认的编译阈值是10000次迭代(百倍以上),所以最好至少稍微超过该阈值。
  • 编辑一旦触发,世界就不会停止; 汇编发生在后台。 这意味着它的效果只有稍微延迟后才能开始观察。
  • 有足够的空间来优化您的测试,以便整个循环被折叠为最终结果。 这将解释不变的数字。
  • 无论如何,我总是通过外部方法调用内部方法来做基准测试,比如10次。 内部方法根据需要进行大量的迭代,比如10,000或更多,以使其运行时间上升至至少数十毫秒。 我甚至不打扰nanoTime因为如果微秒精度对你很重要,那只是测量太短时间间隔的标志。

    当你这样做时,你可以很容易地让JIT在代替解释版本后执行内部方法的编译版本。 另一个好处是你可以确保内部方法的时间稳定。


    如果你想为一个简单的函数做一个真正的基准,你应该使用一个微型基准测试工具,比如Caliper。 试图制定自己的基准将会简单得多。

    链接地址: http://www.djcxy.com/p/76371.html

    上一篇: Performance test independent of the number of iterations

    下一篇: Detecting if a Javascript variable is really undefined