Rust‑WASM パーサーを TypeScript に書き直した結果、処理速度が 3 倍に向上しました。

2026/03/21 6:48

Rust‑WASM パーサーを TypeScript に書き直した結果、処理速度が 3 倍に向上しました。

RSS: https://news.ycombinator.com/rss

要約

日本語訳:

(欠落した詳細を補完し、推測による提案を除外したもの)


要約

本研究では、openui‑lang パーサーの Rust でコンパイルされた WebAssembly (WASM) 実装と純粋な TypeScript バージョンをベンチマーク比較しています。

  • パイプライン – WASM パーサーは次の六段階を経て実行されます: autocloser → lexer → splitter → parser → resolver → mapper → ParseResult。
  • 相互運用オーバーヘッド – 各 WASM 呼び出しでは、入力文字列を WASM メモリにコピーし、
    serde_json::to_string()
    で Rust の結果を JSON にシリアライズし、その JSON を JavaScript に戻して V8 でパースする必要があります。
  • JsValue と JSON ラウンドトリップ
    serde-wasm-bindgen
    を使用して JsValue を直接返す方法は、単一の大きな JSON 転送よりも 30 % 遅く、多数の細かい境界横断が必要でした。1,000 回の実行でベンチマークした結果、JSON ラウンドトリップはすべてのフィクスチャ(simple‑table +9 %、contact‑form +29 %、dashboard +28 %)で直接 JsValue を上回りました。
  • 純粋 TypeScript のパフォーマンス – WASM 境界を除去した一度きりの TS パースは、すべてのフィクスチャで WASM バージョンより 2–3 倍速でした。
  • ストリーミングの複雑さ – 単純なストリーミング手法では、各 LLM チャンクごとに累積文字列全体を再パースし、O(N²) 時間が発生しました。すでにパース済みのステートメントをスキップする増分キャッシュを導入すると、これを O(N) に削減できました。20 文字チャンクの完全ストリームベンチマークでは、増分 TS パーサーは単純 TS パーサーより 2.6–3.3 倍速で、WASM と JSON ラウンドトリップを呼び出す場合と比べて 2.2–4.6 倍速でした。
  • 結論 – WASM は構造化テキストを JavaScript オブジェクトにパースする際に大きな境界オーバーヘッドが発生します。計算負荷が高く、相互運用が少ないワークロード(例: 画像/動画処理、暗号化)は、WASM に適したままであると結論付けられます。

このバージョンは主要なポイントをすべて保持し、推測による提案を除外し、ベンチマークの詳細を明確にしています。

本文

私たちがRustで書いた openui‑lang パーサを WASM にコンパイルした理由

ロジックはまったく正しかった。

  • Rust は高速
  • WASM ならブラウザ上でもほぼネイティブ速度
  • 我々のパーサはそれなりに複雑なマルチステージパイプライン

というわけで、Rust + WASM がベストだと考えていた。
しかし実際には「最適化すべきところ」を誤っていたのである。


パイプライン

ステージ説明
autocloser途中で途切れた文字列を、必要最低限の閉じ括弧/クォートで構文的に有効化する
lexer単一パスで文字を走査し型付きトークンを出力
splitterトークンストリームを
id = expression
という文単位へ切り分ける
parser再帰下降式パーサで AST を構築
resolver変数参照をすべて inlined(hoisting と循環参照検出付き)
mapper内部 AST を React レンダラーが消費する
OutputNode
フォーマットへ変換

パーサはストリーミングチャンクごとに呼び出されるため、レイテンシは極めて重要である。


WASM 境界オーバーヘッド

WASM パーサを呼び出すたびに発生する必須のオーバーヘッドは、Rust コード自体が高速であっても無視できないものだった。

JS 世界          | WASM 世界
───────────────────────────────────────
wasmParse(input)
  │
  ├─ JS ヒープ → WASM 線形メモリへ文字列コピー (割り当て + memcpy)
  │
  │                                 Rust が高速にパース ✓
  │                                 serde_json::to_string() ← 結果をシリアライズ
  │
  ├─ WASM → JS ヒープへ JSON 文字列コピー (割り当て + memcpy)
  │
  JSON.parse(jsonString)              ← 結果をデシリアライズ
  │
return ParseResult

