1 ポイント 投稿者 GN⁺ 2025-01-25 | 1件のコメント | WhatsAppで共有
  • Wildは反復的な開発で非常に高速なリンクを目指すリンカで、まだ増分リンクは実装されていないものの、非増分リンクだけでも高速な状態
  • 既存の高速リンカである mold は増分リンクを行わず、その予定もないことが Wild の出発点であり、Wild は Rust で書くことで増分リンクの複雑さに取り組むことを目標としている
  • 現在の対応プラットフォームは Linux の x86-64、ARM64、RISC-V、LoongArch64、PPC64LE で、静的バイナリ、static-PIE、動的リンクバイナリ、共有オブジェクト、Rust proc-macro、デバッグ情報などをサポート
  • GCC・Clang から既存リンカのように呼び出す drop-in replacement 方式で利用でき、Cargo、C/C++ ビルドシステム、CI 向けの wild-action と組み合わせて使える
  • まだ 増分リンク、より複雑な linker script、Mach-O、Windows 対応は実装されておらず、実際にリンクされたかどうかは readelfstrings でバイナリ内の Linker: Wild version... 文字列を確認する必要がある

Wildの目標と位置づけ

  • Wildは反復的な開発で非常に高速なリンクを目指すリンカ
  • 長期計画は 増分リンク を実装することだが、現時点ではまだ未実装
  • 増分リンクがなくても、すでに非増分リンク性能は高速だとしている
  • mold はすでに非常に高速だが増分リンクを行わず、作者も増分リンクを行う予定がないと述べている点が、Wild が別個のリンカとして作られた理由
  • Wild は Rust で書かれており、増分リンクの複雑さを Rust で扱えることを期待している

インストール方法

  • リリース tarball は releases page から取得でき、展開して wild バイナリを PATH 上の場所にコピーすればよい
  • cargo-binstall があれば、次のコマンドでインストール可能
cargo binstall wild-linker
  • Homebrew でのインストールコマンドは次の通り
brew install wild-linker/wild/wild
  • crates.io の最新リリースをビルドするには次のコマンドを使う
cargo install --locked wild-linker
  • Git の最新未リリースコードをインストールするには次のコマンドを使う
cargo install --locked --bin wild --git https://github.com/wild-linker/wild.git wild-linker
  • Nixpkgs の安定版 Wild は pkgs.useWildLinker pkgs.stdenv を通じて利用できる
  • 最新の不安定な Git リビジョンについては nix/nix.md ドキュメントを参照する必要がある

デフォルトリンカとして使う方法

  • Wild は drop-in replacement として設計されており、GCC や Clang から呼び出せる形で使える
  • 選択肢は次の通り
    • Clang 専用オプション --ld-path=wild
    • GCC 16.1 以降と Clang の -fuse-ld=wild
      • Clang では ld.wild バイナリまたはシンボリックリンクが必要
    • 一般的にサポートされる -B <path>
      • <path>wild を指す ld が入っているディレクトリ
  • Rust と Cargo

    • ~/.cargo/config.toml で Clang と --ld-path=wild を組み合わせて使える
    [target.x86_64-unknown-linux-gnu]
    linker = "clang"
    rustflags = ["-Clink-arg=--ld-path=wild"]
    
    • または -fuse-ld=wild を使うこともできる
    [target.x86_64-unknown-linux-gnu]
    
    # linker = "clang" # Uncomment this line if your GCC is older than version 16.
    rustflags = ["-Clink-arg=-fuse-ld=wild"]
    
  • C/C++ ビルドシステム

    • autotools、CMake、meson などでは通常 LDFLAGS の設定だけで十分
    export LDFLAGS="${LDFLAGS} -fuse-ld=wild"
    
    • 古い GCC バージョンでは、wild を指す ld シンボリックリンクを作成し、そのディレクトリを -B で渡せる
    ln -s /usr/bin/wild /tmp/ld
    
    export CFLAGS="${CFLAGS} -B/tmp"
    export CXXFLAGS="${CXXFLAGS} -B/tmp"
    export LDFLAGS="${LDFLAGS} -B/tmp"
    
    • この種のビルドシステムは複雑になりがちなため、Wild が実際にバイナリのリンクに使われたかどうかは readelf で確認する方法が推奨されている
  • Illumos 向け Cargo 設定

    • Illumos では Clang の絶対パスを指定し、Wild も絶対パスで渡す必要がある
    • Wild の絶対パスを指定しないと、GNU ld または Sun ld に静かに委譲される可能性がある
    [target.x86_64-unknown-illumos]
    
    # Absolute path to clang - on OmniOS this is likely something like /opt/ooce/bin/clang.
    linker = "/usr/bin/clang"
    
    rustflags = [
    
    # Will silently delegate to GNU ld or Sun ld unless the absolute path to Wild is provided.
        "-Clink-arg=-fuse-ld=/absolute/path/to/wild"
    ]
    

