Vize

性能を高めるための工夫

フロントエンドのツールチェーンの性能改善は、ひとつの技で片付くものではありません。

「Rust で書き直して、グラフが伸びるのを待つ」という話ではないのです。時間がどこで使われているのか、メモリがどれだけ頻繁に動いているのか、同じ作業がどれだけ重複しているのか、改善が積み重なっていくアーキテクチャになっているのか。そうした、小さく具体的な判断の長い積み重ねです。

このノートでは、Vize が最適化し続けているポイントを共有します。

ソースファイル、ネイティブの解析、スナップショット、アクション、安心してリリースできる状態をつなぐフィードバックループの図

ループ全体を測る

コンパイラのベンチマークは役に立ちますが、それが開発体験のすべてではありません。

Vue のツールチェーンには、いくつものフィードバックループがあります。

  • 開発サーバーでの 1 ファイルのコンパイル
  • 本番ビルド全体
  • 多数のファイルの lint
  • 多数のファイルのフォーマット
  • 生成された仮想ファイルの型チェック
  • 入力中にエディターが表示する診断
  • 実際のアプリケーションを対象にした CI のチェック
  • AI が生成したパッチの、繰り返しの検証

最も遅いループが、いちばん目立つループだとは限りません。

単体では速く見える関数でも、すべての段階で実行されるなら害になります。小さなメモリ割り当てでも、トークン、AST のノード、診断、生成されるコード片のひとつひとつで発生するなら、無視できません。

だから Vize は、性能をコンパイラだけの性質ではなく、ツールチェーン全体の性質として扱っています。

同じ作業を繰り返さない

最も確実な最適化は、同じ作業を 2 度しないことです。

ツールがばらばらの構成では、同じ .vue ファイルを、次のそれぞれが別々にパースすることがあります。

  • コンパイラ
  • リンター
  • フォーマッタ
  • 型チェッカー
  • エディターの連携
  • コンポーネントのドキュメントを作る仕組み

これはコストがかかります。しかし、より根の深い問題はアーキテクチャにあります。ツールごとにファイルの理解を組み立てていると、性能改善はそれぞれの中に閉じた、限られたものになります。

Vize は、構造を共有するように設計しています。

  • できるだけ 1 回だけパースする
  • SFC のブロックの境界を安定させておく
  • テンプレートの構造を、コンパイラと診断で再利用する
  • 意味解析の結果を、複数の処理で共有する
  • 入力が変わっていなければ、仮想 TypeScript を生成し直さない

最良の最適化は、多くの場合、所有の境界をよりよく引き直すことです。

メモリ割り当ては細部ではなく、機能そのもの

フロントエンドのツールは、小さなオブジェクトを大量に扱います。トークン、ノード、範囲、文字列、スコープ、診断、生成されるコード片などです。

それらを気軽に割り当てていると、ツールチェーンのあらゆる場所でそのコストを払うことになります。

Vize は、メモリ割り当ての振る舞いに強く気を配っています。

  • 寿命の短いコンパイラのデータには、アリーナ方式の領域を使う
  • 繰り返し現れる識別子や名前には、文字列のインターンを使う
  • 部分文字列をコピーする代わりに、コンパクトな範囲で表す
  • 所有が不要な場所では、借用したスライスを使う
  • 大きな構造体を複製する代わりに、安定した内部 ID を使う

目的は、コードを巧妙にすること自体ではありません。

頻繁に実行される処理を単純に保つことです。割り当ても、コピーも、キャッシュミスも減らし、アロケータがプロファイルに顔を出す理由をなくします。

並列化には明確な境界が必要

並列化とは、「スレッドを有効にする」ことではありません。

問題に、はっきりした境界があるときに最もうまくいきます。

  • 互いに独立した多数のファイル
  • 決定論的な集約
  • 予測できる出力の順序
  • グローバルな状態を共有して書き換えないこと
  • 上限のあるキャッシュとセッション

Vue のコンパイル、lint、フィクスチャの総当たりは、多くの場合、ファイル単位で自然に並列化できます。一方で、型チェックとエディターのワークフローは、プロジェクトの状態に依存するので、もっと繊細です。

そこで Vize は、次の問いを分けて考えます。

  • このファイル単位の作業は、独立して実行できるか
  • この処理には、常駐するプロジェクトのセッションが必要か
  • 出力の順序は、利用者から見えるか
  • スレッド数が変わっても、診断は安定しているか
  • 並列化によるメモリの圧迫が、得られる効果を打ち消していないか

速くても出力が不安定なら、十分ではありません。性能の改善は、信頼を保ったまま行う必要があります。

ソースの対応付けがホットパスになることもある

Vue のツールは、中間コードを生成することがよくあります。

つまり、よい診断にはすべて、元に戻るための道筋が必要です。

  • 生成された TypeScript から、元のテンプレートへ
  • 生成されたレンダリングコードから、SFC のソースへ
  • 変換されたスタイルやスクリプトの出力から、元のブロックへ
  • 仮想モジュールの ID から、実際のファイルへ

ソースの対応付けが遅かったり不正確だったりすると、ツールチェーン全体に影響します。利用者には、間違った場所に診断が表示されます。AI による修正のループは、不正確な座標を受け取ります。テストは壊れやすくなります。

そのため、ソースの対応付けには、パースと同じだけ性能への注意を払う価値があります。

  • 範囲をコンパクトに保存する
  • パスの正規化を繰り返さない
  • 生成されたコード片のメタデータを小さく保つ
  • エッジケースをスナップショットでテストする
  • コンパイルが成功する経路だけでなく、診断が多いワークロードもプロファイルする

診断は、製品として利用者の目に触れる部分です。その性能も重要です。

実際のプロジェクトは、作られた安心感に勝る

マイクロベンチマークは、的を絞った問いに答えるときには役に立ちます。

しかしツールチェーンが誠実なものになるのは、実際のプロジェクトで実行したときです。

実際のプロジェクトには、次のようなものが含まれています。

  • 変わった依存関係の構成
  • 大きな SFC
  • 古い書き方
  • 自動生成されたコード
  • 珍しいディレクティブ
  • プラグインの規約
  • パスエイリアス
  • プラットフォーム固有のエッジケース

だから Vize は、実プロジェクトのフィクスチャの総当たりとビルドのスナップショットに投資し続けています。目的は、見栄えのよいテストの件数を集めることではありません。本番のコードと同じように散らかったコードで初めて現れる、性能が急激に低下する箇所を見つけることです。

性能は製品の機能

速さは、人の行動を変えます。

チェックが遅ければ、実行される回数が減ります。 フォーマットが遅ければ、保存時のフォーマットがわずらわしくなります。 型情報を使う lint が遅ければ、チームはそのルールを無効にします。 CI が遅ければ、メンテナーは変更をまとめ、レビューが雑になります。 AI による検証が遅ければ、エージェントはより大きく、より危うい飛躍をします。

速いツールがあってこそ、より厳格なワークフローが現実的になります。

これが、Vize が性能にこだわる本当の理由です。目的は、ベンチマークの数字をよくすることだけではありません。厳格なやり方を、当たり前のやり方だと感じられるようにすることです。

コンパイル、lint、フォーマット、型チェック、診断が、構えることなく実行できるほど速くなれば、品質は特別な行事ではなくなります。

いつもの仕事の進め方になるのです。