Il y a quelques années, j'ai déployé une appli Laravel sur une machine qui n'avait pas ext-mbstring, et rien n'a cassé. C'était justement ça, le problème. Composer avait tranquillement tiré symfony/polyfill-mbstring, toutes les fonctions de chaînes continuaient de marcher, et notre importeur CSV a passé les huit mois suivants à tourner nettement plus lentement que sur mon portable, parce que du PHP en userland faisait le travail qu'une extension C aurait dû faire. Personne n'a vu d'erreur. Il n'y en avait aucune à voir. Si j'en parle, c'est que l'équipe PyTorch vient de sortir la version 2.14 et que, enfouie sous 2 995 commits de 487 contributeurs, il y a une décision que j'aimerais encadrer et accrocher au-dessus du bureau de chaque mainteneur PHP.
La voici. PyTorch 2.14 publie des wheels pour Python 3.15, y compris la version free-threaded, mais torch.compile ne fonctionne pas encore sur 3.15. Plutôt que de te renvoyer discrètement en exécution eager et de laisser ton pas d'entraînement prendre deux fois plus de temps pendant que tu te demandes si c'est la faute du data loader, il lève une erreur. Tu as demandé du code compilé, tu ne peux pas l'avoir, et voici pourquoi. C'est toute la fonctionnalité, et je pense que c'est ce qu'il y a de plus mûr dans cette version.
C'est encore plus frappant quand tu lis le reste des notes, parce qu'une bonne partie des gains de performance mis en avant sur Apple Silicon consistent justement à nettoyer le schéma inverse. Sur le backend MPS, l'algèbre linéaire basculait sur le CPU sans prévenir personne, en payant des copies mémoire dans les deux sens. Dans la 2.14, SVD, eigh et lstsq tournent nativement sur le GPU en float32 et complex64, la factorisation LU a reçu des noyaux Metal sur mesure avec des accélérations supérieures à 100x sur de petites matrices en batch, et la CTC loss ne s'échappe plus vers le CPU. Il y avait aussi une forme de décodage à un seul token qui ratait le chemin rapide et coûtait un ralentissement de 8,5x en bf16 et fp16. Chacun de ces cas est un repli silencieux que quelqu'un a fini par devoir traquer avec un profileur. Les chiffres te disent combien une dégradation polie peut coûter avant que quiconque remarque qu'elle existe.
Maintenant, le contre-argument honnête, et il est solide. Les replis sont la raison pour laquelle un logiciel survit au contact des vraies machines. Le nouveau backend matmul NVGEMM de torch.compile se rabat sur Triton pour toute forme qu'il ne sait pas gérer, et c'est clairement la bonne chose à faire : tu as opté pour un chemin plus rapide, et le perdre sur une forme bizarre ne devrait pas faire tomber tout un run. Pareil pour le float64 sur Metal, qui tourne toujours sur le CPU parce que le matériel n'a pas de type flottant 64 bits. Personne ne veut d'un modèle qui plante sur un Mac parce qu'un tenseur est en double précision. Le polyfill qui m'a caché l'extension manquante a aussi gardé en ligne un site de prod chez un hébergeur que je ne contrôlais pas. Lent et fonctionnel vaut mieux que rapide et mort, la plupart du temps.
Alors, où placer la limite ? Je la mettrais au niveau de l'intention. Quand NVGEMM se rabat sur Triton, le système choisit entre deux noyaux compilés que tu as déjà acceptés, tous deux dans le mode que tu as demandé. Si torch.compile était silencieusement passé en eager sur 3.15, le système aurait jeté ce que tu avais explicitement demandé, et ça mérite un non bien franc. La même version rend visibles les avertissements de dépréciation de TorchScript, ce qui va dans le même sens : si le sol bouge sous tes pieds, le rôle de l'outil est de le dire, même si les logs deviennent plus moches pendant un moment.
PHP a les deux types de repli, et on les distingue rarement. Remplacer une extension C par un polyfill, ça va quand tu n'as jamais demandé l'extension. Charger le JIT d'opcache et le voir désactivé parce qu'une extension surcharge l'exécuteur, c'est un cas où tu l'as bel et bien demandé, et PHP 8 émet au moins un avertissement là-dessus, ce que je considère comme le bon réflexe. Ce que j'aimerais, c'est en voir davantage côté userland : des bibliothèques qui choisissent un chemin lent sans rien dire, des ORM qui retombent en requêtes N+1 quand une relation ne peut pas être chargée en eager loading, des drivers de cache qui se dégradent en stockage tableau quand Redis est injoignable et ne le signalent que dans un log de debug que personne ne suit. Chacun garde la requête en vie et te laisse un mystère de performance à résoudre plus tard, sur la deadline de quelqu'un d'autre.
La solution bon marché se trouve dans notre propre code plus que dans php-src. Les platform requirements de Composer te permettent déjà de déclarer ext-mbstring ou ext-intl comme obligatoires, et beaucoup de projets ne le font tout simplement pas. Une vérification au démarrage qui lève une exception quand le driver demandé n'est pas celui qui sert réellement le trafic, c'est une dizaine de lignes. PyTorch a passé un cycle de release à défaire des ralentissements cachés sur un backend et a choisi, sur un autre, d'échouer tôt. Ça coûte moins cher d'écrire l'erreur maintenant que de partir à la chasse au ralentissement plus tard.
Et toi, où places-tu cette limite dans ta stack ? Y a-t-il en ce moment dans ton appli un repli que tu préférerais transformer en exception, ou au contraire un repli dont tu es content qu'il soit resté silencieux parce qu'il t'a sauvé à un mauvais moment ? J'aimerais entendre parler des deux, surtout si tu fais tourner du PHP sur un hébergement que tu ne contrôles pas entièrement.




Commentaires
Pas encore de commentaire — écris le premier.
Lance la discussion
Pas de compte ni de mot de passe — saisis simplement ton adresse e-mail et nous t’envoyons un lien de connexion à usage unique. Première visite ? Tout se met en place automatiquement.
Ton évaluation sera appliquée automatiquement après ta connexion.
Vérifie ta boîte mail
Nous avons envoyé un lien de connexion à …. Ouvre-le sur cet appareil — cet onglet te connectera automatiquement.
Rien reçu ? Vérifiez le dossier spam — et marquez le message « Non spam » pour qu'il arrive directement la prochaine fois.