Scala 3 a-t-il ralenti nos performances ?
(kmaliszewski9.github.io)- Une régression de performance inattendue s'est produite lors de la migration de la base de code de Scala 2.13 vers Scala 3
- Dans les environnements de test et de déploiement initiaux, toutes les métriques étaient normales, mais quelques heures plus tard le lag Kafka a augmenté
- Les tests de charge ont confirmé une forte baisse de throughput lors du traitement de messages décomposés
- L'analyse avec async-profiler a révélé qu'un bug d'évaluation inefficace de chaînes dans la bibliothèque Quicklens était la cause
- Après la mise à jour de la bibliothèque, les performances ont été rétablies, en soulignant la nécessité de faire attention aux différences de comportement des bibliothèques entre versions de Scala
Processus de migration du service
- Le service existant a été migré de Scala 2.13 vers Scala 3.7.3
- Il s'agissait d'un service orienté collecte de données sans macros, avec des performances critiques
- Après avoir appliqué les dépendances, les options du compilateur, et les changements de types et de syntaxe, la compilation a réussi
- En environnement de test et lors du déploiement progressif, les logs et métriques sont tous normaux
- Les indicateurs aux niveaux infrastructure, JVM et application étaient également en bonne santé
Dégradation de performance d'origine inconnue
- Environ 5 à 6 heures après le déploiement, une hausse du lag Kafka est apparue
- Une baisse du throughput par instance s'est produite même sans pic de données
- Après rollback, le throughput s'est rétabli immédiatement, confirmant que le changement de code en était la cause
Analyse des performances et recherche de la cause
- En test de charge, la régression de performance n'a pas été reproduite au départ
- Une chute du throughput s'est produite uniquement lorsque les messages étaient décomposés et avec des payloads hétérogènes
- Les dépendances de bibliothèque (sérialisation, SDK DB, image Docker, bibliothèque de configuration, etc.) ont été testées en les rétablissant une par une, sans aucun changement
- Après exécution du profiling CPU avec async-profiler :
- En Scala 3, la part CPU du compilateur JIT et de l'étape de décodage a fortement augmenté
- Sur le flamegraph, les appels Quicklens représentaient près de la moitié du temps CPU total
- En Scala 2.13, le même appel ne représentait que 0,5 %
Cause racine
- Un bug d'évaluation inefficace de chaînes de la bibliothèque Quicklens se produisait sous Scala 3
- La correction associée est intégrée dans GitHub PR #115
- Après la mise à jour de la bibliothèque, la différence de performance entre Scala 3 et 2.13 a été résolue
Leçons et recommandations
- Une dépendance à la métaprogrammation des bibliothèques peut provoquer des écarts de performance entre versions de Scala
- Même quand une migration se termine correctement, il faut benchmarker les hotspots et les goulots d'étranglement
- Pour les services où la performance est critique, une validation basée sur des mesures réelles est indispensable au lieu de supposer que ça fonctionne
- Des vérifications préventives sont nécessaires pour éviter une situation où ce sont les benchmarks qui révèlent les goulots d'étranglement que le code ne montre pas
1 commentaires
Avis sur Hacker News
C’est comme ça qu’un blog technique devrait être écrit. Il sera difficile pour l’IA de remplacer un tel niveau de raisonnement
La première question était simple — « pourquoi faudrait-il faire cette mise à niveau ? »
inlinefonctionne comme une partie du système de macrosUtiliser
inlinesur un paramètre indique au compilateur d’inliner l’expression au point d’appelMais si cette expression est volumineuse, cela impose une lourde charge au compilateur JIT
Dans Scala 2,
@inlinen’était qu’une simple suggestion, alors qu’en 3 il est appliqué systématiquementDonc remplacer simplement
@inlineparinlineest une grosse erreurregisteren C/C++Au départ c’était contraignant, mais avec les progrès de l’optimisation, c’est devenu une simple recommandation, puis il a fini par être ignoré
Le
inlinede C++ a suivi une évolution comparableinlinepresque partoutNotamment pour supprimer le surcoût des lambdas dans des fonctions comme
mapIl y a eu peu de problèmes de performance, mais combiné au système de macros de Scala, cela peut produire des expressions complexes et poser problème
J’ai eu une expérience similaire lors du passage de Ruby 2 à 3
Il ne suffit pas de mettre à jour le langage : il faut actualiser l’ensemble des dépendances pour stabiliser le système
Les problèmes d’inférence de types de Scala 2 n’ont toujours pas été résolus, et à la place seul le langage a changé
C’est comme fabriquer un produit dont personne ne veut tout en ignorant la demande du marché
PS : il faut vraiment construire une vraie suite de tests unitaires pour le compilateur
Mais la réécriture de Scala 3 n’a pas résolu les problèmes de vitesse de compilation et de tooling, et a complètement fait perdre son élan au projet
Je me demande vraiment si quelqu’un lancerait un nouveau projet en Scala en 2025
Scala donne l’impression d’être un langage conçu par des universitaires, et le fait qu’il soit devenu brièvement populaire dans l’industrie paraît presque étonnant
Désormais tous les outils doivent s’aligner sur Scala 3, et même IntelliJ n’offre toujours pas un support parfait
Il aurait mieux valu améliorer Scala 2 progressivement, mais on a l’impression que l’accent a été mis uniquement sur le succès académique
Par exemple, il y a encore beaucoup de débats autour de
early return, comme dans l’article de tpolecat, alors que Kotlin le prend en charge sans aucun problèmeLe compilateur Scala possède des milliers, voire des dizaines de milliers de tests, et
le répertoire de tests officiel ainsi que le système de community build valident des millions de lignes de code
C’est indispensable surtout lors de gros changements comme une mise à niveau de version du langage
Nous benchmarkons en continu un outil écrit en C++, mais à cause du bruit de l’environnement, il est difficile d’obtenir des résultats cohérents
Nous réfléchissons à une méthode consistant à exécuter plusieurs fois sur la même machine pour comparer
Le problème, c’est d’avoir créé une deuxième syntaxe puis de l’avoir imposée comme l’avenir
Cela a aussi ralenti l’écosystème des outils
On peut aussi convertir automatiquement le style via le compilateur ou scalafmt
Maintenant, il y a le double : la syntaxe avec accolades et la syntaxe par indentation
La syntaxe
matchest beaucoup trop verbeuse et donne l’impression d’imiter PythonÀ l’époque, Scala était sous les projecteurs grâce à Spark, mais il a raté l’occasion d’évoluer en langage commercial majeur
Aujourd’hui, Spark se fait en Python, et la place des langages JVM modernes est occupée par Kotlin
Au final, Scala donne l’impression d’être redevenu un langage académique
Il suffit de regarder le Scala Adoption Tracker
Les nouvelles fonctionnalités de Scala 3 ont le potentiel de réinventer l’écosystème du langage
Par exemple : explication de Capture Checking
Java a absorbé une partie de l’attrait de Scala en ajoutant des fonctionnalités fonctionnelles
D’après mon expérience, la demande du marché reste minime
C’est surtout parce que Google a restreint la prise en charge de Java que cela s’est passé ainsi
Sur l’ensemble du marché JVM, sa part ne représenterait qu’environ 10 %
Si l’on subit des audits de sécurité (PCI-DSS, etc.), garder les bibliothèques à jour est indispensable
Pour ma part, je préfère plutôt conserver des dépendances anciennes
Les nouvelles versions apportent de nouveaux bugs, des changements de mainteneurs ou des risques de sécurité
Il semble qu’au début seule une partie ait été mise à jour. Procéder par petites étapes est courant, mais là ils ont simplement manqué de chance
Le problème ne venait pas de Scala 3 lui-même, mais de l’interaction de plusieurs facteurs
Mais il faut faire attention, car les bibliothèques spécifiques à Scala incluent souvent la version de Scala dans leur propre version
Ils mettaient bien en valeur l’expressivité de Scala et sa sécurité de typage
Comme dans l’article de Li Haoyi, le langage reste tout à fait séduisant comme alternative à Python
C’est particulièrement important lorsqu’il y a beaucoup de bibliothèques qui abusent de fonctionnalités magiques