Je n'ai généralement aucun problème à décider si certaines données doivent être globales, statiques ou sur la pile (pas d'allocation dynamique ici, donc pas d'utilisation du tas). J'ai également lu quelques questions / réponses comme celle-ci, mais ma question est plus précise car elle implique une énorme quantité de données, énorme par rapport à la mémoire système.
Je travaille un code existant que j'essaye d'améliorer (design, problèmes possibles, performances, etc.). Ce code fonctionne sur un ancien MCU 8 bits avec seulement 4 Ko de RAM . Dans ce code, je suis confronté à l'utilisation d'un tableau de près de 1 Ko (oui, 1 Ko sur un système de RAM de 4 Ko ). Chaque octet de ce tableau est utilisé, ce n'est pas la question. Le problème est que ce tableau est un tableau statique dans le fichier où il est déclaré, donc son cycle de vie est le même que celui du programme (c'est-à-dire qu'il peut être considéré comme infini).
Cependant, après avoir lu le code, je viens de découvrir que ce tableau n'a pas besoin d'un cycle de vie infini, il est construit et traité de manière entièrement procédurale, nous devrions donc pouvoir le déclarer uniquement dans la fonction où il est utilisé, de cette façon, ce serait sur la pile, et nous économiserions donc ce 1 Ko de RAM.
Maintenant, la question: serait-ce une bonne idée? Du point de vue de la conception, s'il n'a pas besoin d'un cycle de vie infini / global, il appartient à la pile. Mais bon, c'est 1 Ko sur 4 Ko, n'y a-t-il pas d'inconvénient à allouer 25% de la RAM comme ça? (cela pourrait représenter 50% ou plus de la pile)
Quelqu'un pourrait-il partager une certaine expérience avec ce genre de situation, ou est-ce que quelqu'un pense à une raison valable de ne pas mettre ce tableau sur la pile? Je recherche des inconvénients techniques ainsi que des commentaires sur le design.
La seule chose dont je suis conscient, c'est que je dois m'assurer que j'ai réellement 1 Ko de pile libre en entrant dans cette fonction. Peut-être que c'est tout ce dont je dois m'occuper, peut-être pas.
Réponses:
Oui, et c'est une forte contrainte. Vous ferez mieux d'être sûr statiquement que si vous avez un si grand espace disponible sur la pile. Si le code est petit, si vous utilisez un GCC récent pour compiler votre code, voyez ceci .
BTW, certains microprocesseurs bon marché pourraient utiliser une "grande" trame d'appel plus coûteuse qu'une "normale" (par exemple parce que leur jeu d'instructions favoriserait un décalage d'un octet par rapport au pointeur de pile). YMMV.
De plus, si vous codez en C et si vous pensez que votre grand tableau peut voir son espace réutilisé à d'autres fins, vous pouvez envisager d'en faire un membre d'union (avec une variable globale de
union
type). Oui, c'est assez moche.Alternativement, vous pourriez envisager de coder un allocateur de tas primitif adapté à votre application (et il pourrait avoir une API différente de
malloc
&free
....).la source
gcc -flto -Os
? ) et vous pourriez gagner de la mémoire ....Les gens ont tendance à être prudents avec une grande pile, car elle croît en arrière dans la RAM et écrase les valeurs des variables, conduisant à un comportement inexplicable. Cela devient encore pire, car vous devez connaître l'adresse de pointeur de pile la plus basse possible et soustraire la taille à allouer lors de l'entrée dans la routine.
C'est tout un travail pour la gestion de la mémoire matérielle (devrait générer des interruptions ou des pannes lorsqu'un débordement de pile se produit) ou pour le compilateur, étant donné qu'il a des fonctionnalités pour ce type d'analyse.
Sinon, vous pouvez faire ce que vous voulez avec votre RAM.
la source
Comme les réponses précédentes l'ont souligné, je recommanderais également de laisser le tableau statique s'il tient dans la mémoire. Dans la plupart des cas, il est beaucoup plus important d'avoir une empreinte mémoire déterministe, même si cela signifie que vous "gaspillez" de la mémoire pour des variables non utilisées tout le temps. Placer de grands tableaux dans votre pile la fera exploser beaucoup trop facilement et les débordements de pile ont tendance à causer des problèmes difficiles à trouver et à reproduire (si vous ne pouvez pas utiliser MMU pour protéger la pile).
La suggestion de partager le bloc avec d'autres données avec union est valide pour l'OMI, même si elle peut également être source de problèmes difficiles à trouver, si vous y trouvez des variables erronées.
Si vous manquez de mémoire et avez désespérément besoin de créer des variables à durée de vie plus courte pour le partager, avant de déplacer le tableau vers la pile, j'envisagerais d'ajouter une allocation de mémoire dynamique, même si elle a ses propres inconvénients. Dans ce cas, ce n'est peut-être pas une réponse, car le tableau semble assez volumineux par rapport à la mémoire disponible.
la source
Vous avez une autre option si vous avez une sorte de stockage flash. Vous pouvez échanger la vitesse d'accès pour ram en stockant vos données en flash et en lisant et en recherchant là-bas. Vous n'auriez besoin que de charger un enregistrement à la fois dans RAM. Ce sera un peu plus compliqué si vous devez pouvoir mettre à jour les enregistrements. Vous aurez besoin d'un mécanisme segmenté au niveau de l'usure. J'ai fait cela dans le passé et j'ai inclus un index pour accélérer l'accès.
la source
Surtout lorsque vous travaillez avec des systèmes embarqués, vous voulez que la plupart des échecs possibles se produisent au moment de la compilation et rien n'échoue au moment de l'exécution (bien si nous pouvions y arriver, cependant ...).
Faire de grands tableaux dont vous pourriez avoir besoin dans des états arbitraires de votre programme alloué statiquement fait exactement cela - L'éditeur de liens vous avertira finalement "cela ne rentre pas dans la RAM", tandis que l'allocation de pile ferait simplement planter votre programme avec une pile difficile à déboguer déborde.
la source