web-dev-qa-db-fra.com

Comment puis-je effectuer un vidage du cache du processeur sous Windows x86?

Je souhaite forcer un vidage du cache du processeur dans Windows (pour des raisons d'analyse comparative, je veux émuler en commençant sans données dans le cache du processeur), de préférence une implémentation C de base ou un appel Win32.

Existe-t-il un moyen connu de le faire avec un appel système ou même quelque chose d'aussi sournois que de dire un gros memcpy?

Plate-forme Intel i686 (P4 et plus, ça va aussi).

46
user183135

Heureusement, il existe plusieurs façons de vider explicitement les caches.

L'instruction "wbinvd" réécrit le contenu du cache modifié et marque les caches vides. Il exécute un cycle de bus pour que les caches externes vident leurs données. Malheureusement, c'est une instruction privilégiée. Mais s'il est possible d'exécuter le programme de test sous quelque chose comme DOS, c'est la voie à suivre. Cela a l'avantage de garder très petit l'encombrement du cache du "système d'exploitation".

De plus, il existe l'instruction "invd", qui invalide les caches sans les renvoyer dans la mémoire principale. Cela viole la cohérence de la mémoire principale et du cache, vous devez donc vous en occuper vous-même. Pas vraiment recommandé.

À des fins d'analyse comparative, la solution la plus simple consiste probablement à copier un grand bloc de mémoire dans une région marquée par WC (combinaison d'écriture) au lieu de WB. La région mappée en mémoire de la carte graphique est un bon candidat, ou vous pouvez marquer une région comme WC par vous-même via les registres MTRR.

Vous pouvez trouver des ressources sur l'analyse comparative de routines courtes sur Programmes de test pour mesurer les cycles d'horloge et la surveillance des performances.

52
Gunther Piez

Il existe des instructions d'assemblage x86 pour forcer le processeur à vider certaines lignes de cache (telles que CLFLUSH ), mais elles sont assez obscures. CLFLUSH en particulier ne vide qu'une adresse choisie des caches L1.

quelque chose d'aussi sournois que de dire un grand memcopy?

Oui, c'est l'approche la plus simple et s'assurera que le CPU vide tous les niveaux de cache. Excluez simplement le temps de vidage du cache de vos benchmakrs et vous devriez avoir une bonne idée de la performance de votre programme sous la pression du cache.

8
intgr

Il n'y a malheureusement aucun moyen de vider explicitement le cache. Quelques-unes de vos options sont:

1.) Thrash le cache en effectuant de très grandes opérations de mémoire entre les itérations du code que vous comparez.

2.) Activez la désactivation du cache dans les registres de contrôle x86 et testez-le. Cela désactivera probablement également le cache d'instructions, ce qui n'est peut-être pas ce que vous voulez.

3.) Implémentez la partie de votre code votre analyse comparative (si c'est possible) en utilisant instructions non temporelles . Bien que ce ne soient que conseils au processeur sur l'utilisation du cache, il est toujours libre de faire ce qu'il veut.

1 est probablement le plus simple et suffisant pour vos besoins.

Edit : Oups, je suis corrigé il y a une instruction pour invalider le cache x86, voir la réponse de drhirsch

2
Falaina

L'instruction x86 WBINVD réécrit et invalide tous les caches. Il est décrit comme :

Réécrit toutes les lignes de cache modifiées dans le cache interne du processeur dans la mémoire principale et invalide (vide) les caches internes. L'instruction émet ensuite un cycle de bus à fonction spéciale qui ordonne aux caches externes de réécrire également les données modifiées et un autre cycle de bus pour indiquer que les caches externes doivent être invalidés.

Il est important de noter que l'instruction ne peut être exécutée que dans ring0, c'est-à-dire le système d'exploitation. Vos programmes utilisateur ne peuvent donc pas simplement l'utiliser. Sous Linux, vous pouvez écrire un module de noyau qui peut exécuter cette instruction à la demande. En fait, quelqu'un a déjà écrit un tel module de noyau: https://github.com/batmac/wbinvd

Heureusement, le code du module du noyau est vraiment minuscule, vous pouvez donc le vérifier avant de charger du code d'inconnus sur Internet dans votre noyau. Vous pouvez utiliser ce module (et déclencher l'exécution de l'instruction WBINVD) en lisant /proc/wbinvd, par exemple via cat /proc/wbinvd.

Cependant, j'ai trouvé que cette instruction (ou au moins ce module du noyau) est vraiment lente. Sur mon i7-6700HQ, je l'ai mesuré pour prendre 750µs! Ce nombre me semble vraiment élevé, donc j'ai peut-être fait une erreur en mesurant ceci - veuillez garder cela à l'esprit! L'explication de cette instruction dit simplement:

La durée ou les cycles de réalisation de WBINVD varient en fonction de la taille et d'autres facteurs des différentes hiérarchies de cache.

1
Lukas Kalbertodt