
2025/12/27 2:13
**原文:** 「How uv got so fast」 **改訂(整理済み):** 「How did UV get so fast?」 **日本語訳:** 「UVはなぜそんなに速くなるのですか?」
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
## Summary uv は Rust のみを利用するよりも、モダンな設計選択とパッケージング標準を採用しているため、pip より優れています。不要なオーバーヘッドを伴うレガシー機能(.egg サポート、pip.conf の読み込み、デフォルトのバイトコードコンパイル、system‑Python インストールの許可、厳格な仕様準拠、requires‑python の上限無視、複数インデックスが設定されている場合に最初のインデックスを選択)を削除することで、uv は不要なコードパスを排除します。さらに、HTTP レンジリクエストによる部分的な wheel ダウンロード、並列ダウンロード、ハードリンクまたはコピーオンライト可能なグローバルキャッシュ、ネイティブ TOML パース、および効率的な PubGrub 依存関係解決器を使用してインストール速度を加速させています。Rust は rkyv によるゼロコピー逆シリアライズ、ロックフリーの同時実行データ構造、単一静的バイナリであるためのインタプリタ起動コストゼロ、および高速比較/ハッシュのためのコンパクトな 64‑ビットバージョン表現を提供します。 Python のパッケージングは、setup.py スクリプトがインストール時に実行される必要があったため遅くていました。PEP 518–658 の導入によりメタデータが宣言的フォーマットへ移行し、PyPI 上の Simple API(2023年5月以降利用可能)が有効になりました。uv は 2024 年2月にリリースされ、初日からこれら新しい標準を活用しています。 影響は大きく、開発者は依存関係をより迅速にインストールでき、軽量な環境を構築できます。これは、Python エコシステム全体で最新のパッケージング標準を完全にサポートするツールへの広範な移行を促進する可能性があります。
本文
uvはpipよりも桁違いに速くパッケージをインストールできます。
一般的な説明として「Rustで書かれているから」と言われますが、これは真実ではあるものの、そこまで説明力がありません。Rustで書かれたツールでも特に高速とは限らないため、本当に速くなる設計上の違いは何なのかが重要です。
Charlie Marsh の Jane Street の講演や Xebia のエンジニアリング・ディープダイブでは技術的詳細まで丁寧に扱われています。
本質的に興味深いのは、どんな設計決定が差を生んだかです。
uvを可能にした標準(PEP)
pip の遅さは実装上の失敗ではなく、Python パッケージングが長年「コードを実行して依存関係を判定する」方式だったためです。
問題点は
setup.py にあります。パッケージの依存関係を知るにはそのスクリプトを実行しなければならず、逆に setup.py を実行するにはビルド時の依存関係が必要という相互依存(チキン・アンド・エッグ)問題でした。
2016 年の PEP 518 はこれを明示的に指摘し、
「
ファイルを実行せずにその依存関係を知る標準的な方法は現在存在しない。」setup.py
という課題を提示しました。pip はパッケージをダウンロードして不正コードを実行し、失敗したらビルドツールをインストールし、再試行するという連鎖的な subprocess を発生させるしかありませんでした。
この問題は段階的に解決されました:
| 変更 | 内容 |
|---|---|
| PEP 518 (2016) | が導入され、ビルド依存関係をコード実行なしで宣言できるようになった。TOML フォーマットは Rust の Cargo から借用された。 |
| PEP 517 (2017) | ビルドフロントエンドとバックエンドを分離し、pip は setuptools の内部構造を理解する必要がなくなった。 |
| PEP 621 (2020) | テーブルを標準化し、依存関係は TOML をパースするだけで取得できるようになった。 |
| PEP 658 (2022) | パッケージメタデータを Simple Repository API に直接埋め込み、wheel をダウンロードせずに依存情報を取得できるようになった。 |
PEP 658 は 2023 年 5 月に PyPI 上で有効化され、uv は 2024 年 2 月にリリースされました。
つまり uv が高速である理由は、エコシステムが「標準を通じてメタデータを取得できる」インフラを整備した時点から可能になったということです。
他のパッケージマネージャー(Cargo、npm)はこのような静的メタデータを最初から採用しており、Python も同等に仕上げたわけです。
uv が削除した機能
速度は「不要なコードパスを排除する」ことから来ます。uv の互換性ドキュメントには「何をやらないか」が明記されています:
非対応 – Egg はワンディスクバイナリフォーマットで、pip だけが扱います。uv は無視します(10 年以上廃止)。.egg
無視 – pip の設定ファイルを全く読みません。環境変数やシステム/ユーザー階層の継承もありません。pip.conf- デフォルトでバイトコード化しない – pip は
を生成しますが、uv は省略し時間短縮。必要ならオプションで有効にできます。.pyc - 仮想環境必須 – pip はシステム Python にインストール可能ですが、uv は明示的なフラグなしではシステム Python を変更しません。権限チェックと安全コードを削除します。
- 厳格な仕様検証 – pip は仕様違反パッケージも受け入れますが、uv は拒否します。フォールバックロジックが少なくなるため高速化。
の上限無視 –requires-python
などの上限はほぼ間違いなので uv は下限のみをチェックし、解決器のバックトラッキングを大幅に削減します。python<4.0- 最初のインデックス優先(first‑index wins) – 複数インデックスが設定されている場合、uv はパッケージが存在する最初のインデックスから取得し、追加リクエストを行いません。
これらは pip が実行しなければならないコードパスであり、uv では省かれています。
Rust を使わなくても可能な最適化
uv の高速さは Rust によるものだけではありません。pip でも今日実装できる重要な最適化があります:
- HTTP Range リクエストによるメタデータ取得 – wheel は ZIP アーカイブで、ファイルリストは末尾にあります。uv はまず PEP 658 メタデータを試み、失敗したら範囲リクエストで中央ディレクトリを取得し、さらに完全ダウンロードへ進むという段階的アプローチです。
- 並列ダウンロード – pip は直列にパッケージを取得しますが、uv は同時に複数をダウンロードします。
- グローバルキャッシュ + ハードリンク – pip は仮想環境ごとにコピーしますが、uv は一度だけ保存しハードリンク(または COW)で共有します。
- Python を使わない解決器 – uv は TOML と wheel メタデータをネイティブにパースし、
しかないパッケージのときのみ Python を起動します。setup.py - PubGrub 解決器 – uv は Dart の pub パッケージマネージャーから採用した PubGrub アルゴリズムを使用し、pip のバックトラッキングより高速で解決失敗の説明も優れています。
これらは言語に依存せず実装可能です。pip が今日実装できるのに、旧来の互換性を維持するために実装されていないという現状があります。
Rust が本当に必要な場面
以下は Rust の特徴が真価を発揮するケースです:
- ゼロコピーデシリアライズ –
を使ってキャッシュ済みデータをコピーせずに読み込みます。rkyv - ロックフリー並行構造体 – 所有権モデルで安全なスレッド間アクセスが可能です(Python の GIL では難しい)。
- インタープリタ起動コストの削減 – uv は静的バイナリで、サブプロセスを spawn しても Python 起動オーバーヘッドはありません。
- コンパクトなバージョン表現 – 多くの場合
に収まるように圧縮し、比較とハッシュを高速化します。u64
これらは確かに有効ですが、設計上の選択によって得られる勝利(標準化されたメタデータ、不要機能の削除など)ほど大きくありません。
言語よりも設計が重要
uv が高速なのは「何をやらないか」によるところです。
PEP 518/517/621/658 で整備された標準化と、古い機能(Eggs, pip.conf 等)の削除、そして最適化の組み合わせが真価を発揮します。Rust はそれらをさらに高速に実装する手段として役立ちますが、根本的な理由ではありません。
pip も並列ダウンロードやグローバルキャッシュ、メタデータのみで解決できる機能を明日からでも追加可能です。ただし 15 年にわたるエッジケースの後方互換性が優先されているため、実装は遅れています。結果として pip は常に「現状維持」モードで動くツールになり、uv のような新規設計では追いつけません。
他のパッケージマネージャーも同様に学べる教訓があります:
- 静的メタデータを採用し、依存関係判定でコード実行を避ける。
- すべてを解決してからダウンロードする前提で設計する。
Cargo や npm はこのアプローチを長年継続しています。もしエコシステムが「パッケージの必要性を知るために任意コードを実行」しなければならないとしたら、速度的には既に不利になります。