- lsr ist ein neues Ersatzprogramm für
ls(1), das mit der io_uring-basierten IO-Bibliothek ourio entwickelt wurde
- Im Vergleich zu bestehendem
ls und Alternativen (eza, lsd, uutils ls) ist es bei der Befehlsausführung deutlich schneller und benötigt mehr als 10-mal weniger System Calls
- Zentrale IO-Vorgänge wie Verzeichnisöffnung,
stat und lstat werden vollständig asynchron und gebündelt über io_uring verarbeitet, um die Leistung zu maximieren. Je mehr Dateien vorhanden sind, desto größer der Geschwindigkeitsvorteil
- Für die Speicherallokation wird Zigs
StackFallbackAllocator genutzt, um mmap-Aufrufe zu minimieren
- Durch einen statischen Build ohne dynamisches Linking ist sogar die Binärgröße kleiner als bei herkömmlichem
ls
Einführung und Bedeutung
- Das Projekt lsr ist ein schnelles Tool zur Verzeichnisauflistung, das
io_uring als Alternative zum normalen ls-Befehl einsetzt
- Im Vergleich zu
ls, eza, lsd und uutils ls zeigt es herausragende Werte bei Ausführungsgeschwindigkeit und Anzahl der verwendeten System Calls
- Mit der selbst entwickelten IO-Bibliothek (ourio) wird möglichst viel IO direkt abgewickelt
- Benchmarks zeigen, dass lsr auch in Umgebungen mit sehr vielen Dateien hohe Leistung und Qualität liefert
Benchmark-Ergebnisse
- Mit
hyperfine wurde die Ausführungszeit der einzelnen Befehle in Verzeichnissen mit n regulären Dateien gemessen
lsr -al erzielte bei 10 bis 10.000 Dateien im Vergleich zu klassischem ls und Alternativen eine deutlich kürzere Laufzeit
- Beispiel: Bei 10.000 Dateien erreichte lsr 22,1 ms und war damit schneller als klassisches
ls (38,0 ms), eza (40,2 ms), lsd (153,4 ms) und uutils ls (89,6 ms)
- Die Erfassung der System Calls erfolgte mit
strace -c
lsr -al: von mindestens 20 Aufrufen (n=10) bis maximal 848 Aufrufen (n=10.000) und damit durchgehend sehr niedrige Werte
ls kam auf bis zu 30.396 Aufrufe (n=10.000), lsd sogar auf 100.512; auch die übrigen Alternativen lagen im Bereich von Tausenden bis Hunderttausenden Aufrufen
- Unter denselben Bedingungen erreicht lsr mit mindestens 10-mal weniger Syscalls die höchste Effizienz
Architektur und Implementierung von lsr
- Das Programm arbeitet in drei Phasen: Argumente parsen, Daten erfassen, Daten ausgeben
- Sämtliche IO findet in der zweiten Phase, der Datenerfassung, statt; dabei werden möglichst alle Dateizugriffe und Informationsabfragen über
io_uring abgewickelt
- Öffnen des Zielverzeichnisses,
stat, lstat sowie die Abfrage von Zeit-, Benutzer- und Gruppeninformationen erfolgen alle auf Basis von io_uring
- Durch gebündelte
stat-Verarbeitung wird die Zahl der System Calls drastisch reduziert
- Mit Zigs
StackFallbackAllocator werden 1 MB Speicher vorab reserviert, um zusätzliche System Calls wie mmap zu minimieren
Statischer Build und Optimierung
- Durch einen vollständig statischen Build ohne dynamisches Linking von libc fällt der Ausführungs-Overhead deutlich geringer aus
- Gegenüber GNU
ls ist die Größe des ReleaseSmall-Builds von lsr mit 79,3 KB statt 138,7 KB kleiner
- Allerdings unterstützt lsr keine Locale-Einstellungen (Sprache/Region). Normales
ls hat durch die Unterstützung vieler Sprachen entsprechenden Overhead
Analyse von System Calls und Leistungsaspekten
lsd ruft pro Datei mehr als fünfmal clock_gettime auf; der Grund dafür ist unklar, vermutet werden interne Zeitmessungen oder Ähnliches
- Sortierung macht einen erheblichen Teil der Gesamtarbeit aus, etwa 30 %
uutils ls ist bei den System Calls effizient, wird aber bei der Sortierung langsamer
- Schon die Einführung von
io_uring allein zeigt das Potenzial für bahnbrechende Leistungssteigerungen in IO-intensiven Umgebungen wie Servern
Fazit
- Die Entwicklungszeit war nicht besonders lang, und der Effekt der Syscall-Optimierung übertraf die Erwartungen
- lsr ist ein experimenteller
ls-Ersatz, der hohe Geschwindigkeit, wenige System Calls und kompakte Größe gleichzeitig erreicht
- Besonders geeignet ist es für Umgebungen mit großen Dateimengen oder Systeme, in denen leistungsstarke IO entscheidend ist
- Trotz gewisser funktionaler Einschränkungen wie fehlender Locale-Unterstützung zeigt es sowohl in der Praxis als auch in Benchmarks bemerkenswert innovative Ergebnisse
1 Kommentare
Hacker-News-Kommentare
Offenlegt, dass er der Autor des Projekts ist, und verweist darauf, dass ein Vorstellungsartikel zu dem auf io_uring basierenden
lsrhier zu finden istls(1)sei durch sein schlichtes Design sehr schnell gewesen, sei aber durch das Hinzufügen vieler Funktionen sowie VFS, verschiedener Zeichensätze, Farbunterstützung usw. durch die Summe kleiner Kosten langsamer geworden. Er hält das für eine interessante Diskussion über die Kosten von Abstraktion, die io_uring behandelttim(Vorstellungslink) zu messen, wäre vermutlich besser als mit hyperfine. Da es in Nim geschrieben ist, könnte das eine Hürde sein, aber dass die Namen sich ähneln, ist für einen Zufall lustigMich würde interessieren, wie sich lsr auf einem NFS-Server verhält, besonders in schlechten Netzwerkumgebungen. Es ist offensichtlich ein Nachteil des NFS-Designs, für instabile Netzwerkdienste blockierende POSIX-Syscalls zu verwenden. Es wäre auch interessant zu beobachten, inwieweit io_uring solche Probleme abmildern kann
ctrl+cin Apps, die gerade einen NFS-Ordner lesen, nicht funktioniert, ist eine bekannte Unannehmlichkeit. Theoretisch unterstützte die Mount-Option intr, Signale an laufende Operationen auf einem entfernten Server zu übergeben und sie damit abbrechbar zu machen, wurde unter Linux aber schon vor langer Zeit entfernt (derzeit bleibt nur die Option soft) (Referenz 1, Referenz 2 (FreeBSD-Unterstützung))Es ist interessant, dass die Zahl der Syscall-Aufrufe um das 35-Fache reduziert wurde, die Geschwindigkeitsverbesserung aber nur etwa beim Zweifachen liegt
Das Projekt ist für mich eher interessant als Beispiel für den Einsatz von io_uring mit erwarteten langfristigen Geschwindigkeitsgewinnen oder als Tutorial zur Verwendung, weniger aus praktischem Nutzen. Im Vergleich zu bestehenden Tools wie eza fehlte mir der intuitive Anreiz, warum man das braucht. Wenn das Auflisten von zehntausend Dateien 40 ms statt 20 ms dauert, würde ich den Unterschied bei einem einzelnen Lauf wohl überhaupt nicht bemerken
ls/dumehrere Minuten. Standardbefehle aus coreutils nutzen die Leistung moderner SSDs oft nicht richtig auslsr ist gut, aber bei Farbgebung und Icon-Unterstützung ist eza besser. Ich habe
eza --icons=always -1eingestellt, sodass Musikdateien (.opus usw.) automatisch mit Icons und Farben angezeigt werden, während sie in lsr einfach als normale Dateien erscheinen. Trotzdem merkt man deutlich, dass lsr leicht zu patchen und extrem schnell ist. Außerdem wäre es schön, wenn auchcatund andere Utilities auf diese Weise umgesetzt würden; auch die Nutzung von tangled.sh und atproto finde ich interessant. Da es in zig geschrieben ist, wirkt es auf Einsteiger zugänglicher als rustcat(direkt zu bat)Ich habe mich gefragt, warum nicht alle CLI-Tools io_uring verwenden. Wenn ich NVMe über USB 3.2 Gen2 anschließe, erreichen normale Tools bei mir 740MB/s, während aio- oder io_uring-basierte Tools bis auf 1005MB/s kommen. Ich vermute, dass auch Strategien für die Queue-Länge oder weniger Locking eine Rolle spielen
Mit strace lässt sich beobachten, dass lsd pro Datei etwa fünfmal
clock_gettimeaufruft. Die genaue Ursache ist unklar; vielleicht um für jeden Zeitstempel „vor x Minuten/Stunden/Tagen“ zu berechnen, oder es ist ein Vermächtnis der verwendeten Bibliothekclock_gettimekein echter Syscall mehr, sondern wird über vDSO abgewickelt (sieheman 7 vDSO). Vielleicht nutzt zig diese Struktur nichtEtwas off-topic vielleicht, aber ich würde gern reale Erfahrungswerte oder Benchmark-Zahlen dazu hören, um wie viele Mikrosekunden io_uring im Vergleich zu LD_PRELOAD den Socket-Latenz-Overhead in 10G-NIC-Umgebungen auf Enterprise-Servern wie Mellanox 4 oder 5 tatsächlich senkt. Es wirkt nicht so, als würden sich die Effekte addieren, daher wären konkrete Zahlen aus der Praxis interessant
io_uring unterstützt
getdentsnicht, daher zeigen sich die entscheidenden Vorteile bei Bulk-stat(z. B.ls -l). Es ist etwas schade, dass sichgetdentsnicht asynchronisieren und überlappen lässtreaddirplus-Operationen (getdents+stat) standardisieren würde, könnte das einen Teil des speziellen Vorteils von io_uring wieder aufhebenEs ist interessant, dass es Icons für die Erweiterungen .mjs und .cjs gibt, aber nicht für Dateiendungen wie .c, .h oder .sh