1 points par GN⁺ 2025-05-07 | 1 commentaires | Partager sur WhatsApp
  • Anukari est un synthétiseur physique 3D en temps réel qui doit calculer de grands modèles masse-ressort sur le GPU, mais sur macOS avec Apple silicon, si la fréquence GPU ne monte pas suffisamment, il devient difficile de respecter les contraintes de latence audio
  • La façon dont les DAW appellent les plugins à chaque bloc de tampon audio interagit avec les heuristiques de gestion de l’énergie de macOS : le GPU semble se reposer entre les blocs et peut rester dans un état de faibles performances
  • Dans le profiler Metal de Xcode Instruments, Anukari fonctionne correctement quand le Performance State est réglé sur Maximum, mais se dégrade fortement sur Minimum, ce qui confirme que le cœur du goulot d’étranglement est la fréquence GPU
  • Pour l’instant, Anukari utilise un contournement de type « waste makes haste » avec un petit spin kernel qui augmente artificiellement la charge GPU, mais le problème persiste sur certains matériels Apple Pro/Max
  • Le développeur demande à l’équipe Apple Metal une extension des Audio Workgroups au GPU, une option de sensibilité temps réel pour MTLCommandQueue, ou des indications sur une solution existante, et estime que la même spin loop n’est pas nécessaire sous Windows

Les problèmes de performances GPU d’Anukari sous macOS

  • Anukari 3D Physics Synthesizer simule en temps réel un grand modèle physique masse-ressort pour générer de l’audio
  • Pour prendre en charge un nombre significatif d’objets physiques, il faut un GPU, et le code physique est davantage limité par l’ALU que par la mémoire
  • L’état mutable de la simulation est stocké dans la threadgroup memory du GPU
    • Une structure proche d’un cache L1 alloué manuellement, donc très rapide
  • L’utilisation typique consiste à l’exécuter comme plugin AU ou VST3 dans un DAW comme Pro Tools ou Ableton
    • Le DAW appelle Anukari à chaque bloc de tampon audio
    • À chaque bloc, Anukari exécute le kernel de simulation physique sur le GPU, attend le résultat, puis retourne
  • Les blocs de tampon audio peuvent absorber le délai de planification du kernel GPU en le répartissant sur plusieurs échantillons, mais le temps d’exécution du kernel lui-même reste déterminant

Le conflit entre gestion de l’énergie de macOS et audio temps réel

  • Apple silicon peut réduire la fréquence d’horloge de la puce pour économiser l’énergie, et macOS maintient une fréquence basse s’il estime que la demande de calcul est faible
  • Le mode d’exécution d’Anukari dans un DAW s’accorde mal avec la manière dont macOS évalue la demande GPU
  • Le GPU est inactif entre les blocs de tampon audio, si bien que la charge moyenne peut n’apparaître qu’à environ 60 %, par exemple
    • Les heuristiques réelles de macOS sont inconnues, mais l’auteur suppose qu’elles pourraient ressembler à une moyenne de charge
    • Cette charge peut ne pas dépasser le seuil nécessaire à la hausse de fréquence du GPU
  • Anukari a besoin d’une faible latence pour respecter ses contraintes temps réel, et donc d’une fréquence GPU élevée
  • On ne sait pas jusqu’où la fréquence du GPU Apple peut descendre, mais elle peut descendre assez bas pour rendre Anukari inutilisable

Le problème de fréquence confirmé avec le profiler Metal

  • Le profiler Metal d’Apple Instruments, inclus dans Xcode, a confirmé qu’Anukari est ALU-bound
  • Le profiler Metal permet de choisir le « Performance State » de Metal pendant le profilage
    • Ce réglage n’est pas configurable en dehors du profiler
  • Avec l’état de performance Maximum, Anukari fonctionne parfaitement
  • Avec l’état de performance Minimum, son comportement se dégrade fortement
  • La différence entre ces deux états montre que la fréquence GPU est au cœur du problème de performances d’Anukari