実際に遅いのは Rust のパース 自体ではなく、境界処理である。
文字列を WASM にコピーし、結果を JSON 化して再び JS に渡し、V8 がその JSON を解析するという一連の操作がコストを押し上げていた。


「直接オブジェクト渡し」は逆に遅い

serde-wasm-bindgen
を使って Rust の構造体を
JsValue
として直接返す方法も試した。
しかし 30 % 遅く なった。

理由は、JS が WASM 線形メモリ上のバイト列をそのままネイティブオブジェクトとして解釈できないためである。
Rust のデータ構造(ポインタ、enum 判別子、アラインメントパディングなど)は JS 実行環境には完全に透明であり、

serde-wasm-bindgen
は「JS オブジェクトをフィールド単位で再構築」する必要がある。
そのため
parse()
呼び出しごとに多数の細かい変換が発生する。

JSON アプローチと比較すると:

  • serde_json::to_string()
    は純粋な Rust で実行され、境界を一度も越えない
  • 生成された文字列は1回だけコピーされ、V8 のネイティブ C++
    JSON.parse
    が単一の最適化済みパスで処理する

大きくて少数の操作 の方が多く細かい操作より勝る。

FixtureJSON ラウンドトリップ (µs)直接 JsValue (µs)
simple‑table20.5225.9 – 29 % 遅い
contact‑form61.4779 – 29 % 遅い
dashboard57.9774 – 28 % 遅い

この変更はすぐに取り消した。


TypeScript 実装への移行

パーサ全体を TypeScript に書き直した。

  • ステージ数は同じ(6)
  • ParseResult
    の形も同じ
  • WASM は使わず、V8 ヒープ内で完結

ベンチマーク手法:ワンショットパース

何を測定するか実施方法
単一
parse(completeString)
の呼び出し時間
JIT を安定させるために 30 回ウォームアップ、次に
performance.now()
(µs 精度)で 1000 回タイムを測定。中央値を報告
使用データ実際の LLM が生成したコンポーネントツリーを各フォーマットでストリーミング構文化
FixtureTypeScript (µs)WASM スピードアップ
simple‑table9.3×2
contact‑form13.4×1.5
dashboard19.4×1.0

WASM を排除したことで、呼び出しごとのコストは劇的に減少。
しかしストリーミングアーキテクチャ自体にはさらに深い非効率が残っていた。


ストリーミングの非効率

パーサは LLM の各チャンクごとに呼び出される。
単純な戦略では、チャンクを蓄積して文字列全体を再度ゼロからパースする:

Chunk 1: parse("root = Root([t")              → 14 chars
Chunk 2: parse("root = Root([tbl])\ntbl = T") → 27 chars
...

1000 文字の出力を 20 文字ずつ配信した場合、
50 回のパースで約 25 000 文字を処理し、計算量は O(N²)

修正:文単位増分キャッシュ

深さ 0 の改行で終端された文は不変(LLM は再度変更しない)とみなし、
完了した文の AST をキャッシュするストリーミングパーサを導入:

State: { buf, completedEnd, completedSyms, firstId }

push(chunk) で
  1. completedEnd 以降から深さ0改行をスキャン
  2. 完了文ごとに parse + cache AST → completedEnd を進める
  3. 残り(最後の不完全文): autoclose + fresh parse
  4. キャッシュ済み + 未完結文をマージして resolve + map → ParseResult
  • 完了した文は再パースされない
  • チャンクごとに 残り1文だけ を再解析する

これで計算量は O(総文字数) へ改善。

ベンチマーク手法:全ストリーム総パースコスト

何を測定するか実施方法
すべてのチャンク呼び出しにわたる累積パース時間文書を 20 文字ずつ再生し、各チャンクで
parse()
(ナイーブ)または
push()
(増分)を呼び出す。100 回再現し中央値を取る
Fixtureナイーブ TS (µs)増分 TS (µs)スピードアップ
simple‑table6977–(単一文のためキャッシュ効果なし)なし
contact‑form3161222.6×2.6
dashboard8402553×3.3

simple-table
は単一文なので両者は同じ。
文数が増えるほどキャッシュの恩恵は顕著に大きくなる。


発見事項まとめ

