jbangを使って複雑なビルドプロセスをなしで jmh/jcstress を実行する

出落ちで申し訳ないけど jbangで、Javaのベンチマークツールであるjmhを実行出来る記事があるのでそれのリンクを貼っておく。 しかし、後述の話に由来するのだが、以下の記事のprofilerの実行はあまり使わないほうが良いと思われる。 jbangによって、jmhが実行するのが簡単になったことに着目してほしい。

Benchmarks, Async profiler and JBang - @maxandersen

従来のjmhの実行方法としては、mavenプロジェクトを用意して、コンパイルして、uberjarを作って実行する、みたいなのが多かったが jbangを使うことで、mavenプロジェクトもuberjarも必要ない。pom.xmlともおさらばできる。.java ファイルだけで良いのは最高だ!

これだけで終わってしまうとアレなので、以下の話を少しする。 jmh標準のprofile機能の話とjcstressの話を書いておくことにする。

  • jmhには 標準でprofilerの機能がついている
  • jmhの標準のprofilerで async-profilerを使ってみる
  • jcstress とは
  • jbangで jcstressを実行してみる

jmhには 標準でprofilerの機能がついている

jmhには標準で様々なツール依存・非依存のprofilingが可能である。 jmhの起動時に -lprof を実行すると、以下のprofilerの一覧が確認できる。

  • cl
  • comp
  • gc
  • jfr
  • pauses
  • perfc2c
  • safepoints
  • stack
  • async
  • perf
  • perfasm
  • perfnorm
  • xperfasm

実際に -lprof を付与して実行するとこんな出力が出る。実行環境や引数によっては使えないprofilerについても表示してくれる。 Apple SiliconのMacOSで実行したので、環境によって出力が変わる可能性があることを付記しておく。

jbang TestBenchmark.java -lprof
Supported profilers:
          cl: Classloader profiling via standard MBeans 
        comp: JIT compiler profiling via standard MBeans 
          gc: GC profiling via standard MBeans 
         jfr: Java Flight Recorder profiler 
      pauses: Pauses profiler 
     perfc2c: Linux perf c2c profiler 
  safepoints: Safepoints profiler 
       stack: Simple and naive Java stack profiler 

Unsupported profilers:
       async: <none> 
Unable to load async-profiler. Ensure asyncProfiler library is on LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (Mac OS), or -Djava.library.path. Alternatively, point to explicit library location with -prof async:libPath=<path>.
   dtraceasm: <none> 
[sudo: a password is required
]
        perf: <none> 
[Cannot run program "perf": error=2, No such file or directory]
     perfasm: <none> 
[Cannot run program "perf": error=2, No such file or directory]
    perfnorm: <none> 
[Cannot run program "perf": error=2, No such file or directory]
    xperfasm: <none> 
Unable to start the profiler. Try running JMH as Administrator, and ensure that previous profiling session is stopped. Use 'xperf -stop' to stop the active profiling session.: [Cannot run program "xperf": error=2, No such file or directory]

jmhの標準のprofilerで async-profilerを使ってみる

自分が普段お世話になっているのは、async-profilerなので、async-profilerを試してみる。 以下のコマンドで、ベンチマークごとに、flamegraphが出力される。

jbang TestBenchmark.java -prof async:libPath=$PWD/lib/libasyncProfiler.dylib\;output=flamegraph\;dir=profile-results

なお、一番最初に紹介したの記事の中では、javaagentでasync-profilerを適用しているようだが、jmhの実行全体のprofileが取られてそうだった。 そのため、jmhでベンチマークのprofileを取りたくなったら、jmh標準の機能を使うのが良いと思われる。 実際に試したテストケースが悪い可能性はあるが、全ベンチマークの実行全体のプロファイルは別にほしくない。 jmh標準の機能では、ベンチマークごとのprofileが取得できる。これがほしかった。

jcstress とは

こちらはベンチマークのためのツールではなく、JVMやクラスライブラリ及びハードウェアによる並行性の問題を調査することを目的としたテストハーネスとテストスイートのようだ。 jcstressを使えば並行処理でrace conditionが発生しないかどうかを検査することが可能になるが、よっぽど特殊な事情がない限り使わないと思う。

アプリケーションを書くうえで、そこまで頑健にrace conditionの検査をする必要性がそこまで発生しないことが多いと思うのと jcstressを使わないと調査出来ないわけではないからだ。

まぁ気が向いたら触ってみて、並行性で疑わしく再現が難しい問題に直面したときにシュッと出せるツールとして使いたい。

jbangで jcstressを実行してみる

というわけで実行してみる。

以下のソースコードを用意する。 jcstress-samples から持ってきたファイルに jbangのコメントヘッダを付与して、依存解決とJavaのバージョンを指定している。

//JAVA 19
//DEPS org.openjdk.jcstress:jcstress-core:0.16

package com.github.wreulicke.test;
// 追記したのはここから↑

/*
 * Copyright (c) 2021, Red Hat, Inc. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
import org.openjdk.jcstress.annotations.Actor;
import org.openjdk.jcstress.annotations.JCStressTest;
import org.openjdk.jcstress.annotations.Outcome;
import org.openjdk.jcstress.annotations.State;
import org.openjdk.jcstress.infra.results.L_Result;

import static org.openjdk.jcstress.annotations.Expect.*;

@JCStressTest
@Outcome(id = "null",                   expect = ACCEPTABLE, desc = "Not seeing the object yet")
@Outcome(id = "class java.lang.Object", expect = ACCEPTABLE, desc = "Seeing the object, valid class")
@Outcome(                               expect = FORBIDDEN,  desc = "Other cases are illegal")
@State
public class TestJcstress {

  Object o;

  @Actor
  public void writer() {
      o = new Object();
  }

  @Actor
  public void reader(L_Result r) {
      Object lo = o;
      if (lo != null) {
          try {
              r.r1 = lo.getClass();
          } catch (NullPointerException npe) {
              r.r1 = npe;
          }
      } else {
          r.r1 = null;
      }
  }

    public static void main(String[] args) throws Exception {
        org.openjdk.jcstress.Main.main(args);
    }
}

実行方法は以下の通りだ。

jbang TestJcstress.java

なんか色々出力が出て、こんなhtmlが出力される。

以前触った記憶だと、並行性の問題で壊れたりすると、表示が変わった記憶があるが jbang を通した実行方法のみ整理しておきたいので、この記事では取り扱わない。

↑の画面から、テストのクラスをクリックすると、以下のような表示があり、compilation modeで網羅的に検査してくれていることが分かる。便利。

まとめ

この記事で、jbangを使って jmh/jcstressを実行する方法を紹介した。jmhは外部の記事のリンクを貼っただけだが。 また、jmh標準のprofile機能の紹介と、jcstressの紹介およびjbangを通した実行方法を紹介した。

jbangは、存在は知っていたのだが、標準でJavaファイルをスクリプト的に実行出来るようになってしまったので あまり興味が沸かず使ったことがなかった。 しかし、最初のリンクの記事を見ると、jbangでは、依存関係をファイルに記述することができ、groovyのGrapeのような仕組みが実装されていることに気付き、これは便利だなぁと思っている。

好きあらばどこかで使いたいが、JavaScriptとかGoで良い説がある。 終わり。