20 points par carnoxen 2024-11-27 | 11 commentaires | Partager sur WhatsApp

Résumé

C’est un texte écrit après avoir observé le conflit entre les développeurs Rust et les développeurs Linux historiques. Différents développeurs peuvent certes avoir chacun leur propre style de code, mais le projet Linux a déjà écarté C++ par le passé pour éviter son style de code et sa structure (RAII).

Le fonctionnement du code mentionné par Asahi Lina est beaucoup trop lent à l’arrêt du programme, et va à l’encontre du traitement par lots, qui est l’approche la plus fondamentale pour créer des logiciels orientés performance. Par exemple, effectuer un traitement par lots à l’aide de régions mémoire permet d’aligner plusieurs durées de vie en une seule, ce qui rend RAII inutile.

Voici des ressources à l’appui de mon argumentation. Elles expliquent toutes pourquoi le traitement par lots est bénéfique :

Je pense donc que Linux ne devrait jamais adopter RAII.


Si j’ai apporté ce texte ici, c’est parce que j’ai vu à plusieurs reprises des développeurs Rust coréens se montrer très en colère en le lisant, et je me demandais donc ce que les gens ici en penseraient. Qu’en pensez-vous ?

11 commentaires

 
techiemann 2024-12-03

C’est mon point de vue, mais je comprends dans une certaine mesure l’élitisme de certains développeurs. Du point de vue de l’« ingénierie » logicielle, il est difficile de trouver un « logiciel » qui, comme Linux, a aujourd’hui aidé les progrès de la philosophie open source en collaborant largement, y compris avec le camp du closed source, dans l’écosystème open source. Dès lors, n’adoptent-ils pas une attitude conservatrice, encore plus exclusive et paraissant même luddiste, par crainte que des programmeurs non éprouvés, portés par Rust, n’affluent comme une marée, ajoutent de manière désordonnée du code échappant au contrôle du noyau central des mainteneurs des projets existants, fassent exploser la dette technique et raccourcissent ainsi le cycle de vie de Linux ?

Il est intéressant de voir qu’on adopte une attitude peu « ouverte » pour que l’open source puisse rester longtemps de l’open source.

 
nullbus 2024-12-02

Moi aussi, j’utilise souvent et recommande des formes de gestion des ressources comme le RAII. Même quand on ne sait pas vraiment ce qu’est le RAII et qu’on l’utilise machinalement, on obtient malgré tout du code « au moins sûr ».

En revanche, si on ne l’utilise pas en comprenant bien son fonctionnement, on risque facilement de produire en masse du code inefficace, du genre à ouvrir et fermer des fichiers des dizaines de fois alors qu’une seule ouverture suffirait. Si les développeurs gardent en permanence un œil sur les performances et que cette culture est bien ancrée dans l’équipe, je pense qu’on peut obtenir des performances tout à fait suffisantes même avec le RAII.

 
aer0700 2024-11-30
  1. RAII : exécuter free à chaque destruction d’objet
  2. Exécution groupée ? Rassembler ce qu’il faut free, puis l’exécuter en bulk ?
    Sous Linux, existe-t-il une fonctionnalité ? Une API, par exemple, qui permette à 2 de s’exécuter plus vite que 1 ?
    J’ai toujours naturellement vécu avec 1, donc j’ai du mal à bien comprendre.
 
kandk 2024-11-29

Je n’ai pas envie de revenir à une expérience de développement où, une fois tout le code terminé, on cherche les fuites de mémoire avec valgrind.

 
kandk 2024-11-29

Je ne le sais pas exactement, mais refuser d’utiliser le RAII semble vouloir dire qu’on compte améliorer les performances de fermeture en s’appuyant sur des fuites mémoire intentionnelles ; je ne sais pas si c’est vraiment la bonne direction.
De toute façon, un développeur capable de bien gérer la mémoire manuellement saura aussi bien utiliser le RAII, et un développeur incapable de développer sans RAII ne saura pas non plus gérer la mémoire manuellement ; je ne vois donc pas de raison de ne pas utiliser le RAII.

 
foriequal0 2024-11-29

