卡尺基准测试由于JIT编译而失败

4

我得到了一个类似于以下的卡尺基准测试:

public Course timeCourseCreation( int i ) {

  return createCourse( i );
}

public Course createCourse( int t ) {

  Course course = new Course();

  for ( int i = 0 + t; i < this.students + t; i++ ) {
    Student student = new Student();
    student.setAge( ( int ) ( 100 * Math.random() ) + t );
    student.setName( UUID.randomUUID().toString() );
    course.getStudents().add( student );
  }

  for ( int i = 0 + t; i < this.courses + t; i++ ) {
    Topic topic = new Topic();
    topic.setDescription( UUID.randomUUID().toString() );
    topic.setDifficulty( ( int ) ( 10 * Math.random() ) + t );
    topic.setName( UUID.randomUUID().toString() );
    topic.setRating( ( int ) ( 10 * Math.random() ) + t );
    course.getTopics().add( topic );
  }

  return course;
}

所以,我有一个可以创建数据结构的方法(createCourse(...))并且我想测量创建此结构所需的时间。
然而,当我运行基准测试时,我会得到以下异常:
Failed to execute java -cp C:\Users\tuhrig\workspace\XmlVsJson\target\classes;C:\Users\tuhrig\.m2\repository\javax\xml\bind\jaxb-api\2.2.11\jaxb-api-2.2.11.jar;C:\Users\tuhrig\.m2\repository\com\google\code\gson\gson\2.2.4\gson-2.2.4.jar;C:\Users\tuhrig\.m2\repository\com\google\caliper\caliper\0.5-rc1\caliper-0.5-rc1.jar;C:\Users\tuhrig\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\Users\tuhrig\.m2\repository\com\google\guava\guava\11.0.1\guava-11.0.1.jar;C:\Users\tuhrig\.m2\repository\com\google\code\java-allocation-instrumenter\java-allocation-instrumenter\2.0\java-allocation-instrumenter-2.0.jar;C:\Users\tuhrig\.m2\repository\asm\asm\3.3.1\asm-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-analysis\3.3.1\asm-analysis-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-commons\3.3.1\asm-commons-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-tree\3.3.1\asm-tree-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-util\3.3.1\asm-util-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-xml\3.3.1\asm-xml-3.3.1.jar com.google.caliper.InProcessRunner --warmupMillis 3000 --runMillis 1000 --measurementType TIME --marker //ZxJ/ -Dbenchmark=CourseCreation de.tuhrig.Benchmark
starting Scenario{vm=java, trial=0, benchmark=CourseCreation}
[caliper] [starting warmup]
[caliper] [starting measured section]
[caliper] [done measured section]
[caliper] [starting measured section]
...
Error: Doing 2x as much work didn't take 2x as much time! Is the JIT optimizing away the body of your benchmark?
...
An exception was thrown from the benchmark code.
com.google.caliper.ConfigurationException: Failed to execute java -cp C:\Users\tuhrig\workspace\XmlVsJson\target\classes;C:\Users\tuhrig\.m2\repository\javax\xml\bind\jaxb-api\2.2.11\jaxb-api-2.2.11.jar;C:\Users\tuhrig\.m2\repository\com\google\code\gson\gson\2.2.4\gson-2.2.4.jar;C:\Users\tuhrig\.m2\repository\com\google\caliper\caliper\0.5-rc1\caliper-0.5-rc1.jar;C:\Users\tuhrig\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;C:\Users\tuhrig\.m2\repository\com\google\guava\guava\11.0.1\guava-11.0.1.jar;C:\Users\tuhrig\.m2\repository\com\google\code\java-allocation-instrumenter\java-allocation-instrumenter\2.0\java-allocation-instrumenter-2.0.jar;C:\Users\tuhrig\.m2\repository\asm\asm\3.3.1\asm-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-analysis\3.3.1\asm-analysis-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-commons\3.3.1\asm-commons-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-tree\3.3.1\asm-tree-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-util\3.3.1\asm-util-3.3.1.jar;C:\Users\tuhrig\.m2\repository\asm\asm-xml\3.3.1\asm-xml-3.3.1.jar com.google.caliper.InProcessRunner --warmupMillis 3000 --runMillis 1000 --measurementType TIME --marker //ZxJ/ -Dbenchmark=CourseCreation de.tuhrig.Benchmark
    at com.google.caliper.Runner.measure(Runner.java:309)
    at com.google.caliper.Runner.runScenario(Runner.java:229)
    at com.google.caliper.Runner.runOutOfProcess(Runner.java:378)
    at com.google.caliper.Runner.run(Runner.java:97)
    at com.google.caliper.Runner.main(Runner.java:423)
    at de.tuhrig.CaliperRunner.main(CaliperRunner.java:22)

