Chose étonnante, j'observe cette perte quand mon binaire passe de 12,1 Mo à 12,6 Mo (tailles obtenues en activant LTO), mais pas de perte en passant de 13,2 à 13,7 Mo (tailles obtenues en désactivant LTO). Cette énigme restera de côté pour l'instant.
Pour comprendre cela il faut peut être se pencher sur les détails techniques des caches d'instructions.
Considérons par exemple le processeur Intel Core i7-12700F.
Cette page nous indique que dans le "CPU core complex #1 - Performance cores", les 8 cores disposent de 8 caches L1 de taille 32 KB pour les instructions. Je suppose que chaque core a son propre cache. De plus le petit bouton triangulaire sur la droite indique aussi que "The level 1 instruction cache is an 8-way set associative cache with a line size of 64 bytes".
J'en déduis donc que chaque cache d'instruction de 32KB gère 32KB/64B = 512 lignes de caches organisée en 64 groupes (=512/8) de 8.
Maintenant, considérons un programme donc la partie critique du code fait 30KB. Il n'a donc besoin que de 480 lignes de caches (=30KB/64B) pour s'exécuter à pleine vitesse . A priori, cela doit tenir et chacun des 64 groupes devra traiter 480/64 = 7.5 lignes donc moins de 8. Malheureusement, ce n'est qu'une moyenne.
Si tout se passe bien, certains groupes auront 7 lignes et d'autres 8 lignes.
Par contre, avec de la malchance, on peut se retrouver avec des groupes nécessitants 9 ou même 10 lignes alors que d'autres seront à seulement 5 ou 6. Le hit ratio va alors s'effondrer dans les groupes traitants plus de 8 lignes.
Il y a une quinzaine d'année, je me rappelle avoir été confronté à un problème de ce genre dans un code Fortran. Après l'avoir optimisé au petits-oignons, je décide de changer l'ordre de 2 fonctions dans un fichier parce que .. c'était plus joli comme cela. L'ordre des appels de fonctions ne changeant pas, j'ai été très surpris de constater une dégradation notable de la vitesse d'exécution. J'ai vérifié au niveau des binaires et la seule différence était l'ordre relatif des 2 fonctions en mémoire. Tout le reste était absolument identique au bit près. Les données de profiling ont confirmé qu'en intervertissant les 2 fonctions, j'avais modifié la distribution du code dans les lignes de caches. J'ai restauré l'ordre initial pour récupérer mes perfs et atteindre mon objectif.
En pratique, il n'y a pas grand chose à faire quand ce genre de chose arrive. Ce n'est pas vraiment contrôlable à moins de tout programmer en assembleur pour un modèle spécifique de processeur.
# Les caches d'instructions sont parfois capricieux
Posté par SChauveau . En réponse au journal Prise de poids et perte de perf. Évalué à 9. Dernière modification le 23 juillet 2023 à 12:15.
Pour comprendre cela il faut peut être se pencher sur les détails techniques des caches d'instructions.
Considérons par exemple le processeur Intel Core i7-12700F.
https://www.cpu-world.com/CPUs/Core_i7/Intel-Core%20i7%20i7-12700F.html
Cette page nous indique que dans le "CPU core complex #1 - Performance cores", les 8 cores disposent de 8 caches L1 de taille 32 KB pour les instructions. Je suppose que chaque core a son propre cache. De plus le petit bouton triangulaire sur la droite indique aussi que "The level 1 instruction cache is an 8-way set associative cache with a line size of 64 bytes".
J'en déduis donc que chaque cache d'instruction de 32KB gère 32KB/64B = 512 lignes de caches organisée en 64 groupes (=512/8) de 8.
Maintenant, considérons un programme donc la partie critique du code fait 30KB. Il n'a donc besoin que de 480 lignes de caches (=30KB/64B) pour s'exécuter à pleine vitesse . A priori, cela doit tenir et chacun des 64 groupes devra traiter 480/64 = 7.5 lignes donc moins de 8. Malheureusement, ce n'est qu'une moyenne.
Si tout se passe bien, certains groupes auront 7 lignes et d'autres 8 lignes.
Par contre, avec de la malchance, on peut se retrouver avec des groupes nécessitants 9 ou même 10 lignes alors que d'autres seront à seulement 5 ou 6. Le hit ratio va alors s'effondrer dans les groupes traitants plus de 8 lignes.
Il y a une quinzaine d'année, je me rappelle avoir été confronté à un problème de ce genre dans un code Fortran. Après l'avoir optimisé au petits-oignons, je décide de changer l'ordre de 2 fonctions dans un fichier parce que .. c'était plus joli comme cela. L'ordre des appels de fonctions ne changeant pas, j'ai été très surpris de constater une dégradation notable de la vitesse d'exécution. J'ai vérifié au niveau des binaires et la seule différence était l'ordre relatif des 2 fonctions en mémoire. Tout le reste était absolument identique au bit près. Les données de profiling ont confirmé qu'en intervertissant les 2 fonctions, j'avais modifié la distribution du code dans les lignes de caches. J'ai restauré l'ordre initial pour récupérer mes perfs et atteindre mon objectif.
En pratique, il n'y a pas grand chose à faire quand ce genre de chose arrive. Ce n'est pas vraiment contrôlable à moins de tout programmer en assembleur pour un modèle spécifique de processeur.