existe-t-il un moyen en C++ pour déterminer la taille du cache du CPU? j'ai un algorithme qui traite beaucoup de données et je voudrais décomposer ces données en morceaux de telle sorte qu'ils tiennent dans le cache. Est-ce possible? Pouvez-vous me donner d'autres conseils sur la programmation en pensant à la taille du cache (en particulier en ce qui concerne le traitement des données multithread/multicœur)?
Merci!
Selon " Ce que tout programmeur devrait savoir sur la mémoire ", par Ulrich Drepper, vous pouvez faire ce qui suit sous Linux:
Une fois que nous avons une formule pour les besoins en mémoire, nous pouvons la comparer avec la taille du cache. Comme mentionné précédemment, le cache peut être partagé avec plusieurs autres cœurs. Actuellement {Il y aura sûrement bientôt un meilleur moyen!} Le seul moyen d'obtenir des informations correctes sans connaissance du codage en dur est via le système de fichiers/sys. Dans le tableau 5.2, nous avons vu ce que le noyau publie sur le matériel. Un programme doit trouver le répertoire:
/sys/devices/system/cpu/cpu*/cache
Ceci est répertorié dans Section 6: Ce que les programmeurs peuvent faire .
Il décrit également un court test juste sous la figure 6.5 qui peut être utilisé pour déterminer la taille du cache L1D si vous ne pouvez pas l'obtenir à partir du système d'exploitation.
Il y a encore une chose que j'ai rencontrée dans son article: sysconf(_SC_LEVEL2_CACHE_SIZE)
est un appel système sous Linux qui est censé retourner la taille du cache L2 bien qu'il ne semble pas être bien documenté.
C++ lui-même ne se soucie pas des caches CPU, il n'y a donc pas de support pour interroger les tailles de cache intégrées dans le langage. Si vous développez pour Windows, il y a la fonction GetLogicalProcessorInformation () - , qui peut être utilisée pour interroger des informations sur les caches CPU.
Préallouez un grand tableau. Accédez ensuite à chaque élément de manière séquentielle et enregistrez l'heure de chaque accès. Idéalement, il y aura un saut dans le temps d'accès lorsque le cache manque. Ensuite, vous pouvez calculer votre cache L1. Cela pourrait ne pas fonctionner, mais cela vaut la peine d'essayer.
Vous pouvez voir ce fil: http://software.intel.com/en-us/forums/topic/296674
La réponse courte est dans cet autre fil:
Sur le matériel IA-32 moderne, la taille de la ligne de cache est de 64. La valeur 128 est un héritage de la microarchitecture Intel Netburst (par exemple Intel Pentium D) où les lignes de 64 octets sont couplées en secteurs de 128 octets. Lorsqu'une ligne d'un secteur est récupérée, le matériel récupère automatiquement l'autre ligne du secteur également. Du point de vue du faux partage, la taille de ligne effective est donc de 128 octets sur les processeurs Netburst. ( http://software.intel.com/en-us/forums/topic/292721 )
Selon ce que vous essayez de faire, vous pouvez également le laisser à une bibliothèque. Puisque vous mentionnez le traitement multicœur, vous voudrez peut-être jeter un œil à Intel Threading Building Blocks .
TBB inclut des allocateurs de mémoire compatibles avec le cache. Plus précisément, cochez cache_aligned_allocator
(dans la documentation de référence, je n'ai trouvé aucun lien direct).
Chose intéressante, j'ai écrit un programme pour le faire il y a un certain temps (en C cependant, mais je suis sûr qu'il sera facile à incorporer dans du code C++).
http://github.com/wowus/CacheLineDetection/blob/master/Cache%20Line%20Detection/cache.c
La fonction get_cache_line est la fonction intéressante, qui renvoie l'emplacement de juste avant le plus grand pic dans les données de synchronisation des accès au tableau. Il a deviné correctement sur ma machine! Si quoi que ce soit d'autre, cela peut vous aider à créer le vôtre.
Il est basé sur cet article, qui à l'origine a suscité mon intérêt: http://igoro.com/archive/gallery-of-processor-cache-effects/
lire le cpuid du cpu (x86) puis déterminer la taille du cache par une table de correspondance. Le tableau doit être rempli avec les tailles de cache que le fabricant de l'unité centrale de traitement publie dans ses manuels de programmation.
IIRC, GCC a un __builtin_prefetch
indice.
http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Other-Builtins.html
a une excellente section à ce sujet. Fondamentalement, il suggère:
__builtin_prefetch (&array[i + LookAhead], rw, locality);
où rw est une valeur 0 (préparation pour la lecture) ou 1 (préparation pour l'écriture), et localité utilise le nombre 0-3, où zéro n'est pas une localité et 3 est une localité très forte.
Les deux sont facultatifs. LookAhead serait le nombre d'éléments à envisager. Si l'accès à la mémoire était de 100 cycles et que les boucles déroulées sont séparées de deux cycles, LookAhead pourrait être défini sur 50 ou 51.
Il y a deux cas à distinguer. Avez-vous besoin de connaître les tailles de cache au moment de la compilation ou au moment de l'exécution?
Pour certaines applications, vous connaissez l'architecture exacte sur laquelle votre code s'exécutera, par exemple, si vous pouvez compiler le code directement sur la machine hôte. Dans ce cas, simplifiez la recherche de la taille et le codage en dur, c'est une option (pourrait être automatisé dans le système de construction). Aujourd'hui, sur la plupart des machines, la ligne de cache L1 devrait être de 64 octets.
Si vous voulez éviter cette complexité ou si vous devez prendre en charge la compilation sur des architectures inconnues, vous pouvez utiliser la fonctionnalité C++ 17 std :: hardware_constructive_interference_size comme bon moyen de secours. Il fournira une estimation au moment de la compilation pour la ligne de cache, mais soyez conscient de ses limites . Notez que le compilateur ne peut pas deviner parfaitement lorsqu'il crée le binaire, car la taille de la ligne de cache dépend, en général, de l'architecture.
Au moment de l'exécution, vous avez l'avantage de connaître la machine exacte, mais vous aurez besoin d'un code spécifique à la plate-forme pour lire les informations du système d'exploitation. Un bon point de départ est l'extrait de code de cette réponse , qui prend en charge les principales plates-formes (Windows, Linux, MacOS). De la même manière, vous pouvez également lire la taille du cache L2 lors de l'exécution.
Je déconseille d'essayer de deviner la ligne de cache en exécutant des tests de performance au démarrage et en mesurant celle qui fonctionne le mieux. Cela pourrait bien fonctionner, mais il est également sujet aux erreurs si le CPU est utilisé par d'autres processus.
Si vous devez expédier un binaire et que les machines sur lesquelles il s'exécutera par la suite présentent une gamme d'architectures différentes avec différentes tailles de cache, vous pouvez créer des parties de code spécialisées pour chaque taille de cache, puis dynamiquement (au démarrage de l'application) choisir le meilleur ajustement un.
Avec C++ 17, vous pouvez utiliser std :: hardware_destructive_interference_size pour déterminer la taille du cache L1. Voir https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size Pour autant que je sache, il n'est pris en charge que par Microsoft Visual Studio 2019 à partir de maintenant.