Alibaba Dragonwell JDK のコルーチン実装 Wisp2 の調査
- AlibabaはOpenJDKのディストリビュータ
- Dragonwell JDKという名前のJDKを開発、提供している
- Dragonwell JDKにはWispというコルーチン実装が組み込まれている
- Kotlin のコルーチンとは違って、JVMのスレッド実装を入れ替える構造になっている
- どのような特性があるのか計測した
環境
コンポーネント | 利用したバージョン |
---|---|
Docker Engine | Docker Desktop 3.4.0 (Windows 10, WSL based engine) |
Alibaba Dragonwell JDK | dragonwell-8.7.7_jdk8u292-ga |
Bellsoft Liberica JDK | jdk-8u292-bellsoft |
https://github.com/alibaba/dragonwell11 は Wisp2 に未対応だった
結果
それぞれの条件で5回ずつ計測。値は実行時間の平均値(ミリ秒)。
JDK | Wisp2無効(ActiveProcessorCount=1) | Wisp2無効(ActiveProcessorCount=4) | Wisp2有効(ActiveProcessorCount=1) | Wisp2有効(ActiveProcessorCount=4) |
---|---|---|---|---|
Dragonwell | 108259.4 | 80422.6 | 1054.2 | 45304.6 |
Liberica | 116458 | 70475.6 | n/a | n/a |
t(Wisp2無効:ActiveProcessorCount=1) > t(Wisp2無効:ActiveProcessorCount=4)
- Wisp2無効では、ActiveProcessorCount=4の実行時間は、ActiveProcessorCount=1より短い(75%くらい)
- 👍CPUコア数に応じて処理速度が速くなっている
t(Wisp2有効:ActiveProcessorCount=1) > t(Wisp2有効:ActiveProcessorCount=4)
- Wisp2有効では、ActiveProcessorCount=4の実行時間は、ActiveProcessorCount=1より大幅に長い(4300%くらい)
- 👎CPUコア数に応じて処理速度が遅くなっている
t(Wisp2無効:ActiveProcessorCount=1) > t(Wisp2有効:ActiveProcessorCount=1)
- ActiveProcessorCount=1では、Wisp2無効の実行時間は、Wisp2有効より大幅に長い(10271%くらい)
- 👍シングルコアCPUならとても処理速度が速くなっている
t(Wisp2無効:ActiveProcessorCount=4) > t(Wisp2有効:ActiveProcessorCount=4)
- ActiveProcessorCount=4では、Wisp2無効の実行時間は、Wisp2有効より長い(177%くらい)
- 🤔マルチコアCPUだとそんなに処理速度は速くならない(1.7倍速くなっているから十分かもしれない)
t(Dragonwell) == t(Liberica)
考察
- Wispの効果
- 実行環境がシングルコアCPUなら、処理速度の大幅な向上が見込める
- 実行環境がマルチコアCPUなら、逆にボトルネックになってしまう可能性がある
- コルーチンスケジューラがマルチコアCPUを想定していないのかもしれない
- デフォルトのスレッド実装
- CPUコア数に応じて処理速度は速くなる傾向がある
- ただしコア数に比例するわけではない
- CPUコア数に応じて処理速度は速くなる傾向がある
操作
実際に動かすコード。なんかピンポンする。
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; public class PingPong { static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); public static void main(String[] args) throws Exception { BlockingQueue<Byte> q1 = new LinkedBlockingQueue<>(), q2 = new LinkedBlockingQueue<>(); THREAD_POOL.submit(() -> pingpong(q2, q1)); // thread A Future<?> f = THREAD_POOL.submit(() -> pingpong(q1, q2)); // thread B q1.put((byte) 1); System.out.println(String.format("%s,%s,%s,%d", args[0], args[1], args[2], f.get())); } private static long pingpong(BlockingQueue<Byte> in, BlockingQueue<Byte> out) throws Exception { long start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000; i++) out.put(in.take()); return System.currentTimeMillis() - start; } }
Dockerfile はこれ。
FROM bellsoft/liberica-openjdk-alpine:8u292 RUN set -x \ && wget -O /tmp/jdk8.tar.gz https://github.com/alibaba/dragonwell8/releases/download/dragonwell-8.7.7_jdk8u292-ga/Alibaba_Dragonwell_8.7.7_x64_linux.tar.gz \ && mkdir -p /usr/lib/jvm/dragonwell8 \ && tar -xzf /tmp/jdk8.tar.gz --strip-components 1 -C /usr/lib/jvm/dragonwell8 \ && rm -rf /tmp/jdk8.tar.gz
計測スクリプト。結果は適当に編集する。
$ docker build -t test-wisp -f Dockerfile . # Git for Windows only $ export MSYS_NO_PATHCONV=1 $ for c in 1 4; do docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/dragonwell8 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/dragonwell8/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/dragonwell8/bin/java -XX:+UnlockExperimentalVMOptions -XX:-UseWisp2 -XX:ActiveProcessorCount=${c} -cp /tmp PingPong dragonwell8 noWisp2 ${c}; done" docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/dragonwell8 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/dragonwell8/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/dragonwell8/bin/java -XX:+UnlockExperimentalVMOptions -XX:+UseWisp2 -XX:ActiveProcessorCount=${c} -cp /tmp PingPong dragonwell8 useWisp2 ${c}; done" done $ for c in 1 4; do docker run --rm -it --volume ${PWD}:/app -e JAVA_HOME=/usr/ilb/jvm/jdk-8u292-bellsoft-x86_64 --cpus ${c} test-wisp sh -c "/usr/lib/jvm/jdk-8u292-bellsoft-x86_64/bin/javac -d /tmp /app/PingPong.java; for _ in 1 2 3 4 5; do /usr/lib/jvm/jdk-8u292-bellsoft-x86_64/bin/java -XX:+UnlockExperimentalVMOptions -XX:ActiveProcessorCount=${c} -cp /tmp PingPong jdk-8u292-bellsoft-x86_64 noWisp2 ${c}; done" done