観点数値
呼び出しごとのコストTypeScript ≈ 9–19 µs, WASM + JSON ラウンドトリップ ≈ 20–61 µs
全ストリーム総コストTypeScript ナイーブ ≈ 69–8400 µs, 増分 ≈ 69–255 µs

WASM から TypeScript に切り替えたことで、呼び出しごとのパフォーマンスは 2.2〜4.6 倍向上。
さらにアルゴリズムを O(N²) → O(N) に改善した結果、全ストリームコストは 2.6〜3.3 倍低減。


学んだ教訓

  1. 実際に時間がかかっている場所をプロファイルする
    言語選択の前に、計算自体よりデータ転送(境界オーバーヘッド)がボトルネックになることが多い。

  2. serde-wasm-bindgen
    で直接オブジェクト渡しは必ずしも高速ではない
    Rust の内部レイアウトを JS が解釈できないため、フィールド単位で再構築する必要がある。結果として境界越え回数が増える。

  3. アルゴリズム的改善が言語レベルの最適化よりも大きいインパクトを持つ
    O(N²) から O(N) にした方が、WASM → TS の切替よりも実際に速くなるケースが多い。

  4. WASM と JS はヒープを共有しない
    WASM の線形メモリは生のバイト列であり、JS にはポインタや enum 判別子などが透明である。常に変換コストが発生する。


WASM が有効なケース

  • 計算負荷が高く、相互作用が少ない
    画像/動画処理、暗号化、物理シミュレーション、オーディオコード化など。入力は大きいが出力はスカラーか in‑place 修正のみで、境界越え頻度が極めて低い。

  • 既存のネイティブライブラリを再実装せずに使いたい
    SQLite, OpenCV, libpng など C/C++ ライブラリをブラウザへそのままデプロイできる。


WASM があまり有効でないケース

  • 構造化テキストを JS オブジェクトに変換するパース
    シリアライズコストは必須。Rust の高速計算が V8 の JIT を上回る余裕がなく、境界オーバーヘッドが支配的。

  • 小さな入力で頻繁に呼び出される関数
    呼び出し 50 回/ストリーム、1 回あたり計算時間 5 µs の場合、境界コストを摊り切れない。


結論

まずは アルゴリズムの効率化(O(N²) → O(N)) を優先し、次に「明らかなパフォーマンスメリットがある」場面でだけ言語/ランタイムを切り替える。
そうすることで、クリーンで高速、保守性の高いパーサーパイプラインを実現できた。

同じ日のほかのニュース

一覧に戻る →

2026/03/21 6:03

**OpenCode – オープンソースAI コーディング エージェント**

## Japanese Translation: **改善された要約** OpenCodeは、プライバシーを最優先にしつつオープンソースで開発されたAIコーディングエージェントです。ターミナル、IDE、またはデスクトップアプリとしてスムーズに動作します。使用されるLLMに応じて自動的に適切なLanguage Server Protocol(LSP)をロードし、同一プロジェクト上で複数のエージェントを同時に起動できるようにします。セッションは簡単なリンクで共有でき、参照やデバッグに利用できます。OpenCodeはGitHub Copilot、ChatGPT Plus/Pro、およびModels.devを通じて75社以上の大規模言語モデルプロバイダー(ローカルモデルも含む)と統合しており、さらに**Zen**というコーディングエージェント向けに特別にテスト・ベンチマークされたAIモデルのキュレートセットを提供します。プロジェクトは120,000件以上のGitHubスター、800人の貢献者、10,000件以上のコミット数を誇り、毎月5百万社以上の開発者に利用されています。またコードやコンテキストデータを保存しないため、プライバシーセンシティブな環境にも適しています。新リリースや機能拡張について情報を受け取りたいユーザーはウェイトリストに登録できます。

2026/03/21 4:16

**Windows 品質への我々の約束**

