4
votes

Les variables statiques entravent-elles la mise en cache des données?

Depuis Optimisation des logiciels en C ++ (Section 7.1) ,

L'avantage des données statiques est qu'elles peuvent être initialisées à valeurs avant le démarrage du programme. L'inconvénient est que la mémoire l'espace est occupé tout au long de l'exécution du programme, même si variable n'est utilisée que dans une petite partie du programme. Cela rend les données mise en cache moins efficace.

L'utilisation de static dans ceci, sauf qu'elle s'applique à la fois au C et au C ++ dans le cas exact de la durée de stockage statique.

clair pourquoi (ou si) la mise en cache des données est moins efficace pour les variables de durée statiques? Voici une comparaison spécifique:

void foo() {
  static int static_arr[] = {/**/};
}
void bar() {
  int local_arr[] = {/**/};
}

Je ne vois aucune raison pour laquelle les données statiques seraient mises en cache différemment de tout autre type de données. Dans l'exemple donné, je penserais que foo sera plus rapide car la pile d'exécution n'a pas à charger static_arr , alors que dans bar , la pile d'exécution doit charger local_arr . Dans les deux cas, si ces fonctions ont été appelées à plusieurs reprises, static_arr et local_arr seront mis en cache. Ai-je tort?


6 commentaires

@Someprogrammerdude Non. Ce problème s'applique également au C et au C ++. Les deux balises appartiennent. Si vous n'êtes pas sûr de l'utilisation de statique dans ce cas, demandez plus de détails.


Les données statiques mettront en cache exactement la même chose que toutes les autres données. Je dirais que l'auteur du livre en question ne sait pas vraiment de quoi il parle.


La source fournit-elle mcve illustrant cette déclaration? Si ce n'est pas le cas, vous pouvez ignorer cette déclaration. Je pense qu'ils impliquent probablement que les données statiques sont généralement stockées «loin» des variables de pile, mais cela ne signifie pas nécessairement que l'utilisation de variables statiques rend automatiquement la mise en cache des données moins efficace.


@Johan Je soupçonne qu'il voulait dire autre chose, mais oui, dans l'état actuel des choses, cette déclaration est absurde. Je vais peut-être lui envoyer un e-mail. Il tient ce livre à jour.


@VTT Malheureusement non. Vous pouvez consulter la section 7.1 pages 26 et 27 pour tout le matériel explicatif fourni. D'accord, je vais continuer à lire sans réfléchir à celui-là alors. Ce qu'il voulait dire plus tard dans le texte sera peut-être clair de toute façon.


Je pense que la partie importante est la suivante: "L'inconvénient est que l'espace mémoire est occupé pendant toute l'exécution du programme, même si la variable n'est utilisée que dans une petite partie du programme ." Il semble que l'auteur ait présenté un exemple où la fonction n'était que peu utilisée, de sorte que la variable «n'existerait» que dans une petite partie du temps total d'exécution du programme, au lieu de l'avoir globale et existante en mémoire tout le temps. Ce qui semble être un point valable - il devrait cependant être formulé plus clairement.


3 Réponses :


6
votes

En général, Agner Fog sait généralement de quoi il parle.

Si nous lisons la citation dans le contexte de la section 7.1 Différents types de stockage de variables , nous voyons ce qu'il entend par " mise en cache moins efficace " au début du section:

La mise en cache des données est médiocre si les données sont dispersées au hasard dans la mémoire. Il est il est donc important de comprendre comment les variables sont stockées. Les principes de stockage sont les idem pour les variables simples, les tableaux et les objets.

Donc, l'idée derrière le fait de dire que les variables statiques sont moins efficaces en cache est que la probabilité que l'emplacement mémoire où elles sont stockées soit "froide" (plus dans le cache) est plus grande qu'avec pile memory, où la variable avec une durée de stockage automatique serait stockée.

En gardant à l'esprit la mise en cache et la pagination, c'est la combinaison de la localisation physique et temporelle du stockage des données qui affecte les performances.


