JDK 21のswitch式でpattern match書いたら10倍以上遅い件

switch式の結果javapしたら https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/runtime/SwitchBootstraps.html java.lang.runtime.SwitchBootstraps と tableswitch が使われることに気がついたが、これ巨大なswitch式をJDK 21以降で書いた場合、同等の巨大なmatch式をScalaで書くよりも速度が速い可能性があるのでは???

というのをbenchmarkしてみた話。

https://github.com/xuwei-k/java-switch-benchmark/commit/654c60e309d5a16ef2f753778b21995f57940567

完全なコードはgithub見てもらうとして、簡単に書いておくと、

sealedの子供のrecordが100個存在する状態を用意

(Scala側もJavaのrecordそのまま使う。Scalaは別途case class用意でもよかったが、おそらくそこの差は出ないと思ったし、この方がある意味では条件が対等なため)

public sealed interface A{
  record A0(int x) implements A{}
  record A1(int x) implements A{}
  record A2(int x) implements A{}
  record A3(int x) implements A{}
  // 同様に合計100個

Javaのif else部分でのパターンマッチ

 public static int f(A x) {
    if (x instanceof A.A0 a) {
      return a.x();
    } else if (x instanceof A.A1 a) {
      return a.x();
    } else if (x instanceof A.A2 a) {
      return a.x();
    // 以下略

実質Java 21から?のswitch式での書き方

  public static int f(A x) {
    return switch(x) {
      case A.A0  a -> a.x();
      case A.A1  a -> a.x();
      case A.A2  a -> a.x();
    // 以下略

Scalaでのmatch

 def f(x: A): Int = x match {
    case a: A.A0  => a.x
    case a: A.A1  => a.x
    case a: A.A2  => a.x

の3種類で、どれが速いでしょう?というのですが、(github actions上での)結果が以下

[info] Benchmark          Mode  Cnt      Score     Error  Units
[info] Bench.javaIfElse  thrpt   10  24243.021 ± 281.926  ops/s
[info] Bench.javaSwitch  thrpt   10   1380.937 ±  19.946  ops/s
[info] Bench.scalaMatch  thrpt   10  19374.291 ± 422.856  ops/s

ops/s は「単位時間(秒)あたり、何回実行できたか?」という意味なので、数が多いほど性能がいい、という指標のはずです。 Scalaのmatchとjavaのif elseは、せいぜい2〜3割の違いがあり、この違いはどこからくるのか?は別途気になるところですが、なんとJavaのswitchが10倍以上遅いです。 Javaのif elseと比較して17倍、Scalaと比較して14倍遅いです。 当たり前ですが、ローカルでやっても傾向は大体同じでした。

これは、SwitchBootstraps使って頑張っているのが裏目に出ている気がするのですが、お客様の中でこのあたりに詳しい方・・・誰か・・・。 あるいはbenchmarkの方法が悪いというか、逆にswitchの方が速くなる場合あるのでしょうか? あるいはSwitchBootstrapsが速度のため(だけ)ではない何か、とか・・・?

少し前に(JDK 21に入ってる)、頑張って最適化してる様子は確認できましたが、現在で速度を問題としてるissueなどはかるく探しても見つからなかったが・・・

追記:

以下のような情報もらった。JVM内部に何かありそうだなぁ・・・。 それにしても、30や40でも、if elseと比較して、少しだけ速い?けど、少ししか速くないのも微妙・・・

さらに追記:

xuwei-k.hatenablog.com