mutsu と nqp を多用したライブラリ

https://github.com/timo/json_fast

JSON::Fast は rakudo の toolchain でも採用されているライブラリであって、Raku においては重要な意味をもつモジュールだ。 このライブラリは nqp 命令を多様することにより、素の raku に比べて極めて高速化されている。

https://tokuhirom.github.io/mutsu/bench-trend.html

通常の micro benchmark では概ね mutsu のほうが高速になっているのが実態である。これは、Cranelift を用いた JIT などの効果が大きい。

一方で、nqp 命令を多様するケースのみ劣後している。

たとえば、JSON::Fast では以下のようなスタイルでコードが書かれている。

    sub str-escape(\text) {
        my $codes := text.NFD;
        my int $i = -1;

        nqp::while(
          nqp::islt_i(++$i,nqp::elems($codes)),
          nqp::if(
            nqp::isle_i((my int $code = nqp::atpos_i($codes,$i)),92)
              || nqp::isge_i($code,128),
            nqp::if(                                           # not ascii
              nqp::isle_i($code,31),
              nqp::if(                                          # control
                nqp::iseq_i($code,10),
                nqp::splice($codes,$lf,$i++,1),                  # \n
                nqp::if(
                  nqp::iseq_i($code,13),
                  nqp::splice($codes,$cr,$i++,1),                 # \r
                  nqp::if(
                    nqp::iseq_i($code,9),
                    nqp::splice($codes,$tab,$i++,1),               # \t
                    nqp::stmts(                                    # other control
                      nqp::splice($codes,$code.fmt(Q/\u%04x/).NFD,$i,1),
                      ($i = nqp::add_i($i,5))
                    )
                  )
                )
              ),
              nqp::if(                                          # not control
                nqp::iseq_i($code,34),
                nqp::splice($codes,$qq,$i++,1),                  # "
                nqp::if(
                  nqp::iseq_i($code,92),
                  nqp::splice($codes,$bs,$i++,1),                 # \
                  nqp::if(
                    nqp::isge_i($code,0x10000),
                    nqp::stmts(                                    # surrogates
                      nqp::splice(
                        $codes,
                        (my $surrogate := to-surrogate-pair($code.chr).NFD),
                        $i,
                        1
                      ),
                      ($i = nqp::sub_i(nqp::add_i($i,nqp::elems($surrogate)),1))
                    )
                  )
                )
              )
            )
          )
        );

        nqp::strfromcodes($codes)
    }

これはなんというか、MoarVM の命令を手書きしているに等しい。add_i などの命令は、integer の加算命令として MoarVM の IR を直接よぶコードにコンパイルされる。 一方で、mutsu ではこれを関数コールとして実装していた。

それではあまりに差が大きすぎるので、nqp:: の命令を、直接 Ir として扱うような最適化を入れざるをえないかなぁ、と思っている。

Published: 2026-09-20(Sun) 06:28