Je me demandais combien de temps free consomme, alors j’ai écrit un petit test — certes très différent d’une charge réelle. (Compilé en release Rust, avec std::alloc::alloc et std::fs::File.)
J’ai alloué 10 000 000 blocs mémoire de tailles variées, soit environ 2,5 Go au total, puis j’ai mesuré uniquement le temps de libération : cela a pris 1,87 seconde. Soit 187 ns par bloc.
À l’inverse, pour les fichiers, j’ai ouvert seulement environ 10 000 handles, puis mesuré uniquement le temps nécessaire pour les fermer : cela a pris environ 9 secondes. Cela revient à 900 µs par fichier.
(Ce PC Windows est particulièrement lent sur les opérations de fichiers, peut-être à cause de l’antivirus. Sur un autre portable Windows, j’obtenais respectivement 400 ns / 200 µs, et sur un autre PC Linux 50 ns / 600 ns.)

Comme alternatives au RAII, on évoque souvent le traitement en masse, ou encore le fait de faire confiance à l’OS à la fin du processus et de laisser fuiter les ressources. Pour la mémoire, cela semble facile à faire.
En revanche, pour des ressources comme les fichiers ou les sockets, je n’ai jamais vu d’API de récupération en masse, et si on laisse fuiter les ressources, même si le temps côté code utilisateur diminue, le noyau récupère exactement ce coût au moment de terminer le processus. Le gain de performance est donc faible.

Le RAII pour la mémoire n’est pas si lent que ça, n’empêche pas non plus d’utiliser des arenas, et n’interdit pas non plus les fuites intentionnelles si besoin ; il me semble donc difficile d’en faire une raison d’éviter le RAII.
Et pour le RAII sur les fichiers, qui est plus lent, dans une situation où il n’existe ni moyen de traiter cela en masse ni moyen d’éviter ce coût, je me demande à quel point les alternatives au RAII sont réellement meilleures.


C’est un peu à côté du sujet, mais j’ai l’impression que les objections au RAII et aux lifetimes se limitent souvent aux seules ressources mémoire, représentées par malloc/free.
Or RAII et les lifetimes sont utiles bien au-delà de l’allocation mémoire : non seulement pour les ressources OS comme les fichiers, sockets ou verrous, mais aussi pour des modèles de ressources comme les object pools ou les connection pools, bref pour la plupart des ressources qui impliquent acquisition et restitution, avec contrôle d’accès exclusif pendant la période de possession.

Ces ressources partagent elles aussi une structure comparable à malloc/free, et partagent donc les mêmes familles de problèmes : fuites, use-after-free, double free, etc.
Et justement parce qu’elles partagent cette même structure, RAII et les lifetimes ne résolvent pas seulement les problèmes mémoire, mais aussi ceux de toutes ces autres ressources ; je pense que cet aspect mériterait d’être davantage mis en avant.
Par exemple, en Rust, on empêche aussi à la compilation les use-after-close et double close sur les handles de fichiers :
https://play.rust-lang.org/?version=stable&mode=debug&edition=…