Le contournement « waste makes haste » et ses limites

  • Comme macOS n’augmente pas la fréquence GPU au moment nécessaire, Anukari utilise un contournement spécifique
  • Il exécute une seconde tâche GPU en parallèle du calcul audio sur GPU afin de créer une charge moyenne élevée et d’inciter macOS à augmenter la fréquence
    • Cette tâche est ajustée pour utiliser le moins de ressources GPU possible tout en stimulant l’heuristique de fréquence
    • Il s’agit en pratique d’une spin loop qui réchauffe le GPU
  • Cette stratégie est appelée « waste makes haste » et est documentée en détail dans un devlog associé
  • Sur le MacBook M1 du développeur, cette méthode a entièrement résolu le problème et Anukari fonctionne de manière stable
  • Mais après la sortie d’Anukari Beta, certains utilisateurs macOS ont rencontré des problèmes
    • Les problèmes de performances semblent particulièrement fréquents chez les utilisateurs de matériel Apple Pro ou Max
    • L’article avance plusieurs hypothèses : une fréquence indépendante par chiplet GPU, ou une spin workload trop conservatrice sur les GPU plus puissants

Les pistes de solution demandées à Apple

  • En partant du principe que les ingénieurs Apple en savent davantage, l’auteur propose plusieurs solutions possibles
  • Solution 1 : étendre le concept d’Audio Workgroup au traitement GPU
    • Le traitement audio de macOS s’effectue dans un thread ou un groupe de threads appelé Audio Workgroup
    • L’OS comprend que ces threads ont des contraintes temps réel et leur attribue une priorité
    • Une MTLCommandQueue gérée par un thread d’Audio Workgroup pourrait être considérée comme du traitement temps réel afin d’ajuster la fréquence GPU
  • Solution 2 : fournir dans l’API Metal une option indiquant une sensibilité temps réel pour MTLCommandQueue
    • La fréquence du chiplet GPU traitant cette queue pourrait être ajustée en conséquence
  • Solution 3 : si une manière d’obtenir déjà ce comportement existe, il suffirait qu’Apple l’indique
  • En haut de l’article, il est ajouté qu’Apple a pris contact et qu’un article séparé aborde ce sujet

Comparaison avec Game Mode et Windows

  • Le Game Mode d’Apple semble proche de ce dont Anukari a besoin, mais il est difficile à appliquer
    • Game Mode fonctionne au niveau du processus
    • Anukari est le plus souvent utilisé comme plugin dans un autre processus, et ce processus ne prend pas en charge Game Mode
    • Anukari ne peut pas le contrôler directement
    • Game Mode exige aussi le plein écran, alors qu’Anukari n’est généralement pas en plein écran
  • Sous Windows, ce problème ne se produit pas
    • On ne sait pas si c’est parce que Windows donne davantage de contrôle à l’utilisateur sur l’état de performance, ou parce que les pilotes NVIDIA sont moins prudents sur la consommation d’énergie
    • Sous Windows, la spin loop n’est pas nécessaire
  • L’article compare le fait que des PC Windows avec des GPU peu performants exécutent correctement Anukari, tandis qu’un Mac M4 Max coûteux peut produire des saccades

Pourquoi le pipelining n’est pas adapté

  • Pipeliner le code GPU pour saturer le GPU convient aux tâches axées sur le débit, mais Anukari est une tâche sensible à la latence
  • En planifiant à l’avance plusieurs kernels de simulation physique, le CPU pourrait préparer le bloc suivant pendant que le GPU traite le bloc audio courant
  • Mais le pipelining augmente la latence en échange d’un meilleur débit
  • Chaque exécution de kernel d’Anukari doit accéder à des données d’entrée audio en temps réel, comme l’entrée micro
  • L’exécution spéculative du bloc audio suivant n’est pas utilisable, car les données d’entrée nécessaires ne sont pas encore disponibles

Le problème de mettre le spin kernel dans la même MTLCommandQueue

  • Si la vraie cause était que le spin kernel et le physics kernel s’exécutent sur des chiplets GPU différents, les placer dans la même MTLCommandQueue pourrait sembler être une solution
  • Cette approche a effectivement été essayée, mais elle n’a pas fonctionné
  • La raison est qu’Anukari est une tâche sensible à la latence
    • Le spin kernel s’exécute parfois un peu trop longtemps
    • Ce temps empiète alors sur le temps d’exécution du physics kernel
  • Une approche avec un petit spin kernel et de la mémoire unifiée volatile, où le CPU écrit un drapeau « exit kernel early », a également été testée
  • Même avec ces mécanismes, il arrive que le spin kernel empiète sur le temps du physics kernel

