chapter12_并发程序的测试_3_避免性能测试的陷阱

  • 垃圾回收

    (1) 垃圾回收的时序无法预测

    所以会导致有时测出的执行时间由于GC的影响而导致不准

    (2) 应对GC造成的测量时间误差的策略

    1° 确保GC操作在测试期间不会运行

    用-verbose查看JVM运行时是否GC过

    2° 确保GC操作在测试期间执行过多次

    这种方式比较靠谱, 因为它比较像真实的运行环境

  • 动态编译

    (1) JIT技术导致测试的时间既有可能是解释执行的时间, 也有可能是编译执行的时间, 还有可能是两种混合

    (2) 测量采用解释执行方式执行的代码速度没有意义, 因为服务端程序运行足够长时间后, 频繁被调用的代码都会被编译

    (3) 应用动态编译对测量结果造成误差的策略

    让程序运行足够长时间后再测量

  • 对代码路径的不真实采样

    (1) 激进的编译器优化会导致同样的一个方法, 在一种运行条件下生成了一种运行方式, 另一种运行条件下生成了另一种运行方式

    例如只在单线程中使用的方法和在多线程中使用的方法的实际运行方式是不一样的

    (2) 解决办法

    尽可能覆盖在该应用程序中将执行的代码路径集合

  • 不真实的竞争程度

    (1) 实际运行时, 可能多个线程都是计算密集型任务, 因此几乎不存在对锁的竞争; 而测试时让多个线程执行的简单任务, 执行的时间较短, 所以竞争很明显, 从而得到了错误的结论

  • 无用代码的消除

    (1) 优化编译可以找出并消除那些对结果不产生影响的无用代码

    基准测试代码常常就是那些无用代码, 因此很有可能没测试到。。。

    (2) 解决办法: 让测试部分的结果被显式的使用到

    但是不能引入IO操作, 这样测的结果也不准;

    一种简单的操作是:

    计算某个对象的散列值, 并且与一个任意值进行比较作为if条件, 成功则输出空字符

    示例

      if (myObject.x.hashCode == System.nanoTime()) {
          System.out.print(" ");
      }
    

    这样的操作一来很少能撞上, 所以不会对程序产生什么影响; 二来真的是在利用结果, 测试代码不会被当成无用代码被优化掉; 三来print方法会把输出缓存起来, 直到调用println才真正执行输出, 所以不会影响结果

  • 结果的不可预测

    (1) 当输入的测试数据总是有规律的数据时, 智能的编译器会将结果缓存而不再运行实际的测试

    (2) 解决办法

    输入的测试数据使用随机数

你可能感兴趣的:(chapter12_并发程序的测试_3_避免性能测试的陷阱)