対応状況と未対応項目

  • 現在対応しているプラットフォームとアーキテクチャは次の通り
    • x86-64 on Linux

    • ARM64 on Linux

    • RISC-V(riscv64gc) on Linux

      • LoongArch64 on Linux
      • 初期サポート段階
      • PPC64LE on Linux
      • 初期サポート段階
      • 動作する機能は次の通りだが、バグがある可能性があるとの注記がある
      • 静的リンクされた non-relocatable バイナリ出力
      • 静的リンクされた position-independent バイナリ、すなわち static-PIE 出力
      • 動的リンクバイナリ出力
      • 共有オブジェクト .so ファイル出力
      • Wild でリンクされた Rust proc-macro
      • crates.io のダウンロード上位の多くの crate のテストを通過
      • デバッグ情報
      • GNU jobserver サポート
      • 部分的な linker script サポート
      • 詳細は linker script support matrix を参照
      • Linker plugin LTO
      • known issues がある
      • まだ実装されていない大きな項目は、現在の優先度が高い順で次の通り
    • 増分リンク

      • より複雑な linker script
      • Mach-O 対応
      • Windows 対応

Wild使用の確認方法

  • readelf は binutils パッケージでインストールでき、次のコマンドで .comment セクションの文字列を確認する
readelf --string-dump .comment my-executable
  • 次のような行があれば Wild でリンクされたバイナリ
Linker: Wild version 0.1.0
  • strings も binutils パッケージで使え、次のコマンドでも確認できる
strings my-executable | grep 'Linker:'

ベンチマークとテスト対象

  • Wild の目標は最終的に 増分リンク によって非常に高速なリンカになること
  • 非増分リンクと、増分リンクが有効になった際の初回リンクもできるだけ高速にすることを目指している
  • すべてのベンチマークは tmpfs に出力する条件で実行される
  • ベンチマークの実行方法は BENCHMARKING.md にまとめられている
  • ベンチマーク対象システムは次の通り
  • 例示されているベンチマークには、Chromium のリンク時間とメモリ使用量、librustc-driver のリンク時間、Wild 自身のリンク時間、Raspberry Pi 5 上での rust-analyzer のリンク時間が含まれる

Rustコードのリンク例とプロジェクト情報

  • cargo test で Wild を使って crate をビルドしテストする例は次の通り
  • このコマンドは ripgrepserdetokiorandbitflags などいくつかの人気 crate で正常に実行されている
  • wild バイナリが PATH にある必要があり、GCC は任意のリンカ使用を許可しないため Clang のインストールが必要
RUSTFLAGS="-Clinker=clang -Clink-args=--ld-path=wild" cargo test
  • ld.wild シンボリックリンクが wild を指している場合は次の方法も可能
RUSTFLAGS="-Clinker=clang -Clink-args=-fuse-ld=wild" cargo test
  • CI で Rust コードのリンカとして Wild を使うには wild-action を参照
  • コントリビューション情報は CONTRIBUTING.md、高レベル設計の概要は DESIGN.md にある
  • Wild に関する会話は Zulip server で行われている
  • David のブログには Wild リンカのさまざまな側面を扱った記事が多くある
  • ライセンスは Apache License 2.0 または MIT license のいずれかを選べる