Les principaux langages à GC gèrent bien la mémoire via le GC, mais pour des ressources comme les file handles ou les sockets, dont la gestion doit rester déterministe, ils finissent malgré tout par introduire en plus des mécanismes de type RAII (comme try-with-resources en Java, using en C#, ou with en Python) ou des mécanismes proches (comme defer en Go).
On se retrouve donc avec plusieurs modes de gestion des ressources dans un même langage, et je me dis que ce n’est peut-être pas aussi bon que cette approche.

 
crosh 2024-11-28
  1. Il est exact que Linux a évité la RAII en excluant le C++, mais cela ne suffit pas à étayer l’idée que Linux a évité le C++ « pour éviter la RAII ». Le courrier de Linus que vous avez cité ne contient pas le terme RAII.
  2. Pour être honnête, à partir du paragraphe qui commence par « Asahi Lina… », tous les liens fournis renvoient à des vidéos YouTube de plusieurs dizaines de minutes, donc ils sont difficiles à consulter. Si vous pouviez donner des exemples techniques concrets, fondés sur des cas où la RAII n’est pas nécessaire, la discussion pourrait sans doute être plus dynamique.

Par exemple, utiliser une zone mémoire pour effectuer des traitements par lot permet de gérer plusieurs durées de vie comme une seule, ce qui rend la RAII inutile.

Si vous voulez parler d’une arène, Rust dispose bien sûr aussi d’arènes, et il est également possible, grâce aux lifetimes, d’interdire l’accès aux éléments de l’arène après sa « libération en bloc » résultant de la disparition de l’arène. Veuillez consulter https://crates.io/keywords/arena.

 
ztaka 2024-11-28

J’aimerais qu’il y ait beaucoup d’autres langages, même après Zig et Rust. Mais jusqu’à présent, je n’ai pas encore vu de langage aussi pertinent que Rust. Je pense plutôt que les connaissances entre développeurs qui émergent de ces discussions entre langages sont utiles. Haha..

 
shoyuvanilla 2024-11-28

Je suis moi aussi développeur, et j’utilise Rust comme langage principal à ma façon ; cela ne m’a pas mis en colère, mais j’ai tout de même eu l’impression qu’on allait chercher un exemple un peu extrême (en disant que « le programme est trop lent à se fermer », et même dans la vidéo liée, il s’agit d’un cas sans lien direct avec un projet Rust : la fermeture de Visual Studio prend trop de temps parce que les destructeurs de chaque composant individuel sont appelés).

Lorsqu’il est nécessaire, pour des raisons de performance, de traiter en une seule fois le nettoyage de plusieurs composants, on peut sans doute choisir non pas d’implémenter Drop pour chaque composant individuellement, mais d’implémenter Drop sur le type qui porte la durée de vie de ces composants afin d’effectuer le nettoyage en bloc. Ce serait encore mieux avec une protection empêchant de créer ces composants autrement que via l’API de ce type.

Bien sûr, la crainte de l’auteur de ce texte semble être que si la pratique consistant à utiliser la RAII entre dans la base de code de Linux, du code présentant des inquiétudes de performance très implicites s’accumule à long terme au sein d’une base de code immense et complexe, au point de provoquer un jour quelque chose de similaire à Visual Studio ; je pense que c’est effectivement une inquiétude tout à fait légitime. Cela dit, comme cela a été mentionné dans d’autres commentaires, la RAII apporte aussi de la sûreté ; à mon avis, le choix relève donc en partie d’un compromis.

 
cosine20 2024-11-28

Les deux côtés disent des choses justes.

Pour prendre une analogie, dans le jeu en ligne LoL, le personnage Azir est perçu comme un champion de très haut tier, avec un split push, un contrôle de zone en teamfight et une valeur d’ultime écrasants, mais cela ne vaut que dans des matchs professionnels à très haut niveau de maîtrise ; au niveau du joueur moyen, sa phase de lane est bien trop faible et son power level général aussi, si bien qu’il n’est qu’un champion de tout dernier tier.

Du point de vue de personnes comme Asahi Lina, qui font partie des 10 % supérieurs en connaissances de programmation et de systèmes d’exploitation, des alternatives à RAII seront évidemment préférables, mais sur le terrain que manipulent les 90 % restants, je pense qu’il n’y a rien de mieux que RAII ou Rust.

Cela dit, comme l’une des grandes raisons d’exiger la memory safety / sûreté mémoire est la sécurité... je pense que ce trade-off est inévitable.

 
joonhwan 2024-11-28

Sans RAII, j’ai l’impression que des développeurs relativement moins expérimentés risquent de produire des bugs à la chaîne

au niveau des applications, et pas de l’OS, au moins...