Performances IA insuffisantes des AI PC : le CPU dépasse le NPU
(github.com/usefulsensors)- L’exécution d’un benchmark NPU sur le SoC Qualcomm basé sur Arm de la Microsoft Surface Pro 11th Edition montre un débit nettement inférieur aux performances d’accélération mises en avant par les Windows AI PC
- Le test est conçu pour exécuter de grandes opérations de MatMul similaires aux couches coûteuses des modèles de type transformer, en utilisant Onnx Runtime et le fournisseur d’exécution Qualcomm QNN
- Dans un exemple de résultat, le CPU atteint 821 gigaops/s, le NPU 225 gigaops/s avec une configuration quantifiée à entrées/sorties flottantes, et 573 gigaops/s avec une configuration appliquant aussi des entrées/sorties 8 bits
- Les 573 gigaops/s du NPU ne représentent que 1,3 % des 45 teraops/s annoncés dans les supports marketing de la Microsoft Surface Pro 11th Edition ; en exécutant le même modèle sur un GPU Nvidia Geforce RTX 4080 Laptop, on obtient 3,2 ms et 2 160 gigaops/s
- Ces résultats valent au 2 octobre 2024 ; des améliorations logicielles, framework ou pilote pourraient réduire la latence, mais dans ce benchmark le CPU reste plus rapide que le NPU
Objectif du benchmark du NPU Qualcomm sur Surface
- Microsoft commercialise les tablettes Surface équipées de SoC Qualcomm Arm comme des AI PC Windows, en affirmant qu’elles peuvent exécuter les modèles de machine learning plus vite et plus efficacement
- Useful Sensors a une vision positive du matériel Qualcomm et du NPU, et a investi du temps et des ressources pour porter son application tierce sur cette plateforme
- Comme il existe peu d’exemples de code ou de benchmarks montrant comment des développeurs externes peuvent obtenir rapidement de bons résultats, l’entreprise publie les performances réellement observées dans un petit projet indépendant
- Les performances mesurées se sont révélées bien inférieures aux attentes ; comme le même matériel a déjà bien fonctionné sur d’autres plateformes comme Android, l’auteur n’exclut pas des améliorations futures via des changements côté application, framework ou pilote
Environnement d’exécution et contraintes d’installation
- Le test utilise un script Python sous Windows
- Au 2 octobre 2024, le Python du Microsoft Store ne prend pas en charge l’architecture Arm et ne convient donc pas à l’exécution des paquets nécessaires pour accéder au NPU Qualcomm
- Les résultats ont été obtenus avec l’installateur Python 3.11.9 Arm64
- Comme il n’existe pas encore de paquet Onnx précompilé pour Windows on Arm, CMake et le compilateur Visual Studio sont nécessaires
- CMake s’installe avec
winget install cmake - Pour Visual Studio, l’édition Community est utilisée, avec la charge de travail
Desktop C++ Development
- CMake s’installe avec
- Les paquets Python s’installent dans le dossier du dépôt via
py -m pip install -r requirements.txt- La branche Onnx est une version où le correctif de compilation du lanceur officiel
pya été rétropoté vers Onnx 1.16 - Le runtime Qualcomm Onnx Runtime renvoie une erreur
Unsupported model IR versionavec les versions récentes d’Onnx, d’où ce choix de combinaison - Le paquet Qualcomm Onnx Runtime utilisé est une build nightly
- La branche Onnx est une version où le correctif de compilation du lanceur officiel
Exécution du benchmark et interprétation de la sortie
- Le benchmark s’exécute avec
py benchmark_matmul.py - Onnx Runtime affiche beaucoup de logs lors de l’initialisation
- Par exemple, un message de
cpuinfoindique qu’il ne reconnaît pas le modèle de puceSnapdragon(R) X 12-core X1E80100 @ 3.40 GHz - Des logs apparaissent aussi lors de la finalisation du graphe et de l’achèvement
- Par exemple, un message de
- Les véritables résultats du benchmark s’affichent à la fin
NPU quantized compute, float I/O accuracy difference is 0.0100NPU quantized compute and I/O accuracy difference is 0.0060CPU took 8.42ms, 821,141,860,688 ops per secondNPU (quantized compute, float I/O) took 30.63ms, 225,667,671,183 ops per secondNPU (quantized compute and I/O) took 12.05ms, 573,475,650,364 ops per second
- Les deux premières lignes montrent la différence de précision pour vérifier que les résultats numériques du CPU et du NPU correspondent
- Les trois dernières indiquent le temps mur pour exécuter le modèle de bout en bout, ainsi que le nombre d’opérations par seconde calculé à partir de cette latence
Modèle mesuré et méthode de quantification
- Le benchmark est conçu pour reproduire six grandes multiplications de matrices similaires aux couches les plus coûteuses en temps dans des modèles transformer comme OpenAI Whisper
- La forme des entrées est
(6, 1500, 256) X (6, 256, 1500) - La forme du résultat est
(6, 1500, 1500) - Le modèle se compose d’un unique nœud
MatMulavec 2 entrées et 1 sortie
- La forme des entrées est
- Le modèle est généré à la volée dans le framework de modèle Onnx, puis transmis à Onnx Runtime
- Le modèle de référence est une version purement flottante, exécutée uniquement sur CPU
- Pour être exécuté efficacement, le NPU nécessite surtout des modèles quantifiés ; le float16 n’est pris en charge que de façon limitée
- La première approche côté NPU utilise la méthode officielle ORT
quantize_static()- Par commodité, les tenseurs d’entrée et de sortie restent en float 32 bits
- Le runtime effectue des conversions au début et à la fin du graphe, tandis que le reste des calculs s’exécute en 8 bits
- Dans cette configuration, les opérations de conversion du NPU sont très lentes, et dans
npu_quant_profile.csvelles représentent plus de 75 % du temps total - La seconde approche construit par programme un graphe de modèle équivalent avec des entrées et sorties 8 bits
- Cette méthode
quantized compute and I/Oest généralement environ 3 fois plus rapide que la version à I/O flottantes - Le profiling montre que l’essentiel du temps est alors bien consacré à la multiplication de matrices
- Cette méthode
Variables prises en compte dans la mesure des performances
- La possibilité d’un comportement compute bound a été prise en compte en choisissant des formes de matrice plus proches du carré
- Les modèles transformer modernes, contrairement aux anciens modèles convolutionnels, reposent sur de grandes multiplications de matrices
- Quand une couche se rapproche d’un produit matrice-vecteur, la réutilisation des poids diminue et l’accès aux valeurs en DRAM peut devenir le goulot d’étranglement
- La dimension
kde la matrice originale de tiny Whisper était 64, mais elle a été portée ici à 256 pour élargir la marge d’optimisation SIMD
- Les réglages d’alimentation ont été orientés vers la performance
- Tous les paramètres d’énergie de Windows ont été placés sur
Best Performance - Le benchmark a été exécuté avec la tablette branchée au secteur
- L’option de session
htp_performance_modede Qualcomm Onnx Runtime a été réglée sursustained_high_performance, car c’est celle qui donnait la plus faible latence totale lors des essais
- Tous les paramètres d’énergie de Windows ont été placés sur
- La structure du modèle a été limitée à une seule multiplication de matrices pour faciliter l’interprétation
- Plusieurs couches, des convolutions ou des poids statiques auraient été possibles, mais un unique MatMul avec entrées dynamiques a été choisi pour refléter la structure transformer largement utilisée dans les LLM et les modèles modernes
- La possibilité d’une erreur de configuration reste ouverte
- Une quantification en 8 bits non signés et des éléments
qdqdans le graphe sont utilisés - Même en essayant de suivre les bonnes pratiques de la documentation, il reste possible d’être sorti du chemin rapide prévu par le pilote ou l’implémentation de l’accélérateur
- Une quantification en 8 bits non signés et des éléments
- Les options d’API Windows pour accéder à l’accélération IA ont aussi été examinées
- DirectML semble ne prendre en charge que l’accès au GPU
- OpenVino semble ne pas fonctionner sur ce matériel Arm
- L’utilisation directe du SDK Qualcomm QNN a donné des résultats de performance similaires
- TensorFlow Lite ne prend pas en charge Windows for Arm
- Dans cette enquête et ces essais, Onnx apparaît comme le framework le plus adapté pour obtenir des performances accélérées par NPU, avec le soutien à la fois de Microsoft et de Qualcomm
Interprétation des résultats
- Les résultats correspondent au 2 octobre 2024 et ont été mesurés sur une Microsoft Surface Pro 11th Edition
- Le SoC est un
Snapdragon(R) X 12-core X1E80100 @ 3.40 GHz
- Le SoC est un
- Même en excluant les conversions float, les résultats du NPU restent inférieurs à ceux du CPU
- Du point de vue d’un accélérateur, ce n’est pas idéal
- En revanche, d’éventuels avantages en efficacité énergétique ou en performances soutenues ne sont pas exclus
- La meilleure performance mesurée du NPU, soit 573 milliards d’opérations/s, ne représente que 1,3 % des 45 billions d’opérations/s annoncés dans les supports marketing de la Microsoft Surface Pro 11th Edition
- En exécutant le même modèle sur un GPU Nvidia Geforce RTX 4080 Laptop, il faut 3,2 ms
- Cela correspond à 2 160 milliards d’opérations/s
- Soit un débit presque 4 fois supérieur à la mesure obtenue sur le NPU de la Surface
2 commentaires
Je pensais que le NPU des Ryzen était un peu plus rapide que le CPU, mais ça m’a complètement coupé l’envie du Snapdragon.
Avis sur Hacker News
À voir les résultats, il semble globalement que les ressources de calcul soient mal exploitées. 8,4 ms sur CPU et 3,2 ms sur GPU, l’écart est beaucoup trop faible ; ici, on aurait sans doute pu s’attendre à un facteur 10 à 20
La cause pourrait être onnxruntime. Certains fabricants de matériel semblent sortir des unités de calcul sans encore fournir de prise en charge correcte ; il faudra voir à quelle vitesse cela évolue
Par ailleurs, beaucoup comprennent mal l’objectif d’un NPU en le résumant à la « vitesse », alors que le cœur du sujet est la faible consommation. Si l’on vise la vitesse, il faut supprimer le goulot d’étranglement mémoire, ce qui finit par conduire à concevoir un ASIC avec sa propre mémoire. Dans la plupart des appareils, le NPU est rattaché au SoC autour du CPU et sert à décharger les calculs d’IA
Il serait intéressant de faire tourner ce benchmark en boucle infinie sur les trois dispositifs CPU/NPU/GPU et de mesurer la consommation. Je m’attendrais à ce que le NPU soit le plus sobre et offre le meilleur nombre d’opérations par watt
Par exemple, avec un filtre photo IA, le GPU est probablement occupé à rendre l’aperçu, tandis que le CPU gère l’UI et les entrées utilisateur
Apple Accelerate était pratiquement le seul moyen d’accéder aux unités matricielles CPU pré-M4 et au NPU. Si vous voulez utiliser autre chose, il faut être prêt à proposer des patchs au framework de machine learning choisi, ou à écrire vous-même le code d’entraînement et d’inférence
Au final, Python appelle bien du C lui aussi, mais il est assez intéressant de voir combien de performance disparaît au passage
Si l’on utilise le GPU, il faut se demander si le timing inclut les opérations asynchrones
Avec une utilisation naïve de
time.time(), le CPU ne fait qu’enregistrer l’heure ;model(input.cuda()).cuda()envoie les données dans la mémoire GPU et lance le calcul, mais comme c’est asynchrone, l’heure de fin peut être enregistrée indépendamment du fait que le résultat soit réellement prêt ou nonC’est un comportement difficile à anticiper si l’on ne connaît pas le système et le matériel. Ce n’est pas propre à Python : la plupart des langages sont conçus pour que le code écrit soit compilé sous une forme plus optimisée, et en l’absence de verrou, cela ne bloque pas le travail du CPU
Pour mesurer réellement le travail du GPU, il faut regarder les timers d’événements CUDA. Dans PyTorch, cela se fait avec
torch.cuda.Event(enable_timing=True)En plus, la taille et le format mémoire compliquent les choses. Ce benchmark utilise une forme défavorable au NPU. Les NPU et GPU veulent généralement du channels last, donc
[1,1500,1500,6]est plus approprié que[1,6,1500,1500]1500 et 6 sont aussi des nombres peu commodes et défavorables au NPU ; vu que ces dispositifs sont encore nouveaux, la perte de performance peut être assez importante
J’ai donné plus de détails ici : https://news.ycombinator.com/item?id=41864828
Ces NPU occupent une surface de silicium considérable, donc ce serait vraiment dommage s’ils finissaient par être peu utilisés. Je n’ai pas trouvé d’analyse du die du Snapdragon X isolant uniquement le NPU, mais du côté d’AMD, avec un objectif similaire d’environ 50 TOPS, on peut voir ici qu’il occupe une surface équivalente à environ trois cœurs CPU hautes performances
https://www.techpowerup.com/325035/amd-strix-point-silicon-p...
Pire encore, la plupart des gens ne semblent pas en avoir besoin non plus, et des enquêtes récentes montrent même une opposition majoritaire à l’infiltration de l’IA. On ne devrait pas payer un surcoût pour ça ; cela devrait être optionnel
Ainsi, les ventes montreraient combien peu de gens veulent payer une prime « IA », et cela rendrait évident à quel point c’est exagéré et inutile
Le but de ce genre de pièce est donc d’être utilisée, mais pas trop
À la place du NPU, on aurait pu employer ces transistors et cette surface de die pour plusieurs autres usages, mais on n’aurait pas ajouté davantage de cœurs CPU hautes performances. Cela augmenterait trop la densité de puissance et créerait des problèmes thermiques difficiles à résoudre sans throttling permanent
[1] https://en.wikipedia.org/wiki/Dark_silicon
Pour ceux qui ne lanceront pas de modèles, cela ressemble à du gaspillage, et je me demande à quels autres usages cela pourrait servir
Les logiciels grand public ne passent pas vraiment à l’échelle à ce niveau ; je me demande donc ce qu’on pourrait faire en allouant davantage de transistors au CPU
C’est aussi proche de la raison pour laquelle Apple met beaucoup de moteurs vidéo dans ses SoC. Avec le budget de transistors disponible, il n’y a pas tant d’autres endroits pertinents où les utiliser. Les performances monothread ne sont plus limitées uniquement par le nombre de transistors, et les logiciels gèrent mal le multithreading
Je pensais que l’objectif de ce genre de dispositif n’était pas d’être rapide, mais de faire tourner de petits modèles avec une consommation très faible. J’utilise un portable AMD récent doté d’un NPU : même en activant les effets vidéo censés tourner sur le NPU, la consommation ne change pas, alors qu’elle augmente avec Nvidia Studio Effects.
Le NPU semble destiné à des modèles très optimisés pour de petites tâches comme le contact visuel, le floutage d’arrière-plan, les modèles de correction automatique, la transcription ou l’OCR. Sous Windows en particulier, j’avais compris qu’il faisait tourner de l’OCR plein écran et des embeddings pour la recherche, pour la fonction rewind.
AMD fait un excellent travail ces derniers temps, mais ne semble pas vraiment le mettre en avant. Ceci est particulièrement intéressant : https://lore.kernel.org/lkml/DM6PR12MB3993D5ECA50B27682AEBE1...
Modification : ce n’était pas un FPGA. J’ai appris quelque chose aujourd’hui.
On peut le vérifier en évaluant des modèles CoreML sous macOS. L’ANE prend environ deux fois moins de temps que le GPU, et le GPU environ deux fois moins que le CPU. Les ratios réels varient selon le modèle.
La puce qui menacera Nvidia sera une puce, avec sa mémoire, assez bon marché pour faire tourner des modèles suffisamment bons sur des appareils personnels comme les smartphones.
Si le grand public reconnaît l’utilité des LLM et accepte de payer un petit supplément sur le prix des appareils, l’avenir de cette technologie me semble être celui de modèles personnels offrant intrinsèquement une protection de la vie privée.
La quantité d’informations personnelles que les gens déversent dans des services comme ChatGPT est stupéfiante. Sur Reddit, on voit souvent des personnes accros aux apps de petite amie virtuelle IA confier leurs goûts les plus sombres, des aveux vulnérables, voire des conversations potentiellement criminelles, à des éditeurs d’apps anonymes.
Google indique aussi explicitement que, si l’historique Gemini est activé, le contenu des conversations peut être examiné.
Pour des prédictions de tokens complexes nécessitant des modèles plus grands, on pourra toujours interroger un LLM dans le cloud, mais pour les consommateurs, la protection de la vie privée doit être garantie.
Pour un assistant personnel du quotidien, le chat et la recherche d’informations, je ne pense pas qu’il faille un raisonnement de pointe ni des LLM démonstratifs.
En revanche, je suis un peu sceptique sur les conclusions précises de l’article. Il s’agit d’ONNX chez Qualcomm, et cela peut être ancien. Côté Android, on critiquait beaucoup l’ingénierie logicielle de Qualcomm.
La direction générale reste néanmoins la bonne. La plupart des affirmations sur l’accélération IA dans le matériel grand public relèvent presque universellement du bluff, sauf si A) on utilise du logiciel 1P, ou B) quelqu’un en interne chez le 1P veut vraiment que vous exploitiez cette fonctionnalité.
Même avec quelque chose comme TensorRT, ce ne sera pas aussi rapide qu’une implémentation écrite directement dès le départ, et il y a une raison pour laquelle Nvidia y consacre autant de ressources humaines. Cela dit, on s’en rapproche assez et cela réduit beaucoup le temps de développement.
Ces dispositifs sont généralement optimisés pour des tâches similaires et répétitives. Il se peut donc qu’une partie des informations collectées ici soit inexacte.
Je n’ai pas utilisé directement ces puces NPU, mais les timings me semblent difficiles à croire. Les timings CUDA à la fin ont de fortes chances de ne pas avoir été mesurés correctement dans le code. Mesurer le temps d’exécution est moins simple qu’il n’y paraît.
Le nombre d’opérations annoncé ne compte que les opérations effectuées directement sur le NPU, alors que l’article original a peut-être inclus du travail CPU dans les mesures NPU et GPU. La documentation fournit un outil de benchmarking, donc j’imagine qu’une approche similaire a été utilisée, et je serais aussi curieux de voir la variance après le warm-up.
Le format des données semble également incorrect. Ici, il faut du
channels last. La documentation le confirme aussi.Le nombre 1500 paraît aussi étrange et peut entraîner des erreurs supplémentaires. Avec 1536, 2048, 256, ou des valeurs plus petites, les résultats pourraient être différents. Les modèles réels ne traitent pas des images en pleine résolution, et si l’architecture est optimisée pour le modèle, les informations de forme deviennent importantes. En machine learning, l’optimisation des formes est assez importante.
À la lecture rapide de la documentation, la configuration semble aussi inadaptée. Dans “Model Workflow”, il est indiqué que les données doivent être en virgule flottante 8 bits ou 16 bits, mais il existe plusieurs types de virgule flottante. Le bfloat de PyTorch n’est pas identique à
torch.halfni àtorch.float16.La précision mixte reste un sujet déroutant ; quand ce genre de problème apparaît, cela vaut la peine de l’examiner sérieusement. Je ne recommande pas de se contenter d’appliquer la procédure standard de quantification et de s’arrêter là. C’est un bon point de départ, mais si l’objectif n’est pas simplement “assez bon”, il ne faut pas s’y arrêter.
Je ne pense toutefois pas que ces résultats soient inutiles. Ils doivent simplement être améliorés. Ce genre de travail est plus complexe qu’il n’y paraît, en grande partie parce que la technologie est récente et que les détails sont encore en cours de stabilisation.
Lorsqu’on compare avec un CPU ou un GPU, en particulier CUDA, il faut se rappeler que des centaines de milliers d’heures-personnes y ont été consacrées, et qu’au moins des dizaines de milliers d’heures-personnes ont aussi été investies dans des bibliothèques de haut niveau comme Python. Ces dispositifs ne sont pas encore prêts à être utilisés directement au niveau d’abstraction linguistique préféré de l’utilisateur moyen, mais ils peuvent être très utiles si l’on accepte de travailler plus près du matériel.
Pour mesurer des opérations GPU asynchrones dans PyTorch, il faut utiliser des événements CUDA et
torch.cuda.synchronize(), plutôt que d’encadrer la sortie du modèle avec un timer CPU.[1] https://www.thonking.ai/p/what-shapes-do-matrix-multiplicati...
Déployer un modèle sur un NPU demande pas mal d’optimisation basée sur le profilage. Si l’on prend un modèle qui tourne bien sur CPU et qu’on l’amène tel quel sans l’optimiser pour le NPU, le résultat est généralement décevant
La description du dépôt GitHub est bien plus instructive que le billet de blog
En faisant tourner une multiplication de matrices int8 avec onnx, les performances sont d’environ 0,6 TF
https://github.com/usefulsensors/qc_npu_benchmark
Ils disent avoir rendu les matrices d’entrée plus proches d’une forme carrée pour permettre le tiling et la réutilisation, mais je ne serais pas surpris qu’une bonne partie des optimisations possibles ne soient pas arrivées dans Onnx
Qualcomm ne semble pas donner d’accès direct au NPU, et s’attend plutôt à ce que l’utilisateur convertisse son modèle avec un framework puis le lui fournisse. D’après mon expérience, les outils de conversion sont généralement assez mauvais et ratent beaucoup d’optimisations
Donc ce n’est peut-être pas tant « le NPU est mauvais » que « les outils de conversion sont mauvais ». J’attendrai de pouvoir y accéder directement, et je ne fais pas confiance aux outils de conversion
Je pense que les NPU sont bien adaptés aux très petits modèles de machine learning et à l’approximation de fonctions très rapide. C’est l’usage que j’ai en tête. Les LLM sont très à la mode en ce moment, mais il existe énormément de tâches spécialisées où de petits modèles sont vraiment utiles
Je ne dis pas qu’il n’en existe pas, mais honnêtement je ne vois pas très bien lesquelles, donc ça m’intéresse
J’espère que le NPU de l’Elite X est devenu plus accessible grâce à Copilot+, mais l’idée est qu’il ne faut pas s’attendre à ce qu’un « modèle généraliste soit magiquement téléporté sur le NPU » aussi facilement
Une RTX 4080 devrait pouvoir atteindre environ 40 TFLOPS, alors qu’ici ils ne rapportent que 216 milliards d’opérations par seconde. À ce niveau-là, je pense qu’il faudrait revoir le benchmark
Il y a de fortes chances qu’il y ait une erreur sérieuse dans la mesure des FLOPS. Il est possible que le CPU batte le NPU, mais pour comparer correctement, il faudrait benchmarker plusieurs multiplications de matrices sans synchronisation applicative
Le benchmark est une multiplication de matrices de la forme
(6, 1500, 256) X (6, 256, 1500), ce qui n’est pas très grand dans le monde de l’IA. Avec des matrices bien plus grandes, l’écart serait plus importantPar exemple, même Llama 3.1 8B, qui fait partie des petits modèles, contient des multiplications de matrices comme
(batch, 14336, 4096) x (batch, 4096, 14336)Je ne trouve pas ce benchmark assez réaliste
J’ai lancé qprof, le profileur NPU de Qualcomm, sur ce benchmark. D’après les résultats du profilage, la charge a été assignée aux cœurs vectoriels, et non aux cœurs tensoriels qui fournissent l’essentiel de la puissance de calcul du NPU
À la louche, HMX semble 30 fois plus puissant que HVX
La charge de travail est relativement petite, donc les surcoûts de quantification/déquantification des entrées-sorties et du mapping NCHW-NHCW empêchent de bien exploiter les capacités du matériel. Padding les poids et les entrées sur des multiples de 64 aiderait aussi les performances
Graphe de profilage : https://imgur.com/a/2OKR93e
Les performances de calcul HVX estimées sont de
4 * 2 * 1.43 * 1024 / 8 = 1,46 TOPSen int8. Ici, 4 est le nombre de cœurs vectoriels, 2 le nombre d’opérations par cycle, 1,43 GHz la fréquence HVX, 1024 bits la largeur des registres vectoriels, et 8 bits la précision4 * 2 * 1.43 * 1024 / 8Le vrai titre de l’article devrait être « Benchmarking Qualcomm's NPU on the Microsoft Surface Tablet »
Ce n’est pas un article sur les NPU en général, mais sur un NPU précis, observé avec un benchmark précis et une combinaison précise de bibliothèques/frameworks. Donc, en réalité, cela ne prouve pratiquement rien