Lorsque des calculs limités en bande passante mémoire sont effectués dans des environnements de mémoire partagée (par exemple, filetés via OpenMP, Pthreads ou TBB), il existe un dilemme sur la façon de garantir que la mémoire est correctement répartie sur la mémoire physique , de sorte que chaque thread accède principalement à la mémoire sur un bus mémoire "local". Bien que les interfaces ne soient pas portables, la plupart des systèmes d'exploitation ont des moyens de définir l'affinité des threads (par exemple pthread_setaffinity_np()
sur de nombreux systèmes POSIX, sched_setaffinity()
Linux, SetThreadAffinityMask()
Windows). Il existe également des bibliothèques telles que hwloc pour déterminer la hiérarchie de la mémoire, mais malheureusement, la plupart des systèmes d'exploitation ne fournissent pas encore de moyens de définir des stratégies de mémoire NUMA. Linux est une exception notable, avec libnumapermettant à l'application de manipuler la politique de mémoire et la migration des pages à la granularité des pages (en ligne principale depuis 2004, donc largement disponible). D'autres systèmes d'exploitation s'attendent à ce que les utilisateurs observent une politique implicite de «premier contact».
Travailler avec une politique de «première touche» signifie que l'appelant doit créer et distribuer des threads avec l'affinité qu'il envisage d'utiliser plus tard lors de la première écriture dans la mémoire fraîchement allouée. (Très peu de systèmes sont configurés de manière à malloc()
trouver réellement des pages, il promet simplement de les trouver lorsqu'elles sont réellement défaillantes, peut-être par des threads différents.) Cela implique que l'allocation utilisant calloc()
ou initialisant immédiatement la mémoire après l'allocation à l'aide memset()
est nuisible car elle aura tendance à défaillir toute la mémoire sur le bus mémoire du cœur exécutant le thread d'allocation, ce qui conduit à la bande passante mémoire la plus défavorable lorsque la mémoire est accessible à partir de plusieurs threads. Il en va de même pour l' new
opérateur C ++ qui insiste pour initialiser de nombreuses nouvelles allocations (par exemplestd::complex
). Quelques observations sur cet environnement:
- L'allocation peut être rendue «collective de threads», mais maintenant l'allocation devient mélangée dans le modèle de thread, ce qui n'est pas souhaitable pour les bibliothèques qui peuvent avoir à interagir avec des clients utilisant différents modèles de thread (peut-être chacun avec leurs propres pools de threads).
- Le RAII est considéré comme une partie importante du C ++ idiomatique, mais il semble nuire activement aux performances de la mémoire dans un environnement NUMA. Le placement
new
peut être utilisé avec de la mémoire allouée viamalloc()
ou des routines delibnuma
, mais cela change le processus d'allocation (ce qui, je pense, est nécessaire). - EDIT: Ma déclaration précédente sur l'opérateur
new
était incorrecte, elle peut prendre en charge plusieurs arguments, voir la réponse de Chetan. Je crois qu'il y a toujours un souci d'obtenir des bibliothèques ou des conteneurs STL pour utiliser l'affinité spécifiée. Plusieurs champs peuvent être compressés et il peut être gênant de s'assurer que, par exemple, unstd::vector
réalloue avec le gestionnaire de contexte correct actif. - Chaque thread peut allouer et fausser sa propre mémoire privée, mais l'indexation dans les régions voisines est plus compliquée. (Considérons un produit matriciel-vecteur clairsemé avec une partition en ligne de la matrice et des vecteurs; l'indexation de la partie non possédée de x nécessite une structure de données plus compliquée lorsque x n'est pas contigu dans la mémoire virtuelle.)
Est-ce que des solutions à l'allocation / initialisation NUMA sont considérées comme idiomatiques? Ai-je omis d'autres problèmes critiques?
(Je ne veux pas pour mon C ++ exemples pour impliquer l'accent sur cette langue, mais le C ++ langage code des décisions sur la gestion de la mémoire qu'une langue comme C n'a pas, donc il a tendance à être une plus grande résistance en suggérant que les programmeurs C ++ font les les choses différemment.)