Comment détecter de manière générique l’associativité de la ligne de cache à partir du code en mode utilisateur?

Je suis en train de monter un petit correctif pour l’outil cachegrind / callgrind dans valgrind qui détectera automatiquement, en utilisant un code complètement générique, des instructions CPU et la configuration du cache (pour le moment, seules les configurations automatiques x86 / x64 et d’autres architectures ne fournissent pas Configuration de type CPUID en code non privilégié). Ce code devra s’exécuter entièrement dans un contexte non privilégié, c’est-à-dire un code en mode utilisateur pur. Il doit également être portable pour différentes implémentations POSIX. Par conséquent, grokking / proc / cpuinfo ne le fera pas, car l’un de nos systèmes de destination n’a pas une telle chose.

La détection de la fréquence de la CPU, du nombre de caches, de leur taille et même de la taille de la ligne de cache peut être réalisée à l’aide de code POSIX générique à 100% ne comportant aucun code d’opération spécifique à la CPU (juste beaucoup d’hypothèses raisonnables, telles que l’ajout deux nombres ensemble, si sans décrochage de la mémoire ou du registre, seront probablement exécutés en un seul cycle). Cette partie est assez simple.

Ce qui n’est pas si simple, et pourquoi je demande à StackOverflow, est de savoir comment détecter l’associativité de ligne de cache pour un cache donné? L’associativité est le nombre d’emplacements d’un cache pouvant contenir une ligne de cache donnée de la mémoire principale. Je peux voir que l’associativité du cache N1 pourrait être détectée, mais cache N2? L’associativité de N1 se met en travers de la route?

Je comprends que c’est probablement un problème qui ne peut pas être résolu. Mais je le jette sur StackOverflow et espère que quelqu’un sait quelque chose que je ne connais pas. Notez que si nous échouons ici, je vais simplement coder en dur dans un défaut d’associativité à quatre voies, en supposant que cela ne ferait pas une énorme différence en termes de résultats.

Merci,
Niall

Voici un schéma:

Avoir un modèle d’access à la mémoire avec une foulée S et un nombre d’éléments uniques accédés = N. Le test touche d’abord chaque élément unique, puis mesure le temps moyen d’access à chaque élément en accédant au même motif un très grand nombre de fois.

Exemple: pour S = 2 et N = 4, le type d’adresse serait 0,2,4,6,0,2,4,6,0,2,4,6, …

Considérons une hiérarchie de cache à plusieurs niveaux. Vous pouvez faire les hypothèses raisonnables suivantes:

  • La taille de n + 1 ème niveau de cache est une puissance deux fois supérieure à celle du nième cache
  • L’associativité de n + 1 ème cache est également une puissance deux fois supérieure à celle du nième cache.

Ces 2 hypothèses nous permettent de dire que si deux adresses correspondent au même ensemble dans n + 1 ème cache (disons L2), elles doivent alors mapper au même ensemble dans le nième cache (disons L1).

Supposons que vous connaissiez la taille des caches L1, L2. Vous devez rechercher l’associativité du cache L2.

  • set ssortingde S = taille du cache L2 (afin que chaque access mappe vers le même ensemble dans L2 et dans L1 également)
  • varier N (par puissance de 2)

Vous obtenez les régimes suivants:

  • Régime 1: N <= associativité de L1. (Tous les accès HIT en L1)
  • Régime 2: associativité de L1
  • Régime 3: N> associativité de L2 (Tous les access sont manquants dans L2)

Ainsi, si vous tracez le temps d’access moyen en fonction de N (lorsque S = taille de L2), vous verrez un tracé semblable à une étape. La fin de l’étape la plus basse vous donne l’associativité de L1. L’étape suivante vous donne l’associativité de L2.

Vous pouvez répéter la même procédure entre L2-L3 et ainsi de suite. S’il vous plaît laissez-moi savoir si cela aide. La méthode d’obtention des parameters de cache en faisant varier le pas d’un modèle d’access à la mémoire est similaire à celle utilisée par le test de performances LMBENCH. Je ne sais pas si lmbench implique aussi l’associativité.

Pourriez-vous créer un petit programme qui n’accède qu’aux lignes du même ensemble? Ensuite, vous pouvez augmenter la distance de stack entre les access et lorsque le temps d’exécution diminue considérablement, vous pouvez supposer que vous avez atteint l’associativité.

Ce n’est probablement pas très stable, mais cela pourrait peut-être donner une longueur d’avance, je ne sais pas. J’espère que ça peut aider.

Pour la plate-forme x86, vous pouvez utiliser cpuid :

Voir http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html pour plus de détails.

Vous avez besoin de quelque chose comme:

 long _eax,_ebx,_ecx,_edx; long op = func; asm ("cpuid" : "=a" (_eax), "=b" (_ebx), "=c" (_ecx), "=d" (_edx) : "a" (op) ); 

Ensuite, utilisez les informations selon la doc dans le lien mentionné ci-dessus.