- lsr est un nouveau programme de remplacement de
ls(1), développé à l’aide de la bibliothèque d’E/S basée sur io_uring, ourio
- Par rapport à
ls et aux outils alternatifs existants (eza, lsd, uutils ls), la vitesse d’exécution de la commande est extrêmement élevée, avec plus de 10 fois moins d’appels système
- Toutes les E/S majeures, comme l’ouverture de répertoires,
stat et lstat, sont traitées de façon asynchrone et par lots avec io_uring afin de maximiser les performances. Plus il y a de fichiers, plus c’est rapide
- Zig StackFallbackAllocator est utilisé pour minimiser les appels à
mmap lors des allocations mémoire
- Construit statiquement sans édition de liens dynamique, il a même un exécutable plus petit que
ls classique
Présentation et intérêt
- Le projet lsr est un outil rapide de listage de répertoires utilisant io_uring comme alternative à la commande
ls classique
- Comparé à
ls, eza, lsd et uutils ls, il affiche d’excellentes performances en vitesse d’exécution et en nombre d’appels système
- Il effectue directement autant d’E/S que possible via sa bibliothèque d’E/S développée en interne, ourio
- Les benchmarks montrent que lsr offre des performances et une qualité élevées même dans des environnements contenant un grand nombre de fichiers
Résultats des benchmarks
- Les temps d’exécution de chaque commande ont été mesurés avec
hyperfine dans un répertoire contenant n fichiers ordinaires
- Pour
lsr -al, sur des répertoires de 10 à 10 000 fichiers, il enregistre des temps d’exécution nettement plus courts que ls et ses alternatives
- Exemple : avec 10 000 fichiers, lsr atteint 22.1ms, soit la meilleure performance face à
ls (38.0ms), eza (40.2ms), lsd (153.4ms) et uutils ls (89.6ms)
- L’agrégation des appels système a été effectuée avec
strace -c
lsr -al conserve un nombre d’appels très faible, de 20 au minimum (n=10) à 848 au maximum (n=10 000)
ls monte jusqu’à 30 396 appels (n=10 000), lsd jusqu’à 100 512, et les autres alternatives restent elles aussi dans une fourchette de plusieurs milliers à plusieurs centaines de milliers
- Dans les mêmes conditions, lsr atteint la meilleure efficacité avec au moins 10 fois moins de syscalls
Structure et méthode d’implémentation de lsr
- Le programme fonctionne en trois étapes : analyse des arguments, collecte des données, sortie des données
- Toutes les E/S ont lieu pendant la deuxième étape, celle de la collecte des données, et tous les accès fichiers / récupérations d’informations possibles sont traités avec io_uring
- L’ouverture du répertoire cible,
stat, lstat ainsi que la récupération des informations de temps, d’utilisateur et de groupe sont tous effectués sur une base io_uring
- Le traitement par lots de
stat réduit drastiquement le nombre d’appels système
- Zig StackFallbackAllocator préalloue 1MB de mémoire pour minimiser les appels système supplémentaires comme
mmap
Build statique et optimisations
- Comme il s’agit d’un build entièrement statique, sans édition de liens dynamique avec libc, le surcoût à l’exécution est nettement réduit
- Face à GNU
ls, la taille du build ReleaseSmall de lsr est plus petite : 138.7KB contre 79.3KB
- En revanche, lsr ne prend pas en charge les locales (langue/région).
ls classique supporte plusieurs langues, ce qui entraîne un surcoût
Analyse des appels système et des problèmes de performances
lsd appelle clock_gettime plus de 5 fois par fichier ; la raison n’est pas claire (mesure interne du temps, etc., supposément)
- Le tri (sorting) représente une part importante du travail total (environ 30 %)
uutils ls est efficace en appels système, mais ralentit au niveau du traitement du tri
- L’adoption d’io_uring à elle seule montre un potentiel d’amélioration radicale des performances dans des environnements à fortes E/S, comme les serveurs
Conclusion
- Le temps de développement n’a pas été particulièrement long, et l’effet de l’optimisation des syscalls dépasse les attentes
- lsr est un remplaçant expérimental de
ls qui réunit rapidité, faible nombre d’appels système et taille compacte
- Il est particulièrement adapté aux environnements avec de très gros volumes de fichiers ou aux systèmes où les performances d’E/S sont cruciales
- Malgré certaines limites fonctionnelles, comme l’absence de support des locales, il montre des résultats innovants à la fois en pratique et en benchmark
1 commentaires
Avis Hacker News
L’auteur du projet se présente et indique qu’un billet de présentation de
lsrbasé sur io_uring est disponible icils(1)d’UNIX était extrêmement rapide grâce à une conception simple, mais il s’est ralenti à mesure que se sont accumulés de petits coûts liés à l’ajout de nombreuses fonctionnalités, au système de fichiers virtuel (VFS), aux différents jeux de caractères, à la prise en charge des couleurs, etc. Il trouve intéressante la discussion sur le coût d’abstraction traité par io_uringbfs -j1)tim(lien de présentation) semblerait meilleur qu’avec hyperfine. Comme c’est écrit en Nim, cela peut être un défi, mais la ressemblance des noms est amusante, même si elle est fortuiteOn se demande quelles seraient les performances de lsr sur un serveur NFS, surtout dans un environnement réseau médiocre. Il est évident qu’utiliser des syscalls POSIX bloquants avec un service réseau instable est une faiblesse de conception de NFS. Il serait intéressant d’observer dans quelle mesure io_uring atténue ce problème
ls, etc.) n’aient pas à gérer eux-mêmes les erreurs réseau. À l’origine, le protocole NFS ne conservait pas d’état, ce qui permettait au client de se rétablir automatiquement après un redémarrage du serveur. On se demande si io_uring remonte correctement les erreurs dans ce type de cas. La manière dont les timeouts NFS sont gérés est également un point d’intérêt$HOMEsur NFS depuis plusieurs PC, et tant que le réseau est bon et qu’on évite les cas difficiles comme les écritures parallèles, l’utilisabilité moyenne de NFS est plutôt satisfaisante. En revanche, il a déjà eu des difficultés à cause de déconnexions lorsque le câble réseau était instablectrl+cne fonctionne pas dans une application lisant un dossier NFS est un inconvénient bien connu. En théorie, l’option de montageintrpermettait d’interrompre une opération en cours sur un serveur distant en lui transmettant un signal, mais cette option a été retirée de Linux il y a longtemps déjà (aujourd’hui seule l’optionsoftexiste encore) (réf.1, réf.2 (prise en charge FreeBSD))Il est intéressant de constater que, même avec 35 fois moins d’appels système, l’amélioration de vitesse n’est que d’environ 2x
Ce projet attire davantage l’attention comme démonstration des gains de vitesse à long terme qu’on pouvait espérer d’io_uring, ou comme tutoriel sur son usage. Par rapport à des outils existants comme eza, il n’y avait pas de motivation intuitive claire expliquant pourquoi il en fallait un nouveau. Si lister dix mille fichiers prend 40 ms contre 20 ms, on a l’impression qu’à l’échelle d’une exécution isolée la différence est imperceptible
ls/dupeut prendre plusieurs minutes. Les commandes de base de coreutils n’exploitent souvent pas pleinement les performances des SSD moderneslsr est bien, mais eza reste supérieur pour la coloration et la prise en charge des icônes. Il utilise personnellement
eza --icons=always -1, ce qui fait que les fichiers audio (.opus, etc.) apparaissent automatiquement avec des icônes et des couleurs, alors que dans lsr ils sont affichés comme de simples fichiers. Cela dit, lsr donne clairement l’impression d’être facile à patcher et extrêmement rapide. Il aimerait aussi voircatet d’autres utilitaires réalisés de cette façon, et trouve intéressante l’utilisation de tangled.sh et d’atproto. Le fait que ce soit écrit en Zig lui paraît aussi plus accessible que Rust pour un débutantbatest un remplaçant moderne decat(voir bat)lsproduit de jolies couleursIl se demandait pourquoi tous les outils CLI n’utilisent pas io_uring. Dans son cas, lorsqu’il connecte un NVMe en USB 3.2 gen2, un outil ordinaire atteint 740 MB/s, alors qu’un outil basé sur aio ou io_uring monte jusqu’à 1005 MB/s. Il pense qu’il y a aussi un effet de stratégie de profondeur de file et de réduction des verrous
#ifdef, ce qui ralentit l’adoption de nouvelles technologies spécifiques à une plateforme ou à une version. Il estime qu’aujourd’hui l’avantage de compatibilité entre diverses plateformes de type POSIX est moins important qu’autrefoisAvec strace, on observe que lsd appelle
clock_gettimeenviron cinq fois par fichier. La cause exacte n’est pas claire ; c’est peut-être pour calculer, pour chaque horodatage, un affichage du type « il y a x minutes/heures/jours », ou bien à cause d’un héritage de bibliothèqueclock_gettimen’est plus un vrai syscall mais passe par le vDSO (voirman 7 vDSO). Il se demande si Zig n’exploite peut-être pas cette architectureC’est peut-être un peu hors sujet, mais il serait curieux d’avoir des retours d’expérience ou des chiffres de benchmark concrets sur la réduction, à l’échelle de la microseconde, de la latence des sockets grâce à io_uring par rapport à LD_PRELOAD dans un environnement 10G NIC sur des serveurs très orientés entreprise comme les Mellanox 4 ou 5. Les effets ne semblent pas se cumuler, et s’il y a des retours directs, il aimerait entendre des chiffres
io_uring ne prend pas en charge
getdents, donc son principal avantage se voit sur les stat en masse (par ex.ls -l). Il est un peu regrettable qu’on ne puisse pas rendre le traitement degetdentsasynchrone ni le recouvrir avec d’autres opérationsgetdents+stat), une partie des avantages propres à io_uring serait probablement neutraliséeIl trouve amusant qu’il existe des icônes pour les extensions
.mjset.cjs, mais pas pour des extensions comme.c,.hou.sh