1件のコメント

 
GN⁺ 2025-01-25
Hacker Newsのコメント
  • moldがAGPLからMITへ再ライセンスされたあと(mold 2.0リリースの一部)、世界的に見て別の高速リンカを作る必要性はかなり薄れたと思っていたので、こういうプロジェクトが出てくるとは思わなかった
    しかも場合によってはすでにmoldより2倍速いというのだからなおさら意外で、どう発展するのか見守りたいし、作者の幸運を祈る

    • moldには増分リンクをやるつもりがないので、別のリンカが存在する十分な理由はある
      Microsoftのリンカは何十年も前から増分リンクがデフォルトなのに、Linuxにはいまだに本番運用可能な増分リンカがないのは少し気まずい
    • リンカにおいてAGPLとMITの違いはなぜ重要なのか?
    • moldのWindows対応がどういう状況なのかわからない
      GitHubのissueを見ると、作者は最初は対応しようとしていたが、Windows対応をsoldリンカに移したようで、しかしsoldは最近アーカイブされたので、結局Windows対応はないのか、それとも自分が流れを読み違えているのか混乱している
    • 自分の使い方が間違っているのかもしれないが、たぶん使うべきLTOを使うと、moldはまったく速くならない
    • ちょっと待って、GPLのものをMITに再ライセンスするなんて可能なのか?
  • 前に見てみたが、まだ本番で使える状態なのかはわからない
    READMEを見る限りそうではなさそうなので、今もmoldを使っている
    macOSユーザー向けには、Appleが1〜2年ほど前に新しいリンカを出していて、そのためmold作者はmacOS版の作業を中止した
    Rustで使うならconfig.tomlにこう入れればよい

    [target.aarch64-apple-darwin]

    rustflags = [

    "-C",

    "link-arg=-fuse-ld=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",

    "-C",

    "link-arg=-ld_new",

    ]

    • 作者はまだ本番で使うべきではないとかなり明確に言っている
    • moldも本番では使わず、開発用として使っている
    • 最近は新しいリンカがもうデフォルトではないのか? あれを追加しても効果があるのかよくわからない
    • Sequioaでもその場所で合っているか確認できる?
      コマンドラインツールはインストールしたが、/usr/bin/ld/usr/bin/ld-classicしかない
  • C/C++コンパイラがリンクという中間段階をなくして、プログラム全体を1つの単位としてビルドしたら新鮮だと思う
    コンパイラが最初からプログラム全体を見られるなら、LTO自体が不要になる
    増分ビルドのために一部のビルド成果物を保存する必要はあるだろうが、オブジェクトファイルの形ではなく、生成コードの出所と依存関係がわかるメタデータが必要で、正しい部分だけ置き換えられるようにすべきだ
    最近の外部ライブラリはたいてい動的リンクなので、ソースからビルドする必要はなく、リンカをなくしても非公開依存関係で問題になることはない
    それでも足りなければ、コンパイラがオブジェクトファイルも受け付けるようにして、バイナリに静的リンクしなければならないレガシーや特殊なケースに対応できる

    • SQLite3はすべてをつなぎ合わせて1つのコンパイル単位にしている
      たぶん思っている以上に多くの人がすでにこの方式を使っている

      https://sqlite.org/amalgamation.html

  • 高速リンカへの関心はRustの採用と人気によって大きく高まった
    そこそこのサイズの静的リンクRustバイナリでも、リリースモードのコンパイルではリンク段階に数分かかることがある(mold使用時でも)
    Rustだけの問題ではなく、全体として厳格な静的リンク、LLVMが提供するLTOやBOLTのような高度なリンク時最適化、Rustコミュニティのコンパイル時間への不満が組み合わさった結果だ
    RustはLLVMとの強い関係、事実上の依存関係によって、LLVMのリンク時マジックが最も広く採用されている言語になった。C++でも同じ問題は起こりうるが、その場合は言語よりもツールチェーンのせいだと見なされる可能性が高い
    wildは最適化増分リンカになる可能性があるのでしばらく注目していたが、正直、実際に増分リンクができるようになるまでは触ってみる動機がまったくない

    • C++は一部のコンパイラが増分コンパイルと増分リンクをサポートしているため、Rustよりずっと速くコンパイルできる場合がある
      またC/C++エコシステムにはバイナリライブラリを受け入れる文化があり、リポジトリをクローンしたり開発ブランチを切り替えたりするたびに世界全体を再コンパイルせず、自分のアプリケーションだけをコンパイルすれば済むことが多い
    • この問題はWasmで解決した
      外側のアプリケーションシェルからWasmのビジネスロジックを呼び出すようにすれば、内部ロジックだけ再コンパイルすればよく、外側のアプリシェルを再起動する必要もない
  • 2008年: GNU ldより速い新リンカgold

    2015年ごろ: goldより少なくとも2倍速いドロップイン置き換えリンカlld

    2021年: lldより数倍速い新リンカmold

    2025年: 新リンカwild...

    • あまり言及されないが、これはどれも実際のプログラムが使う多くの有用な機能を実装しない代償として可能になっている
    • 否定的な話なのか肯定的な話なのか、あるいは短い歴史整理なのかわからないが、リンカからまだより良い性能を引き出せるという事実はかなり励みになる
    • goldはbinutils 2.44.0で削除予定なので、公式に死んだも同然だ
    • Windowsにはまだ初期段階だが、The RAD Linkerもある
  • 関連して、古いが良い本としてJohn LevineのLinkers and Loadersがある
    下の一覧の最後の本だ

    https://www.johnlevine.com/books.phtml

    数年前に読んだがかなり面白く、この分野の定番本だ
    リンクを見ればわかるように、ほかにも人気のあるコンピュータ書籍を書いている

  • かなり有望に見える
    最初からRustで書かれていて、高速で増分リンクをサポートすることを目標にしている
    Rustで使うなら、たぶんgccをリンカドライバとして使ってもいけるはずだ
    プロジェクトの.cargo/config.tomlに:

    [target.x86_64-unknown-linux-gnu]

