🧊

`oxc-parser`の`experimentalRawTransfer`オプションについて

名前のとおりまだexperimentalではあるけど、これは紹介せずにはいられない!というわけで。

import { parseSync } from "oxc-parser";

const CODE = `
  // ...
`;

const ret = parseSync("test.js", CODE, {
  preserveParens: false, // default `true`, for ESTree compat
  experimentalRawTransfer: true, // 👈🏻
});

Rustベースのツールの課題

XXXをRustで書き換えたら実行速度が劇的に改善された!って話は、巷にいろいろある。

速さは正義であり、それはそれでよい話。少なくとも、それだけで完結しているうちは。

問題は、そのツールを既存のJSエコシステムと組み合わせたいってなった場合にどうするか。 たとえば、Linterのプラグインを自作したいだとか、ESTreeのASTを利用する既存のツールで再利用したいとか。

こうなると問題になるのは、RustでパースしたASTを、JS側にどうやって送る?ってこと。

一番わかりやすい解としては、serdeなんかでJSONにフォーマットして、JS側でJSON.parse()するってのがある。

けど、そうなるとこの変換コストがかかって普通に遅い。JSで全部書いたほうが速いってなる。

さて、どうしたものか。

OXC以外のツールでは

Biomeでは、LinterのプラグインをGritQLで書けるようデザインしてる。

RFC: Biome Plugins Proposal · biomejs/biome · Discussion #1762 https://github.com/biomejs/biome/discussions/1762

JS/TSでプラグインを書ける道も、まだ諦めたわけではないようではあるけど。

Denoでは、もう既にJS/TSでLinterプラグインが書けるらしい。

Deno 2.2: OpenTelemetry, Lint Plugins, node:sqlite https://deno.com/blog/v2.2#javascript-plugin-api

Rust-JS間の問題に対しては、ASTのフォーマットを工夫してるとのこと。

Speeding up the JavaScript ecosystem - Rust and JavaScript Plugins https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/

簡単にまとめると、

  • ASTのデータ構造を、ツリーではなくフラットにして
  • 冗長な文字列もマップで管理して、後から参照するように
  • なおかつ、NodeFacadeを用意してgetterで遅延で評価するように

という、効率よく変換するという感じのアプローチ。

OXCでは

子曰く、「Rust-JS間でのフォーマット変換がボトルネックなのであれば、そもそも変換しなければいい」。

え、どういうこと?そんなんできるん?ってなるけど、それをやってのけちゃうのがこの人たち・・・。

feat(ast/estree): raw transfer (experimental) by overlookmotel · Pull Request #9516 · oxc-project/oxc https://github.com/oxc-project/oxc/pull/9516

せっかくブログを書くのなら、具体的にこういう処理をやってそれを実現してる!ってことを本当は書きたかったけど、高度すぎて正直よくわからんかったです。

こっちの元Issueにも詳しい説明があります。

Faster passing ASTs from Rust to JS · Issue #2409 · oxc-project/oxc https://github.com/oxc-project/oxc/issues/2409

ざっくりまとめると、

  • OXCではArenaアロケーターですべてのメモリを管理してる
    • だから速い
  • そしてこのメモリを、napi-rsを通してそのままJS側に持っていき、(Array)Bufferから触れるようにした
    • これはメモリのポインタだけのやり取り
  • メモリレイアウト上のどこに欲しいものがあるか?は、JS側で解決する
    • これはスキーマで事前に決められてるので、マッピングするだけ
  • やろうと思えば、その逆方向の機構も作れる

なるほど、わからん。

興味を持った人向けに、リポジトリのどこにコードがあるかの取っ掛かりだけ置いておきます。

という感じ。

氏は過去にSWCにもcontributeしてたことがあって、その頃からの構想だったらしく、ドラマティックな展開だわ。

おわりに

厳密にベンチしたわけではないので明言は避けるけど、手元の数値だと、より大きいファイルでその速度差を実感できる感じやったし、小さいファイルでもJSと遜色ないくらい。

ちゃんとベンチした結果とかも、そのうち公開されるかな?乞うご期待。

JSだけでなくJSXも(ASTの形はさておきTSも)パースができて、しかも遅くないっていうのが偉いし、Acornのアウトプットと比較したテストにも100%通ったESTreeになっててさらに偉い。

本当にすごい!偉業!