Y a-t-il un inconvénient à allouer une énorme quantité de pile pour une seule baie dans un système embarqué?

12

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.

Tim
la source
4
Vous avez écrit: "et donc enregistrez ce 1 Ko de RAM". Gardez-le pour quoi? Ce 1 Ko doit être disponible lorsque vous en avez besoin pour le tableau, alors pourquoi ne pas faire une allocation statique? Avez-vous une autre utilisation de la mémoire lorsqu'elle n'est pas nécessaire pour la baie?
kkrambo
@kkrambo À un moment donné, nous considérons qu'un système est plein lorsque nous ne pouvons rien ajouter de plus sur la RAM, que ce soit statique ou sur la pile. Si nous ne mettons ce tableau sur la pile que lorsque nous l'utilisons, il laisserait place à une autre fonctionnalité tant qu'ils ne sont pas utilisés en même temps. Mais la question est tout à fait légitime, pour le moment si nous ne changeons rien dans le SW, nous n'avons pas besoin de plus de RAM;)
Tim
1
Pourriez-vous préciser si ce tableau a un contenu qui reste toujours le même ou s'il change lorsque la fonction qui l'utilise est invoquée?
Blrfl
@Blrfl Il change à chaque appel de la fonction.
Tim

Réponses:

8

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.

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 uniontype). 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....).

Basile Starynkevitch
la source
1
Merci, je m'attendais un peu à ce genre de réponse, c'est-à-dire qu'il reste alloué statiquement pour être sûr que je ne me retrouve pas avec un débordement de pile. Malheureusement, je n'ai pas de compilateur GCC récent et le code n'est pas petit.
Tim
Ne pouvez-vous pas obtenir (peut-être en compilant GCC à partir de son code source) un compilateur GCC pour votre plate-forme?
Basile Starynkevitch
2
J'en ai déjà un, c'est vraiment très vieux. Travailler sur un nouveau n'est pas de mon ressort, et cela ne rentrerait pas dans mon emploi du temps. Mais bien sûr, certaines choses seraient plus faciles avec des outils à jour.
Tim
3
Demander à votre patron de vous procurer un GCC plus récent (soit en vous fournissant des outils binaires à jour, soit en vous donnant du temps pour la recompilation de GCC) vaut à mon humble avis, car il est très probable que les GCC plus récents optimiseraient légèrement mieux (le savez-vous gcc -flto -Os? ) et vous pourriez gagner de la mémoire ....
Basile Starynkevitch
2
Il est sur la bonne voie, mais cela n'arrivera pas avant un certain temps. J'utilise déjà -Os sur d'autres systèmes avec des chaînes d'outils plus récentes, mais je n'étais pas au courant de ce paramètre -flto, merci de l'avoir signalé. (Notez que j'ai fait quelques tests avec le paramètre GCC -Os et -O1-3 il y a quelques semaines sur GCC 5.4.1 et que la RAM était la même tout le temps. Le FLASH et le temps de fonctionnement du MCU étaient cependant différents. Ce n'était pas sur le même MCU que celui que je mentionne dans ce Q / A)
Tim
6

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.

Martin Sugioarto
la source
4

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.

MaKo
la source
1

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.

Tereus Scott
la source
1
J'ai également fait cela dans le passé, mais ma question portait davantage sur les problèmes que je pourrais rencontrer si je mettais cela sur la pile, pas vraiment sur les alternatives à la RAM lorsque nous en manquions. Merci d'avoir répondu quand même
Tim
1

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.

tofro
la source