Pourquoi le GPU kernel hedging est difficile

  • Une approche consistant à exécuter plusieurs copies du kernel physique et à utiliser le résultat terminé en premier, à la manière du request hedging dans les systèmes distribués, a aussi été envisagée
  • Elle pourrait réduire la latence de queue et la variance de latence, tout en créant une charge GPU qui incite l’OS à augmenter l’état de performance
  • Mais Anukari rencontre plusieurs problèmes
    • Si un physics kernel prend plus longtemps qu’un cycle de bloc audio, ce flux de kernels prend du retard
    • Le flux en retard doit rattraper son retard lors de futurs blocs, ce qui nécessite un fast-forward copiant l’état interne d’un autre flux
  • La copie de l’état interne coûte cher
    • Le plus gros état interne est le tampon audio utilisé pour la delay line
    • Il stocke 1 seconde d’audio passé par microphone
    • Sa taille est de 48,000 samples * 50 mics * 2 channels * 16 voices * 4 bytes, soit 307 Mo
    • Elle augmente encore avec des sample rates plus élevés
  • Pour traiter cela efficacement, il faudrait suivre précisément les zones dirty de chaque hedged kernel stream et ne copier que ces zones
    • Mais l’agencement mémoire du tampon est optimisé pour la charge de lecture du physics kernel
    • Même en copiant le minimum, il faudrait copier des zones dispersées dans tout le tampon, ce qui serait lent
  • Les modifications du modèle par l’utilisateur devraient également être propagées à tous les hedged kernels
  • Le physics kernel a une empreinte GPU bien plus grande que le spin kernel « waste makes haste »
    • Le hedging créerait davantage de charge GPU inutile et pourrait réduire le nombre d’instances d’Anukari exécutables en parallèle
    • Les hedge kernels pourraient aussi se concurrencer et tous ralentir

Optimisations déjà effectuées et pourquoi le GPU est nécessaire

  • La simulation d’Anukari étant ALU-bound, il reste peu de marge pour des optimisations classiques comme l’amélioration des schémas d’accès mémoire
  • Pour améliorer les performances, il faut optimiser le débit arithmétique
    • Des opérations FP16 sont utilisées lorsque c’est possible afin de mieux saturer les ALU Apple
    • Des micro-benchmarks servent à ajuster l’ordre des instructions
    • Tout l’état physique est placé en L1 memory
    • L’ordre des chargements est réorganisé pour la vectorization
  • Le fait que les threads d’un SIMD-group Apple partagent généralement l’instruction pointer est également exploité
    • Différents objets physiques suivent des branch paths très divergents
    • Simuler deux types d’objets dans un même SIMD-group ralentit l’exécution à cause de l’instruction masking
    • Pour l’éviter, l’agencement mémoire des objets physiques est optimisé dynamiquement afin de réduire le nombre de types d’objets exécutés dans un même SIMD-group
    • Cette optimisation est détaillée dans the new warp alignment optimizer
  • Il reste des possibilités d’optimisation arithmétique, mais elles devraient se limiter à des gains de quelques points de pourcentage
  • Sur des machines puissantes, Anukari peut simuler 768 à 1024 objets physiques
    • Chaque objet peut être connecté arbitrairement à d’autres objets
    • Les objets effectuent généralement une intégration d’Euler implicite à un sample rate audio de 48 000 échantillons par seconde
    • Chaque objet possède 3 à 10 paramètres de comportement
    • Certains comportements incluent des opérations coûteuses comme des rotations vectorielles, exp() ou log()
    • Pour la polyphonie, toute la simulation physique est exécutée jusqu’à 16 fois en parallèle
  • Le CPU ne permettait pas cette approche ; les nombreuses ALU du GPU, le contrôle de l’agencement du cache L1 et des structures de concurrence comme threadgroup_barrier sont nécessaires
  • Anukari ne pourrait pas exister sans traitement GPU

Pourquoi l’API GPU Audio n’est pas la solution

  • Alexander Talashov, CEO de GPU Audio, a affirmé qu’Anukari pourrait résoudre le problème en utilisant l’API GPU Audio
  • Le développeur considère GPU Audio comme un bon produit et le présente comme un produit rendant le GPU accessible au DSP
  • Mais il estime que GPU Audio n’est pas utile pour Anukari
  • Contrairement aux applications DSP traditionnelles, Anukari est plutôt un intégrateur d’équations différentielles numériques
    • Il comporte bien une part de DSP, mais la majorité du calcul relève de l’intégration eulérienne
    • Les traitements DSP comme la compression des microphones du monde physique sont effectués inline dans le calcul physique du GPU
  • Anukari programme directement le GPU à bas niveau avec Metal
  • Ce dont il a besoin, c’est qu’Apple permette d’augmenter de manière fiable la fréquence GPU

1 commentaires

 
GN⁺ 2025-05-07
Avis sur Hacker News
  • Certains d’entre vous ont peut-être vu Anukari dans mon post Show HN : https://news.ycombinator.com/item?id=43873074

    Dans ce fil, il a été question des performances de macOS. Anukari tourne bien sur la plupart des machines Apple silicon, y compris le M1 de base, et tous mes tests ont été faits sur un M1 de base, avec d’excellents résultats. Le matériel est vraiment impressionnant.

    Mais pour que ça fonctionne, j’ai dû implémenter un contournement assez bizarre afin de pousser macOS à augmenter la fréquence d’horloge du GPU pour que le traitement audio soit suffisamment rapide. Les heuristiques habituelles de macOS pour déterminer l’état de performance du GPU ne comprennent pas la charge de travail particulière d’Anukari.

    J’ai donc fini par documenter toute la situation de façon excessivement détaillée, et j’aimerais obtenir de l’aide pour être mis en relation avec la bonne personne chez Apple, probablement quelqu’un côté API Metal. Aidez-moi :)

    • Tu as parlé d’un « article très long et très technique », mais en le lisant jusqu’au bout, je l’ai trouvé pas si long, très clair et bien écrit, et instructif. Beau travail d’écriture.

      Je n’ai jamais eu de Mac et mon PC est vieux au point de ne pas avoir de vrai GPU, donc il y a peu de chances que je puisse essayer Anukari tout de suite, mais ça a l’air vraiment génial, c’est dommage. J’espère que ça sera vite résolu.

    • Je me demande si tu as essayé cet entitlement : https://developer.apple.com/documentation/bundleresources/en...

      Je me demande si com.apple.developer.sustained-execution fonctionne aussi dans l’autre sens.

    • Article intéressant, et problème intéressant aussi. J’ai l’impression que si l’idée d’exécuter le travail dans la même file échoue, c’est au fond pour la même raison que le problème initial. À cause de la fréquence d’horloge variable, un ordonnancement précis est impossible, et selon la fréquence GPU choisie par le système d’exploitation, le moment où le spin s’arrête se décale par rapport au moment idéal, ce qui crée une forme d’aliasing.

      Dans ce cas, il se peut que la tâche de spin ne soit pas assez complexe pour pousser le GPU à sa fréquence maximale. S’il tournait vraiment à pleine performance, on devrait pouvoir caler de manière stable le moment de fin du spin sans ajouter de PLL logiciel. Je n’ai pas vu de description détaillée de l’implémentation du spin, mais une boucle de spin plus complète, qui sollicite davantage de parties du GPU de façon continue, serait probablement plus efficace pour maintenir la fréquence au niveau maximal.

    • J’ai raté le Show HN, mais dès que j’ai vu ça, je me suis dit que ça irait très bien avec des paysages sonores ASMR créatifs et de l’audio multidimensionnel immersif. Ce serait super si toi, ou l’un de tes utilisateurs, pouviez créer une démo. Félicitations pour le projet, et j’espère que tu trouveras de l’aide pour le problème avec Apple.

    • Bon article, avec une explication claire et facile à comprendre. J’ai clairement déjà rencontré le même type de problème que celui décrit, dans d’autres contextes.

  • Tout le monde, ça a marché. J’ai eu une conversation très productive avec exactement la bonne personne de l’équipe Metal ! Merci de m’avoir aidé à attirer l’attention d’Apple. Je ne m’attendais absolument pas à autant de soutien.

    https://anukari.com/blog/devlog/productive-conversation-appl...

    • C’est bien qu’il y ait maintenant un contournement, mais le fait que vous ne puissiez même pas partager ce qu’il est illustre ironiquement à la perfection la dernière phrase de https://news.ycombinator.com/item?id=43904921 sur la manière dont Apple communique.

      C’est du genre : « Si vous réglez cette valeur comme ceci puis que vous la changez comme cela, ça marche. Ce n’est pas documenté, mais maintenant vous le savez. »

      Quand vous implémenterez le contournement, ce serait bien de le mettre dans une fonction au nom très explicite, afin que d’autres personnes confrontées à des contraintes GPU similaires, sensibles à la latence, puissent au moins retrouver la trace de l’incantation magique par désassemblage.

    • HN a une fois de plus rempli sa mission originelle : franchir les barrières bureaucratiques placées devant le support client des grandes entreprises.

      Félicitations pour le projet et bonne chance.

  • J’ai travaillé dans deux entreprises connues qui avaient des apps très populaires sur l’Apple App Store.

    Les équipes Apple avec lesquelles nous étions en contact ne s’intéressaient absolument pas à nos problèmes ; à la place, elles nous invitaient régulièrement dans leurs bureaux pour discuter des dernières fonctionnalités à présenter à la WWDC, et nous forçaient de fait à les prendre en charge. C’était le début et la fin de notre relation avec elles. Pour comprendre pourquoi les logiciels Apple bogués ne fonctionnaient pas, il fallait obligatoirement ouvrir des tickets de support technique.

    Les responsables des relations développeurs chez Apple ne sont pas des gens sérieux.

    • Heureusement, comme le montre le post d’origine ci-dessus, mon expérience n’est pas une règle générale. Mais il y a environ dix ans, quand je travaillais dans une entreprise avec une app assez connue, une mise à jour a complètement détruit les performances de l’app.

      Exactement au même moment, un concurrent a sorti une app qui n’avait aucun problème de performance. Il s’est avéré que le développeur de cette app concurrente venait de quitter Apple, et qu’il avait laissé dans le pilote vidéo d’Apple un piège non documenté qui cassait notre app. Nous n’avons pu corriger l’app qu’après avoir désassemblé le binaire du concurrent et trouvé le changement non documenté. Ce développeur s’est même moqué de notre CEO par e-mail. Quel monde merveilleux.

  • Le profileur Metal dispose d’une fonctionnalité très utile qui permet de choisir l’état de performance de Metal pendant le profilage d’une application. Impossible de le régler en dehors du profileur.

    Ça suggère qu’il existe probablement une API privée. Est-ce que la rétro-ingénierie ne serait pas la voie la plus simple ? Sauf, bien sûr, si cela nécessite un privilège spécial impossible à contourner sans désactiver SIP.

    • Il y a forcément une API privée. L’article le dit aussi :

      « Le profileur Metal dispose d’une fonctionnalité très utile qui permet de choisir le “Performance State” de Metal pendant le profilage d’une application. Impossible de le régler en dehors du profileur. »

      Si ce n’est pas une API privée, comment le profileur Metal pourrait-il faire ça ? On ne pourrait pas observer le profileur avec un outil de débogage pour comprendre ce qui se passe en interne ?

  • Le problème, si cette API est rendue publique, c’est que beaucoup trop de développeurs vont systématiquement forcer l’activation du mode performances maximales. Je ne sais pas vraiment s’il existe une bonne façon d’empêcher ça tout en fournissant l’API

    • Sur un appareil sur batterie, il existe déjà une infinité de façons pour une appli de gaspiller de l’énergie. Au bout du compte, le système repose déjà sur la confiance que les développeurs, volontairement ou par erreur, ne lancent pas inutilement des tâches énergivores. Ajouter une API de plus qui peut gaspiller de l’énergie si elle est mal utilisée ne changerait pas grand-chose

    • L’article parle aussi du mode Jeu, une fonctionnalité optimisée pour ce genre de cas dans les versions récentes des systèmes d’exploitation Apple. Quand le mode Jeu s’active, une notification apparaît, et la plupart des applications ne voudraient pas de ça. Jusqu’ici, je n’ai vu aucun cas d’abus de cette fonctionnalité

    • Les développeurs n’abusent pas encore des workgroups audio dans tous leurs pools de threads pour obtenir une planification sur les P-cores et une priorité élevée. Cela suggère donc que, lorsqu’un workgroup audio émet des commandes vers le GPU, on pourrait appliquer une sorte de délai d’expiration avant downclock du GPU, basé sur le dernier moment où le workgroup a envoyé des données

      L’audio sur GPU est aujourd’hui un domaine très niche, mais comme l’entreprise mentionnée dans l’article a récemment publié un SDK, cela pourrait devenir plus courant. Malgré tout, je ne suis pas vraiment convaincu. Traiter sur le GPU revient presque à dire qu’on ne se soucie pas de la latence ; à mon avis, il suffit donc d’augmenter la taille des tampons d’entrée/sortie

    • Même en cas d’abus de l’API, ce serait plus efficace que de lancer de fausses tâches occupées pour obtenir le même résultat. Les applis peuvent déjà le faire sans API, ou sans les autorisations que l’API pourrait exiger

    • Et une autorisation manuelle ? Même si elle était cachée quelque part, elle serait probablement nécessaire pour certaines applis très niche

      Et au niveau du système d’exploitation, il suffirait de refuser par défaut Zoom, Teams et les navigateurs web :)

  • La meilleure façon de procéder :

    1. Parcourir les vidéos de la WWDC et trouver l’ingénieur qui semble le mieux connaître le problème rencontré

    2. S’il s’appelle Michael Thomson, lui envoyer directement un e-mail du type mthomson@apple.com

    • Ou bien l’envoyer à son frère Pichael à pthomson
  • À part ça, Anukari devrait sortir un pack de sons Mick Gordon et partager les revenus avec lui. Ce type crée des trucs complètement dingues, et ses démos sont incroyables. Quand on dispose d’un outil aussi puissant, collaborer avec des artistes est à la fois une bonne affaire et une bonne chose pour le monde. Si on aime Mick Gordon, en tout cas — et moi, je l’aime bien

  • Je n’ai absolument pas besoin de cette appli, mais elle est vraiment géniale. Ce genre d’applis ramène du fun dans l’informatique. Je ne dis pas qu’il n’y en a plus du tout aujourd’hui, mais ça rappelle une époque où circulaient davantage de programmes graphiques et expérimentaux, jusqu’à la demoscene

  • Il ne faut pas rater le lien https://x.com/Mick_Gordon/status/1918146487948919222 dans l’avant-dernier paragraphe. C’est une démo créée par Mick Gordon, et @anukarimusic a répondu :

    « lol, c’est le deuxième jour après la sortie, et vous avez déjà complètement pulvérisé toutes les démos que j’ai créées en l’utilisant tous les jours pendant deux ans »

  • Mettre à jour 1024 objets à 48 kHz semble faisable même sur CPU, selon la façon dont le code est écrit. Ça fait bien 48 millions de mises à jour par seconde, non ? Ça semble adapté à quelques boucles parallélisées sur plusieurs cœurs avec OpenMP

      1. Pour la polyphonie, Anukari exécute jusqu’à 16 copies complètes du modèle physique. Cela fait donc 16 * 1024 * 48K. Il va falloir mettre à jour l’article de blog

      2. Comme l’utilisateur peut connecter arbitrairement les objets entre eux, chaque objet doit lire et traiter les connexions vers N autres entités

      3. Pour utiliser tout le CPU, il faut une synchronisation entre les cœurs à chaque étape physique, et c’est lent

      4. La quantité de traitement par objet est assez élevée. Il y a beaucoup de fonctions transcendantes, même si des approximations sont possibles, et il y a aussi beaucoup de fonctionnalités. Tous les paramètres peuvent être modulés, il faut être robuste face aux NaN, etc.

      5. Les utilisateurs veulent exécuter plusieurs instances d’Anukari en parallèle pour plusieurs pistes, effets, etc.

      Vu autrement, cela donne 4 GHz / (16 voice * 1024 obj * 4 connections * 48,000 sample) = 1.3 cycles per thing

      Le GPU traite cette charge de travail en un clin d’œil. C’est une architecture parfaitement adaptée. Il peut traiter 16 voice * 1024 obj entièrement en parallèle, la synchronisation à chaque étape est simple, et l’utilisateur peut gérer le cache L1

    • Si le calcul est correct, cela donne 83 cycles d’horloge pour calculer un échantillon. Avec 16 cœurs, cela ferait théoriquement 1333 cycles, ce qui n’est pas énorme — d’autant plus qu’on ne peut pas vraiment utiliser le CPU à presque 100 % en permanence