JMH 迭代次数从 >300,000 ops/sec 到 < 1 op/sec

JMH iterations go from >300,000 ops/sec to < 1 op/sec

我的mainclass包含多线程要使用的数据结构。在我原来的程序中,我也是在这里启动线程。

public class JDKSolution {

public static final ConcurrentHashMap<Long, Book> books = new ConcurrentHashMap<>();
public static void main(String[] args) throws RunnerException{
    Options opt = new OptionsBuilder()
        .include(JDKSolutionThread.class.getSimpleName())
        .forks(1)
        .jvmArgsAppend("-XX:MaxInlineLevel=12")
        .verbosity(VerboseMode.EXTRA)
        .build();

    new Runner(opt).run();              
}
}

书简直了

public class Book {
  static long count  = 0;
  long id;
  Book(){
    synchronized(this){
      id = count;
      count++;
    }
  }

  long getID(){
    return id;
  }
}

我有另一个扩展线程的 class;这里是要分析的方法。

@State(Scope.Benchmark)
public class JDKSolutionThread extends Thread{

@Benchmark 
    public void addBook(){
        Book b = new Book();
        books.put(b.getID(), b);
    }
}

输出看起来像这样

# Warmup Iteration  15: 329731.671 ops/s
# Warmup Iteration  16: 23160.167 ops/s
# Warmup Iteration  17: 55316.913 ops/s
# Warmup Iteration  18: 30454.814 ops/s
# Warmup Iteration  19: 0.316 ops/s
# Warmup Iteration  20: 0.880 ops/s
Iteration   1: 0.085 ops/s
Iteration   2: 1.349 ops/s
...
Iteration  20: 4.508 ops/s
<JMH had finished, but forked VM did not exit, are there stray running threads? Waiting 24 seconds more...>
Thread[DestroyJavaVM,5,main]
  at java.lang.Object.wait(Native Method)
  at java.lang.Thread.join(Thread.java:1245)
  at java.lang.Thread.join(Thread.java:1319)
  at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:106)
  at java.lang.ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:46)
  at java.lang.Shutdown.runHooks(Shutdown.java:123)
  at java.lang.Shutdown.sequence(Shutdown.java:167)
  at java.lang.Shutdown.shutdown(Shutdown.java:234)

Thread[Thread-0,5,main]
  at java.lang.StringCoding.deref(StringCoding.java:63)
  at java.lang.StringCoding.encode(StringCoding.java:330)
  at java.lang.String.getBytes(String.java:918)
  at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
  at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
  at java.io.File.exists(File.java:819)
  at sun.misc.URLClassPath$FileLoader.getResource(URLClassPath.java:1245)
  at sun.misc.URLClassPath.getResource(URLClassPath.java:212)
  at java.net.URLClassLoader.run(URLClassLoader.java:365)
  at java.net.URLClassLoader.run(URLClassLoader.java:362)
  at java.security.AccessController.doPrivileged(Native Method)
  at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
  at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
  at org.openjdk.jmh.runner.link.BinaryLinkClient.close(BinaryLinkClient.java:141)
  at org.openjdk.jmh.runner.ForkedMain.hangup(ForkedMain.java:136)
  at org.openjdk.jmh.runner.ForkedMain$HangupThread.run(ForkedMain.java:157)

<shutdown timeout of 30 seconds expired, forcing forked VM to exit>

每秒的操作应该在每次迭代 ~300000 的范围内。什么会导致少至 1 次操作?

在大多数基准测试模式中,JMH 一遍又一遍地调用 @Benchmark

假设 books 是一个集合,实际发生的是使集合饱和,并且每个 put 调用都会添加到越来越大的集合中。您必须在达到某个大小后清除集合,或者在 put 之后删除其他一些 Book 以保持恒定大小,或者将集合模拟为 "skip" 添加等。所有这些措施会对实验设计和您从实验中获得的数据产生影响,因此您有责任选择并面对后果。