## Japanese Translation: Microsoft は、ユーザーから報告されたタスクバーの混乱したオプション、Copilot エントリポイントのごちゃごちゃ、侵入的なアップデート、遅いファイルエクスプローラー、騒がしいウィジェット、分断された Insider Program などの課題に対処するため、Windows 11 のユーザビリティ・パフォーマンス・信頼性の一連の更新を展開しています。 主な変更点は次のとおりです: - **タスクバー**: 新しい再配置オプション(上部、左側、右側)とより小さなタスクバーで、パーソナライズ性が向上します。 - **Copilot**: スニッピングツール、フォト、ウィジェット、メモ帳のエントリポイントを削減し、有用な体験に焦点を当てることでアクセスを簡素化しました。 - **Windows Update**: コントロールが拡張されました—設定中にアップデートをスキップでき、長時間停止、再起動またはシャットダウン時にインストールせずに済み、自動再起動/通知の数が減ります。 - **ファイルエクスプローラー**: 起動速度向上、ちらつき軽減、ナビゲーション滑らか化、ファイルタスクパフォーマンスの信頼性向上です。 - **ウィジェット**: デフォルトが静かになり、外観コントロールが改善され、Discover フィードのパーソナライズが向上しました。 - **Insider Program**: チャネル定義を明確化し、機能アクセスを容易にし、ビルド品質を高め、フィードバック可視性とエンゲージメント機会を増やすことで簡素化されました。 - **Feedback Hub**: 提出速度の向上とコミュニティインタラクションのために大幅な再設計が行われました。 - **システムパフォーマンス目標**: Windows のリソース使用量を低減し、メモリフットプリントを削減、アプリケーション、ファイルエクスプローラー、WSL 全体で応答性を改善します。 - **信頼性イニシアチブ**: OSクラッシュ、ドライバー品質、Bluetooth/USB の安定性、カメラ/オーディオ接続、デバイス再起動の一貫性、および月1 回の単一再起動と一時停止オプションを対象にします。 - **Windows Hello**: 顔認証の信頼性向上、指紋サインイン速度の高速化、ROG Xbox Ally X のようなゲーム用ハンドヘルドデバイスでの PIN 設定のセキュリティ強化です。 - **Craft 改善**: スタート/タスクバーの信頼性向上、パーソナライズ拡張、デバイス設定を静かに、ウィジェットの賢さ向上、通知削減、タスクバー・スタート・ファイルエクスプローラー・設定間で一貫した検索機能。 Microsoft は実際のハードウェア上で検証/テストを深化させ、デフォルトのセキュリティ設定を引き上げ、Insider のフィードバックに依存して将来の Windows 11 リリースを導く予定です。その結果として、ユーザーと開発者双方に対し、より柔軟なインターフェイス、スムーズな更新、静かな通知、そして高い信頼性が実現します。

2026/03/21 6:42

**タイトル:** GLP‑1薬を中止すると心筋梗塞と脳卒中のリスクが急増 **主なポイント:** - GLP‑1受容体作動薬(GLP‑1 RA)をやめると、心筋梗塞・脳卒中のリスクが高まります。 - これらの薬を中止した患者は、継続している患者に比べて心血管イベントの発生率が増加する可能性があります。 - 本研究は、GLP‑1 RAを服用している患者の心血管安全性には、投与継続(薬剤遵守)が重要であることを示唆しています。

## Japanese Translation: (以下の文は、元の意味を正確に保持し、構造や専門用語もそのまま維持した日本語訳です。) **改訂された要約:** 研究によると、短期間であってもグルカゴン様ペプチド‑1(GLP‑1)薬を中断すると、米国退役軍人の2型糖尿病患者において心臓発作や脳卒中のリスクが増加し、継続使用ではそれらのリスクが低減することが示されました。研究者は33万3000人以上の退役軍人を3年間追跡調査しました:GLP‑1治療を2年間停止したグループは心血管リスクが22%増加し、決して中断しなかった患者は18%リスク減少、再開のみで12%の利益にとどまることが明らかになりました。治療が途切れた際には体重・炎症マーカー・血圧・コレステロールが悪化し、「代謝的ウィップラッシュ」と呼ばれる効果が観察されました。GLP‑1薬はもともと糖尿病のために開発されましたが、現在では腎臓・肝臓・心血管系・関節炎・認知症・依存症などのアウトカムにも有益です。ただし、新規使用者のおよそ半数が早期に中断しています。著者らは、服薬遵守と効果を別々に追跡すべきだと主張し、医療システムには長期的な継続利用を支援するプログラムの構築を求めています。この研究は *BMJ Medicine* にZiyad Al‑Aly(ワシントン大学)によって発表され、心血管保護のためにGLP‑1療法を持続させる重要性を強調しています。