该异常告诉我基准测试失败是因为JIT编译。那么,我应该如何在此处防止JIT编译?
我已经尝试使用随机值初始化数据结构(如您在代码中所见),我尝试在我的测试中返回一个值(如您在代码中所见),以及尝试使用course对象,例如调用course.getSize()。但是都不起作用。
3个回答

7
JIT 以可怕的方式干扰您的代码,使所有测量结果都变得不可靠。人们努力尝试让事情至少有些正确,但最终只能编写像 JMH 这样的东西。这是一个很棒的工具,可以帮助测量性能并获得有意义的结果。它甚至可以让您 避免死代码消除,并且有大量 示例,查看这些示例肯定会帮助您解决问题...

但这是错误的方式。通常会保密,但既然你已经自己找到了正确的方法,我就告诉你吧。你可以使用-Xint选项来禁用JIT。就是这样,不用谢我。


我将尝试使用“-Xint”选项并查看JMH。谢谢。 - Thomas Uhrig
3
@ThomasUhrig,请不要使用“-Xint”。以下是我阅读您的帖子的方式: 我需要测量风速。为了这么做,我决定拿一支狙击步枪,看看子弹离靶心有多远。 但我用了一把17世纪的火枪,在我试图使用它时,它打到了我的脚上。这是因为我的手指扣动了扳机。 我该如何切掉手指以便不再发生这种情况? - Evil Menace
当然我会试一下!看,我对我的代码实际运行速度并不感兴趣。它是否快速在我看来并不重要,无论是10毫秒、20毫秒还是100毫秒。我只想比较两种不同的算法,并知道哪个相对更快。也许“-Xint”选项不是为了砍掉我的手指,而是为了使用步枪上的锁来防止误伤自己。 - Thomas Uhrig
1
@ThomasUhrig,我认为两种不同算法在解释模式和完全JIT编译模式下的相对性能可能会有很大差异。我手头没有演示,但希望我可以得到一个。 - Evil Menace

3

您无法对依赖UUID生成的内容进行基准测试。从大多数操作系统中获取随机信息取决于操作系统状态、随机池的状态等。

我发现这会导致不可靠的行为。

首先要做的是摆脱它。


谢谢。我会重写我的代码,不使用UUID和Math.random()。我认为保持POJO稍微不同是一个好主意,因为我会在后续的基准测试中使用它们。 - Thomas Uhrig

0

你的基准测试并不是很有用:你主要测试了Math.random和UUID的速度。此外,它“只是”分配了一个PoJo(似乎是这样),除了可能会或可能不会受到GC的限制之外,你还能期望什么结果呢?

JIT不是你的问题,因为你想让你的代码加速。我不确定问题出在哪里,因为你返回创建的对象,JIT可能不会抑制代码(除非Caliper不可靠)。

我怀疑Math random或GC导致了不稳定的结果。你可以尝试使用JMH,但它不会使测量更有用。


我知道这个例子并不是很有用,但它只是一个示例。我的实际基准测试是针对一种数据结构上的不同算法进行的,该数据结构看起来类似于上面的“课程>学生&主题”示例。然而,我的基准测试一直失败,我发现这是由于初始数据结构的创建造成的,因此我将其分解为了这个例子。但你是对的,Math.random和UUID是这里最昂贵的部分。 - Thomas Uhrig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接