rustflags = ["-C", "link-arg=-fuse-ld=wild"]

脇道ですが、Rust はなぜここで gcc や clang を経由する必要があるのでしょうか? 足りない機能があるのでしょうか?

  • 残念ながら gcc は -fuse-ld= フラグで任意のリンカを受け付けません
    受け付けるリンカは bfd、gold、lld、mold だけです
    gcc に wild をリンカとして呼ばせることはできますが、現状では wild リンカが入ったディレクトリを作り、バイナリまたはシンボリックリンクの名前を "ld" に変えたうえで、gcc に -B/path/to/directory/containing/wild を渡す必要があります
    Rust がリンカを直接呼ばず gcc や clang 経由でリンカを呼ぶ理由は、C コンパイラが現在のプラットフォームで libc と C ランタイムをリンクするために必要なリンカフラグを知っているからです
    たとえば Scrt1.ocrti.ocrtbeginS.ocrtendS.ocrtn.o などです

  • Rust コンパイラが機械語ではなく IR バイトコードを生成するからです

  • 気になるのですが、非インクリメンタルな場合にこれが mold より速くなりうる理論的な理由は何でしょう?
    「Rust だから」はいろいろなことの説明にはなりますが、期待される性能上の利点までは説明しません
    「Rust によって活用しやすくなる難易度の低い並列化の機会がある」という話なら興味深いですが、明示も示唆もされていません

    • はっきりとは分からず、主に Mold のコードベースに詳しくないからです
      手がかりの一つは、Mold はより高速なアロケータ mimalloc を使うと約 10% 速くなると聞いたことです
      Wild で mimalloc を試しましたが、測定可能な速度向上はありませんでした
      これを見ると、Mold は Wild よりアロケータを多用しているようですし、Wild ではヒープ割り当て回数を最適化しようと明確に努力しました
      全体としては設計判断の違いだと思います
      これが Rust とどう関係するかというと、Wild を Rust から C や C++ に移植しても性能はほぼ同じだろうと確信しています
      ただし Rust では 借用チェッカー のおかげで問題ないコードパターンが、C や C++ では落とし穴になりうるため、保守性が厄介になるかもしれません
      以前 C++ で書いていたころは、小さな性能コストを受け入れてでも、より防御的に書いていましたが、Rust ではコンパイラが後ろ盾になってくれると分かっているので、ずっと大胆に書けます
  • 偶然ですね
    1 時間前に作業中の C プロジェクトで wild、mold、ld の性能を比較しました
    2.3 万行、172 ファイルで、gcc+ld ではユーザー時間が約 23.4 秒、gcc+mold は 22.5 秒、gcc+wild は 21.8 秒でした
    なので、構造がしっかりしたプロジェクトならリンク時間はそれほど大きな問題ではないかもしれないと思います

    • まっさらな状態からビルドしているように聞こえます
      その場合、時間の大半はリンクではなくコードのコンパイルに使われます
      高速リンカの利点は 反復開発 で最も大きいです
      つまり、コードに小さな変更を加えて再ビルドし、結果を実行するとき、コンパイラの仕事は普通ほとんどありませんが、リンクは依然として最初から行われるため、支配的な時間になりやすいのです
    • リンク時間が重要になるのは、小さなプロジェクトではなく Chrome のようなものをビルドするときです
    • 高速リンカはたいてい インクリメンタルコンパイル の状況で編集サイクルを短縮するのに役立ちます
    • ld.lld はどうですか?
  • 「このベンチマークは David Lattimore のノート PC(2020 年モデルの System76 Lemur Pro)で実行され、4 コア(8 スレッド)と 42GB RAM を搭載している」

    https://news.ycombinator.com/item?id=33330499

    ちなみに wild が肥大化していると言いたいわけではありません
    問題があるとすれば、それで開発されるソフトウェアと、そうしたソフトウェアを使う人たちのコンピュータの側にあります

    • https://news.ycombinator.com/item?id=42896619

      「... RAM が 16GB で、アップグレードできない ...」

    • 半分冗談ですが、Rust でコーディングする人ならすでに 32GB RAM は積んでいそうです
      個人的には、ノート PC を 64GB にアップグレードするために文字どおり他のすべてを犠牲にしたのは、ほとんど素晴らしい判断でした
      ほとんどと言ったのは、RAM に全振りするのではなく、RAM とディスプレイの両方にお金をかけるべきだったからです
      唯一の欠点は、週に 1 回、開いたタブを整理する作業が夜いっぱいかかる仕事になったことです