7 commentaires

Il est difficile d'appliquer le concept de localité des données aux données statiques. Je veux dire, ai-je tort de penser que la mémoire BSS est toujours mise en cache? Cela ressemble à une mauvaise application des concepts. La pile et le tas sont mappés paresseusement aux pages, mais BSS est une taille définie et toujours chargé. Cela nécessite une réflexion plus approfondie.


Je veux dire, le compilateur sait exactement quand quelles parties de BSS seront nécessaires. Il indiquera donc sûrement au CPU de le mettre en cache. Ça ne marche pas comme ça? Je suis naïf.


Au niveau du processeur, il n'y a pas de "données statiques", il n'y a que des pages de mémoire et des lignes de cache. Le compilateur C ++ réorganisera les accès mémoire pour commencer à récupérer les données le plus tôt possible, mais le problème avec variables locales statiques signifie que le standard C ++ actuel exige qu'elles soient initialisées de manière atomique la première fois que le bloc est exécuté. Cela les rend très lents en raison de la surcharge de verrouillage et de l'incapacité de retirer l'accès à la mémoire du bloc.


Les compilateurs restent généralement en dehors de la prise de décision concernant la mise en cache, car de telles décisions nécessitent des connaissances statiques et dynamiques (comment le programme se comporte réellement lorsqu'il s'exécute), la plate-forme CPU cible réelle (il existe des différences entre les fournisseurs) et le contexte dans lequel le programme l'application s'exécute (le cache est partagé avec d'autres applications et OS).


À propos, la .bss section est pour zéro- stockage global initialisé. Un tableau global qui n'est pas initialisé à zéro sera stocké dans la section .data (ou .rdata s'il s'agit de const ). Dans tous les cas, oui, les pages mémoire seront pré-allouées, mais le fait qu’elles soient ou non dans le cache dépend des modèles d’utilisation totale de la mémoire de l’application ( active working set ).


D'accord, merci beaucoup d'avoir dissipé mes idées fausses pour moi. Je suis beaucoup plus clair sur ce qui se passe maintenant!


Toujours trouver cela très mal formulé, car le stockage en tas aurait également un problème similaire, et je ne parle même pas d'ASLR qui rend le commentaire obsolète à mon humble avis.



4
votes

La déclaration a ou n'a pas de sens selon la façon dont vous la ponctuez:

Lecture 1:

L'avantage des données statiques est qu'elles peuvent être initialisées aux valeurs souhaitées avant le démarrage du programme. L'inconvénient est que l'espace mémoire est occupé pendant toute l'exécution du programme, même si la variable n'est utilisée que dans une petite partie du programme.

Tout ce qui précède rend la mise en cache des données moins efficace.

C'est insensé.

Lecture 2:

L'avantage des données statiques est qu'elles peuvent être initialisées aux valeurs souhaitées avant le démarrage du programme.

L'inconvénient est que l'espace mémoire est occupé pendant toute l'exécution du programme ...

... même si la variable n'est utilisée que dans une petite partie du programme. Il y a un cas où cela pourrait rendre la mise en cache des données moins efficace.

Ce cas serait où la variable statique a été allouée de stockage soit dans une page qui n'est pas toujours permutée, soit sur une ligne de cache qui est rarement utilisée autrement. Vous pouvez subir un manque de cache, ou théoriquement dans le pire des cas une erreur de page (bien que franchement, avec la quantité de mémoire physique à notre disposition ces jours-ci, si cela se produit, vous avez de plus gros problèmes). cas particulier que vous démontrez, la réponse serait "ça dépend".

Oui, l'initialisation de static_arr est une opération unique et peut donc être considérée comme sans coût.

Oui, l'initialisation de local_arr se produit chaque fois que la fonction est appelée, mais il se peut que ce soit:

  1. cette initialisation est triviale, ou
  2. l'initialisation est élidée par le compilateur dans le cadre d'une passe d'optimisation

En général, sauf si vous avez une optimisation spécifique en tête, il est préférable (tm) d'écrire le code qui énonce explicitement le comportement que vous voulez, c'est-à-dire:

  • utiliser des variables statiques (variables avec une durée de stockage statique) lorsque la ou les valeurs de la variable / tableau doivent survivre à des appels successifs à la fonction.

  • utiliser des variables locales (strictement, des variables avec une durée de stockage automatique) lorsque les valeurs existantes n'ont pas de sens à l'entrée ou à la sortie de la fonction.

Vous constaterez que le compilateur fera dans presque tous les cas la chose la plus efficace après les passes d'optimisation.

Il existe un cas particulier où l'initialisation statique est meilleure (tm). Dans le cas (par exemple) d'un tampon qui nécessite une allocation dynamique. Vous ne voudrez peut-être pas encourir le coût de l'allocation / désallocation à chaque appel. Vous voudrez peut-être que la mémoire tampon augmente dynamiquement lorsque cela est nécessaire, et qu'elle continue de croître, car les opérations futures pourraient bien avoir besoin de la mémoire à nouveau.

Dans ce cas, l'état réel de la variable est la taille de celle qui lui est allouée. tampon. Ainsi, l'état est important sur l'entrée et la sortie de la fonction, par exemple:

  std::string const& to_string(json const& json_object)
  {
    static thread_local buffer;               // thread-safe, auto-expanding buffer storage
    buffer.clear();                           // does not release memory
    serialise_to_string(buffer, json_object); // may allocate memory occasionally
    return buffer;
  }


2 commentaires

Pouvez-vous expliquer ce que vous entendez par des variables de durée statiques sur une page donnée? Les variables statiques ne sont jamais sur une pile / tas mappé paresseux, pour autant que je sache. Doit être sur BSS, qui a une taille définie et est chargé avec le programme lui-même. Vous devriez mettre à jour votre réponse pour être plus correcte, ou peut-être que je me trompe.


@okovko la section BSS pourrait être remplacée si les ressources mémoire du système d'exploitation sont soumises à une charge importante. C'est la raison de mon commentaire "vous avez d'autres problèmes".



3
votes

La réponse de rustyx l'explique. Les variables locales sont stockées sur la pile. L'espace de pile est libéré lorsqu'une fonction retourne et réutilisé lorsque la fonction suivante est appelée. La mise en cache est plus efficace pour les variables locales car le même espace mémoire est réutilisé encore et encore, tandis que les variables statiques sont dispersées à différentes adresses mémoire qui ne peuvent jamais être réutilisées à d'autres fins. Que les données statiques soient stockées dans la section DATA (initialisée) ou dans la section BSS (non initialisée) ne fait aucune différence à cet égard. L'espace du haut de la pile restera probablement mis en cache tout au long de l'exécution du programme et sera réutilisé plusieurs fois.

Un autre avantage est qu'un nombre limité de variables locales est accessible avec un décalage de 8 bits par rapport au pointeur de pile, tandis que les variables statiques ont besoin d'une adresse absolue 32 bits (en 32 bits x86) ou d'une adresse relative 32 bits (en x86-64). En d'autres termes, les variables locales peuvent rendre le code plus compact et améliorer l'utilisation du cache de code ainsi que du cache de données.

// Example
int main () {
  f();
  g();
  return 0;
}

void f() {
   int x; 
   ...
}

void g() {
   int y;  // y may occupy the same memory address as x
   ...
}


1 commentaires

Je vois! Donc, si x et y étaient statiques, alors le haut de la pile ne serait pas réutilisé et le programme utiliserait des données "froides". C'est une excellente réponse et je pense que c'est incroyable d'amener l'auteur du manuel en question à donner une réponse aussi claire à la confusion. Votre manuel pourrait éventuellement bénéficier de l'inclusion de cet exemple dans la section 7.1. Je vous remercie!