- Analyse directe de l’architecture interne de l’Apple Neural Engine (ANE) et mise en œuvre d’une méthode pour contourner CoreML et accéder directement au matériel
- Suppression de la couche d’abstraction de CoreML et exécution directe de la compilation, du chargement et de l’exécution des modèles via l’API
_ANEClient
- Analyse de MIL (Machine Learning Intermediate Language) et du format binaire E5, confirmant que l’ANE est un moteur d’exécution de graphes fondé sur des primitives d’opération fixes
- Démonstration de la possibilité d’un transfert de données zero-copy entre GPU et ANE grâce à la mémoire partagée IOSurface
- Cette recherche constitue le premier volet d’une trilogie consacrée à l’évaluation des performances réelles du M4 ANE et à ses possibilités d’apprentissage, avec pour enjeu le premier cas de contrôle direct d’un matériel privé d’Apple
Approche de rétro-ingénierie par collaboration humain–IA
- La recherche a été menée en collaboration entre un chercheur humain et Claude Opus 4.6 d’Anthropic
- L’humain a défini les axes d’exploration, et l’IA a assuré l’analyse des données et l’écriture du code
- Le point de départ était la question suivante : « Peut-on entraîner directement des modèles sur l’Apple Neural Engine ? »
- Apple ne publie ni l’ISA, ni l’architecture interne, ni l’interface de programmation directe de l’ANE
- L’accès n’est possible qu’à travers CoreML, ce qui rend difficile la compréhension du fonctionnement matériel
- L’équipe a donc remonté l’ensemble de la pile logicielle, de CoreML jusqu’au pilote noyau IOKit, afin d’obtenir un chemin de code permettant de contrôler directement l’ANE
Architecture du Neural Engine
- L’ANE n’est pas un GPU ni un CPU, mais un graph execution engine
- Il exécute l’intégralité d’un graphe de réseau neuronal compilé comme une opération atomique unique
- L’ANE de la puce M4 (nom de code H16G) dispose de 16 cœurs, d’une profondeur de file de 127 requêtes, d’un contrôle DVFS indépendant et d’une coupure d’alimentation à 0 mW au repos
- Apple a introduit pour la première fois un ANE à 2 cœurs avec l’A11 (2017), puis l’a étendu à chaque génération
Ce qui distingue cette étude des travaux précédents
- Ressources publiques existantes :
- la documentation sur le fonctionnement de l’ANE et l’analyse de performances de Matthijs Hollemans
- les premiers exemples de rétro-ingénierie de mdaiter/ane
- le pilote Linux rétro-ingéniéré de Asahi Linux
- le code officiel d’optimisation des transformers apple/ml-ane-transformers
- Apports originaux de cette étude :
- accès direct réussi à l’API
_ANEClient sans CoreML
- déchiffrement du chemin de compilation MIL en mémoire
- mesure du débit réel après suppression de la surcharge de CoreML
- exécution d’apprentissage de modèle sur un matériel conçu pour l’inférence uniquement
Méthodologie d’analyse
- Exploration de classes : extraction de la liste des classes internes à
AppleNeuralEngine.framework via la commande dyld_info -objc
- Method swizzling : interception des appels CoreML pour identifier le chemin d’appel vers les frameworks privés
- Analyse binaire : décodage des bundles E5 compilés pour comprendre le format des programmes
- Analyse de scaling : variation de la taille des matrices, de la profondeur des graphes et du nombre de canaux pour déduire la topologie matérielle
- Au final, découverte de plus de 40 classes privées, dont
_ANEClient, _ANEModel, _ANERequest, _ANEIOSurfaceObject, _ANEInMemoryModel
Contourner CoreML : accès direct à _ANEClient
_ANEClient permet de contrôler directement tout le pipeline compilation → chargement → évaluation d’un modèle
- CoreML n’est en pratique qu’une couche de confort qui encapsule ce processus
- L’ANE prend en charge jusqu’à 127 requêtes d’évaluation simultanées (queue depth), ce qui l’optimise pour l’inférence en streaming à haut débit
- Les buffers d’E/S basés sur IOSurface permettent un transfert par mémoire partagée entre GPU et ANE
MIL : le langage d’entrée de l’ANE
- CoreML utilise MIL (Machine Learning Intermediate Language) plutôt que ONNX ou protobuf
- Il repose sur le modèle SSA (static single assignment), avec types et formes explicitement définis
- Dans l’exemple de code, l’opération
matmul est exprimée de manière explicite
- Le layout des tenseurs suit le format NCDHW + Interleave, avec une structure
[Batch, Channels, Depth, Height, Width]
Format binaire E5
- Les programmes MIL sont compilés en binaires E5 FlatBuffer
- multiplication de matrices 1024×1024 : 2 688 octets, multiplication 128×128 : 2 680 octets
- la taille du code étant presque identique, cela indique qu’il ne contient pas l’algorithme de calcul matriciel lui-même, mais seulement des informations de configuration paramétrées
- Cela signifie que l’ANE exécute les graphes en combinant des primitives d’opération fixes (
Conv, MatMul, Elementwise, etc.)
Chemin de compilation en mémoire
_ANEInMemoryModelDescriptor permet de compiler du MIL en mémoire sans accès disque
- Principaux problèmes et solutions :
milText requiert non pas un NSString, mais un NSData (octets UTF-8)
weights doit être fourni sous forme de dictionnaire nom–données
- un accès à un répertoire temporaire est requis en interne → les droits d’écriture sont indispensables
- Une faute de frappe,
Desctiptor, a été repérée dans le code interne d’Apple
Profil matériel
- D’après l’analyse IOKit, l’ANE dispose de canaux indépendants de gestion de l’alimentation et de l’horloge (DVFS)
- présence de divers déclencheurs matériels/logiciels comme
ANE_ADCLK_TRIG et ANE_PPT_TRIG
- Parmi les opérations prises en charge repérées dans ANECompiler.framework, Conv apparaît comme la primitive centrale
- la partie 2 doit montrer qu’en convertissant un
1×1 Conv en MatMul, il est possible d’obtenir un gain de performances de 3×
Protocole IOSurface
- Toutes les entrées/sorties de données passent par des objets de mémoire partagée IOSurface
- le mécanisme est identique à celui du partage de textures GPU
- cela ouvre la possibilité de construire un pipeline zero-copy GPU↔ANE
Structure du cache de compilation
- Le compilateur ANE met en cache les binaires E5 sur disque
- chemin :
~/Library/Caches/.../com.apple.e5rt.e5bundlecache/.../H16G.bundle/
- première compilation : 20–40 ms, exécution immédiate en cas de cache hit
- c’est favorable à l’inférence, mais l’apprentissage nécessite une recompilation quand les poids changent
Zones encore inexplorées
- Classes pas encore analysées :
_ANEChainingRequest — possibilité d’enchaîner plusieurs modèles dans un seul dispatch
_ANESharedEvents, _ANESharedSignalEvent, _ANESharedWaitEvent — fence/signal pour la synchronisation GPU↔ANE
_ANEPerformanceStats — possibilité de compteurs de performances matériels
_ANEVirtualClient — possible approche de virtualisation multiprocessus
- Éléments encore inconnus :
- la microarchitecture des cœurs ANE et l’ISA
- la méthode d’affectation des opérations aux cœurs dans le graphe
- la fréquence d’horloge et la structure SRAM
Prochaines étapes
- Partie 2 : scaling de la multiplication de matrices, goulots d’étranglement SRAM, comparaison des performances de
Conv et MatMul, validation du chiffre « 38 TOPS » annoncé par Apple
- Partie 3 : entraînement de réseaux neuronaux sur l’ANE
- Tout le code est publié dans le répertoire
ane/ de github.com/maderix/ANE
- Environnement de test : M4 Mac Mini, macOS 15.x
Aucun commentaire pour le moment.