Quand et comment aligner sur la ligne de mémoire cache taille?
Dans Dmitry Vyukov excellent délimitée mpmc file d'attente écrit en C++
Voir: http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
Il en ajoute un rembourrage variables. Je présume que c'est pour s'aligner à une ligne de cache pour la performance.
J'ai quelques questions.
- Pourquoi est-il fait de cette façon?
- Est-il un portable méthode de
toujours travailler - Dans ce cas, il serait préférable d'utiliser
__attribute__
à la place.
((aligned (64))) -
pourquoi serait-rembourrage avant un pointeur de tampon aider avec l'exécution? ce n'est pas seulement le pointeur chargés dans le cache donc, c'est vraiment de la taille d'un pointeur?
static size_t const cacheline_size = 64; typedef char cacheline_pad_t [cacheline_size]; cacheline_pad_t pad0_; cell_t* const buffer_; size_t const buffer_mask_; cacheline_pad_t pad1_; std::atomic<size_t> enqueue_pos_; cacheline_pad_t pad2_; std::atomic<size_t> dequeue_pos_; cacheline_pad_t pad3_;
Serait-ce le concept de travail en vertu de la gcc pour du code c?
Vous devez vous connecter pour publier un commentaire.
Il est fait de cette façon afin que les différents cœurs de modifier les différents champs n'aurez pas à faire rebondir la ligne de cache contenant les deux d'entre eux entre leurs caches. En général, pour un processeur à accéder à certaines données dans la mémoire, l'ensemble de la ligne de cache en contenant, doit être en ce processeur mémoire cache locale. Si c'est la modification des données, qui cache l'entrée doit généralement être la seule copie dans tous les cache dans le système (mode Exclusif dans le MESI/MOESI de style protocoles de cohérence de cache). Lorsque les noyaux indépendants essayer de modifier les différentes données qui se vivent sur la même ligne de cache, et donc perdre du temps à se déplacer qu'ensemble de la ligne arrière, qui est connu comme faux partage.
Dans l'exemple que vous donnez, un noyau qui peut être enqueueing une entrée (lecture (partagée)
buffer_
et de l'écriture (exclusif) seulementenqueue_pos_
) tandis que l'autre retire (partagébuffer_
et exclusifdequeue_pos_
) sans base caler sur une ligne de cache possédé par l'autre.Le rembourrage au commencement signifie que
buffer_
etbuffer_mask_
se retrouver sur la même ligne de cache, plutôt que d'être divisés sur deux lignes et nécessitant donc le double de la mémoire de la circulation pour l'accès.Je ne suis pas sûr si la technique est entièrement portable.
L'hypothèse est que chaque(voir les commentaires)cacheline_pad_t
va lui-même être aligné à 64 octets (sa taille) limite de ligne de cache, et donc tout ce qui suit, il sera sur la prochaine ligne de cache. Autant que je sache, le C et le C++ le langage normes exigent que ce de bâtiments, d'ouvrages, de sorte qu'ils puissent vivre dans des tableaux bien, sans violer les exigences alignement de l'un de ses membres.La
attribute
approche serait plus spécifiques de compilateur, mais peut réduire la taille de cette structure en deux, depuis le rembourrage serait limitée à l'arrondissement chaque élément complète de la ligne de cache. Cela pourrait être très bénéfique si l'on a beaucoup de à ces.Le même concept s'applique en C et C++.
std::aligned_storage
qui vous permettent de demander à un stockage de taille définie et l'alignement. L'alignement par défaut pour unchar [N]
est1
autrement.alignas
déclaration des modificateurs pour ce faire de façon portable. C'est pris en charge sur tous les activement développé compilateur C++.mpmc_bounded_queue
aalignas(64)
dans sa déclaration, il semble quecacheline_pad_t pad0_
n'est pas nécessaire, carbuffer_
seront alignés sur la ligne de cache (en supposant que cache la ligne de la taille est de 64)? Dans ce cas, les rembourrages, il peut être plus "compact" sans faux partage? Merci à l'avance!alignas
le membre concerné variables, parce que vous voulez sur des lignes de cache; le faire sur l'ensemble de lampmc_bounded_queue
ne fonctionnerait pas. Il serait en effet enregistrer un (insignifiant) nombre d'octets dans l'exemple donné, mais ne devrait pas affecter les performances.alignas
est appliqué surmpmc_bounded_queue
, ses membres ne seront pas de ligne de cache alignées, maismpmc_bounded_queue
est; de là,buffer_
ne seront pas alignés, droite?Vous devrez peut-être s'aligner dans une limite de ligne de cache, ce qui est généralement 64 octets par ligne de cache, lorsque vous travaillez avec des interruptions ou de données hautes performances lit, et ils sont obligatoires à utiliser lorsque vous travaillez avec des interprocessus, les sockets. Avec Interprocessus, les sockets, les variables de contrôle qui ne peuvent être réparties sur plusieurs lignes de cache ou de RAM DDR mots bien il va provoquer la L1, L2, etc ou des caches ou des DDR RAM pour fonctionner comme un filtre passe-bas du filtre et de votre interruption de données! CE QUI EST MAUVAIS!!! Cela signifie que vous obtenez bizarre erreurs lors de votre algorithme est bon et il a le potentiel pour faire de vous rendre fou!
La RAM DDR est presque toujours à lire en 128 bits des mots (RAM DDR Mots), qui est de 16 octets, de sorte que l'anneau de la mémoire tampon variables ne doivent pas être dispersés sur plusieurs de RAM DDR mots. certains systèmes n'utilisent 64 bits de RAM DDR mots et techniquement, vous pourriez obtenir un 32 bits de RAM DDR mot sur un 16-bit CPU, mais on pourrait utiliser de la SDRAM à la situation.
On peut aussi juste être intéressé à réduire le nombre de lignes de cache à utiliser lors de la lecture de données dans une haute performance de l'algorithme. Dans mon cas, j'ai développé le plus rapide du monde entier en chaîne de l'algorithme (40% plus rapidement qu'avant la plus rapide de l'algorithme) et je suis en train de travailler sur l'optimisation de la Grisu algorithme, qui est le plus rapide du monde flottant-point de l'algorithme. Pour imprimer le nombre à virgule flottante, vous devez imprimer le entier, dans le but d'optimiser la Grisu une optimisation j'ai mis en œuvre est que j'ai cache-ligne alignée à la Recherche de Tables (LUT) pour Grisu dans exactement 15 lignes de cache, ce qui est plutôt bizarre que cela en fait aligné comme ça. Cela prend la Lut de la .sev section (c'est à dire de la mémoire statique) et les place sur la pile (ou d'un segment, mais la Pile est plus approprié). Je n'ai pas comparé ça, mais c'est bon pour l'amener jusqu', et j'ai beaucoup appris à ce sujet, est le moyen le plus rapide pour charger des valeurs est à la charge de l'i-cache et ne le cache de données. La différence est que le je-cache est en lecture seule et a beaucoup de grandes lignes de cache, car il est en lecture seule (2KB était ce que le professeur cite moi une fois.). Donc, vous allez enfin degrigate votre performance d'un tableau d'indexation, par opposition à chargement d'une variable comme ceci:
par opposition à la méthode lente:
La différence est que le
int variable = 12345678
seront chargées de la i-les lignes de cache par compensation à la variable dans l'i-cache depuis le début de la fonction, tandis queslower_way = int[0]
seront chargées à partir de la plus petite d-lignes de cache à l'aide de beaucoup plus lente tableau d'indexation. Cette subtilement que je viens de découvrir est en fait ralentir ma et beaucoup d'autres entier en chaîne de l'algorithme. Je dis cela parce que vous pouvez chose que vous êtes l'optimisation par cache-alignement des données en lecture seule lorsque vous n'êtes pas.Généralement en C++, vous allez utiliser la
std::align
fonction. Je vous conseille de ne pas utiliser cette fonction car il n'est pas garanti pour fonctionner de façon optimale. Ici est le moyen le plus rapide pour s'aligner sur une ligne de cache, qui, pour être à l'avant je suis l'auteur et c'est un shamless plug:Kabuki Toolkit Alignement De La Mémoire De L'Algorithme De
et c'est ici le plus vite std::aligner remplacement: