- lsrは、io_uringベースのIOライブラリ ourio を活用して開発された新しい
ls(1) 代替プログラム
- 既存の
ls および代替ツール(eza, lsd, uutils ls)と比べて、コマンド実行速度が非常に高速で、システムコール数も10倍以上少ない
- ディレクトリのオープン、
stat、lstat など主要なIOをすべて io_uring で非同期・バッチ処理し、性能を最大化。ファイル数が多いほどより高速
- Zigの
StackFallbackAllocator を活用し、メモリ割り当て時の mmap 呼び出しを最小化
- 動的リンクなしで静的ビルドされており、実行ファイルサイズも既存の
ls より小さい
紹介と意義
- lsrプロジェクトは、一般的な
ls コマンドの代替として io_uring を活用する高速なディレクトリ一覧ツール
- 既存の
ls、eza、lsd、uutils ls と比較して、実行速度とシステムコール使用量の両面で優れた性能を示す
- 自作のIOライブラリ(ourio)により、可能な限り多くのIOを直接処理
- ベンチマークを通じて、lsrが大規模なファイル環境でも高い性能と品質を証明
ベンチマーク結果
hyperfine を使い、n個の通常ファイルがあるディレクトリで各コマンドの実行時間を測定
lsr -al は10〜10,000個のファイル条件で、既存の ls / 代替ツールに比べて圧倒的に短い実行時間を記録
- 例:10,000個のファイルでは lsr が 22.1ms、既存の
ls(38.0ms)、eza(40.2ms)、lsd(153.4ms)、uutils ls(89.6ms)を上回る最高速度を記録
- システムコール集計は
strace -c で実施
lsr -al:最小20回(n=10)から最大 848回(n=10,000)と、非常に少ないコール数を維持
ls は最大30,396回(n=10,000)、lsd は100,512回など、他の代替ツールも数千〜十万回規模
- 同一条件で lsr は最低でも10倍以上少ない syscall 数で、最高の効率を達成
lsrの構造と実装方式
- プログラムは 引数解析、データ収集、データ出力 の3段階で動作
- すべてのIOは2番目のデータ収集段階で発生し、可能なあらゆるファイルアクセス / 情報取得を io_uring で処理
- 対象ディレクトリのオープン、
stat、lstat、時刻 / ユーザー / グループ情報の取得をすべて io_uring ベースで実行
stat をバッチ処理することで、システムコール数を大幅に削減
- Zigの
StackFallbackAllocator により1MBのメモリを事前確保し、mmap など追加のシステムコールを最小化
静的ビルドと最適化
- libc の動的リンクなしの完全静的ビルドのため、実行オーバーヘッドが著しく小さい
- GNU
ls と比べて、lsr の ReleaseSmall ビルドサイズは 138.7KB 対 79.3KB で、より小さい
- ただし lsr には ロケール(言語 / 地域)サポートがない。一般的な
ls は多言語対応のためにオーバーヘッドが発生する
システムコールと性能課題の分析
lsd はファイルごとに clock_gettime を5回以上呼び出しており、その理由は不明(内部タイミング計測などと推測)
- ソート処理が全体処理のかなりの部分(約30%)を占める
uutils ls はシステムコール効率は高いが、ソート処理で遅くなる
- io_uring の導入だけでも、サーバーなど高負荷IO環境で革新的な性能向上の可能性が確認された
結論
- 開発時間もそれほど長くかからず、syscall 最適化の効果は期待以上
- lsr は 高速、少ないシステムコール、コンパクトなサイズ を同時に実現する実験的な
ls 代替ツール
- 大量ファイル環境や高性能IOが重要なシステムに非常に適している
- locale 未対応など一部の機能制約はあるが、実運用でもベンチマークでも革新的な結果を示している
1件のコメント
Hacker Newsのコメント
プロジェクトの作者であることを明かしたうえで、io_uring ベースの
lsrの紹介記事はこちらで読めるとのことtim(紹介リンク)で時間計測すると hyperfine より良さそう。Nim 製なのでハードルはあるかもしれないが、名前が似ているのは偶然にしては面白いNFS サーバー上で(特に良くないネットワーク環境で)lsr の性能がどうなるのか気になる。信頼性の低いネットワークサービスに blocking な POSIX syscall を使うことが NFS 設計の弱点であるのは明らかで、io_uring がこうした問題をどこまで緩和できるかも観察ポイント
syscall 呼び出し回数を 35 倍減らしたのに、速度改善が 2 倍程度という点は興味深い
io_uring の活用事例として期待していた長期的な性能向上というより、チュートリアル的な使い方紹介としてのほうが関心を引くプロジェクト。既存の eza のようなツールと比べて、なぜこれが必要なのかという体感的な動機付けがなかった。ファイル 1 万件の一覧表示が 40ms と 20msだとしても、単発実行ではまったく違いを感じない気がする
lsr も良いが、カラー表示やアイコン対応は eza のほうが優れている。自分は "eza --icons=always -1" に設定しているので、音楽ファイル(.opus など)は自動でアイコンと色が付くが、lsr ではただの普通のファイルとして表示される。それでも lsr はパッチも当てやすく、非常に速いことは確かに感じる。さらに cat や他のユーティリティもこういう形で作ってほしいという期待や、tangled.sh と atproto を使っている点も面白い。Zig 製なので Rust より初心者にはとっつきやすく感じる
なぜすべての CLI ツールが io_uring を使わないのか気になっていた。自分は nvme を usb 3.2 gen2 に接続したとき、普通のツールでは 740MB/s だが、aio や io_uring ベースのツールでは 1005MB/s まで出る。キュー長戦略やロック削減の効果もあると思う
lsd がファイルごとに clock_gettime を 5 回ほど呼んでいるように strace で観察される。正確な理由は分からず、おそらく各タイムスタンプについて「何分前/何時間前/何日前」を計算しているか、ライブラリのレガシーかもしれない
man 7 vDSOを参照)。もしかすると Zig がこの構造を活用していないのでは、という推測やや脱線気味かもしれないが、Mellanox 4 または 5 のようなハイエンド企業向けサーバーで、10G NIC 環境において io_uring が LD_PRELOAD と比べてソケット遅延オーバーヘッドをマイクロ秒単位でどれだけ減らせるのか、実体験やベンチマーク値が気になる。効果は積み上がらないようにも見えるので、直接の経験があれば数値を聞きたい
io_uring は getdents をサポートしていないため、主な利点は bulk stat(例: ls -l)で現れる。getdents 処理を非同期化して並行実行できればよいのに、という惜しさがある
.mjs や .cjs 拡張子のアイコンはあるのに、.c、.h、.sh のようなファイル拡張子にはないのが面白い