9
votes

Pourquoi un programme C ++ affecterait-il plus de mémoire pour les variables locales qu'il n'aurait besoin dans le pire des cas?

inspiré par Cette question .

Apparemment dans le code suivant: P>

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    if( GetTickCount() > 1 ) {
        char buffer[500 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    } else {
        char buffer[700 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    }
    return 0;
}


19 commentaires

Parce que le compilateur n'est pas assez intelligent pour comprendre le pire des cas?


@R. Martinho Fernandes: Peut-être que je l'ai appelé une carence dans le compilateur dans la question.


Oh. J'ai interprété cela pour signifier un compilateur de buggy.


On dirait que c'est juste un déficit optimiseur. Pourquoi allouons-vous de gros tampons sur la pile? Cela semble stupide; C'est peut-être pour la raison pour laquelle les devs ne passaient pas leur temps micro-optimisant-le :)


@Tomalak Geret'kal: Vous ne voyez pas la grande image. Supposons que vous ayez un gros interrupteur ou un énorme bloc de if-else-est ou quelque chose comme ça. Vous pourriez avoir un objet plutôt petit dans chaque branche, mais avec cette stratégie de compilateur, leurs tailles s'accumulent et qui provoque un débordement de pile.


Pour être honnête, je pense que la question que vous avez liée à répondre à votre question dans l'intégralité. Il est clair que des réponses là-bas qu'il n'y a pas de raison particulièrement bonne pour ce comportement et que c'est à quel point le compilateur veut faire.


@shachartooth: Vous auriez besoin de beaucoup de petits objets pour frapper 1 m; S'il s'agit d'un problème, votre fonction est beaucoup trop grande. N'allouez pas de gros tampons sur la pile: que est votre "grande image".


@Tomalak Geret'kal: Dans la vie réelle, vous pourriez avoir une limite de pile beaucoup plus petite et des objets avec une taille comme 50 kilo-octets. C'est le cas où nous avons trouvé ce problème. En fait, même des objets plus petits peuvent causer le problème s'il y en a beaucoup d'entre eux.


@shacharpôt: cas Extreme Edge. Votre fonction est trop grande. Décomposez votre programme dans des fonctions plus petites et arrêtez d'allouer de gros tampons sur la pile. La pile n'est pas destinée à stocker de grands objets. (Et, s'il vous plaît, il n'y a aucune raison pour être condescendante. Juste parce que nous ne savons pas que je ne sais pas de "vraie vie".)


@Tomalak Geret'kal: D'accord, je suis d'accord que c'est un cas d'avance. Je n'ai jamais eu l'intention de dire que vous ne connaissez pas la vie réelle.


Je vais avoir encore plus de bowvotes ici, mais - c'est clairement un cas d'utilisation alloca () (ok, ok, si vous êtes assez chanceux d'avoir un compilateur C99 décent - puis utilisez une matrice de taille variable).


@@ TOMALAK GERET'KAL: BTW Cette stratégie peut conduire à une localité de cache beaucoup pire en cas de petits objets. Je suppose que ce n'est pas très bon.


@ SK-Logic: alloca () est seulement bon pour les pods, n'est-ce pas?


En regardant la sortie de l'assemblée de GCC, il est parfois disposé à faire l'optimisation


@Plasmahh: J'ai essayé CodePad.org - le comportement est identique, juste la limite de taille de la pile est plus élevée. Avez-vous inclus du code équivalent qui empêcherait d'éliminer les succursales et les allocations?


@shacharpôt de: Oui, j'ai utilisé Rand (), sur Ideone Ça marche bien: ideone.com/vhlwp Regarder Le code d'assembleur généré Il réserve 2068 octets sur la pile. Avez-vous vérifié que vous ne compilez pas avec des options de protection de la pile de pile activées pouvant influer sur le comportement d'allocation Stackk?


@PLASMAHH: Non, cela ne fera pas - vous ne pouvez pas savoir avec certitude combien a été réellement attribué de cette manière, car l'ordre des allocations n'est pas spécifié. Ma stratégie consistait à déduire une taille maximale de la pile avant que le programme ne se bloque puis construire un si-ele avec une allocation dans chaque branche. C'était 150 mégaoctets sur CodePad.org - une allocation n'échapperait pas, mais deux branches avec 120/150 le feraient.


@Plasmahh: Ici c'est ideone.com/f7iy2 - lorsque les deux premières lignes sont non motivées et que tous les autres sont commentés ça va bien.


@shachartooth: ça va faire; Au moins sur Linux, car la pile grandit automatiquement là-bas, en regardant la sortie de montage suffira, et vous pouvez voir que les cadres sont ajustés pour être 2068 octets


4 Réponses :


-3
votes

Page d'exploitation et alignement des octets pourrait être un facteur. L'entretien ménager peut également utiliser une pile supplémentaire avec de l'espace requis pour appeler d'autres fonctions dans cette fonction.


5 commentaires

Qu'est-ce que cela a à voir avec la question?


-1, devinette sauvage. Et quelle "maison de maison" consumerait 500 Ko sur la pile?


Avec un tel ménage, je préférerais un autre compilateur.


Avec ce ménage, je préférerais une plus grande maison.


Le système d'exploitation glisse généralement une page - même s'il s'agit d'un octet.



2
votes

Je ne peux que spéculer que cette optimisation a été jugée trop sans importance par les concepteurs de compilateur. Ou peut-être, il y a une raison subtile de sécurité.

BTW, sous Windows, Stack est réservé dans son intégralité lorsque le thread commence l'exécution, mais est engagé sur la base nécessaire, de sorte que vous ne passez pas vraiment beaucoup de mémoire "réelle" même si vous réservé une grande pile.

Réservation d'une grande pile peut être un problème sur le système 32 bits, où disposer d'un grand nombre de threads peuvent manger l'espace d'adressage disponible sans vraiment vous engager beaucoup de mémoire. Sur 64 bits, vous êtes doré.


1 commentaires

Je pense qu'il pourrait y avoir une raison de sécurité, mais je suis très curieux de savoir ce que cela peut être.



2
votes

Cela pourrait être réduit à votre utilisation de SecuryzeroMorie. Essayez de le remplacer par une zéromémorique régulière et voyez ce qui se passe - la page MSDN indique essentiellement que SZM a une certaine sémantique supplémentaire au-delà de ce que la signature implique, et ils pourraient être la cause du bogue.


3 commentaires

La seule chose spéciale avec Securyzerzeromemory () est que son contenu n'est pas montré au compilateur. Si je le remplace par Zeromemory () L'allocation est optimisée. De plus, le comportement est exactement le même lorsque j'utilise memset () suivi de printf () sur codpead.org et ideone.com


Securyzeromemory Ne pas être optimisé ne devrait pas affecter la capacité du compilateur à chevaucher les allocations de stockage. Dans les deux branches, le stockage est solidement mis à zéro. Personne ne devrait se soucier de ce qui se passe après cela, et les cas ifs, alors-sinon sont mutuellement exclusifs par définition. Me regarde comme un compilateur muet.


@Bo Persson: Quel que soit le code équivalent sans SecuryzeroMory () montre le même comportement sur codépad.org/ideone.com



1
votes

Le code suivant lors de la compilation de GCC 4.5.1 sur Ideone place les deux tableaux à la même adresse: < Pré> xxx

entrée: 6

sortie:

0xBF8E9B1C
0xbf8e9b1c

La réponse est probablement "Utiliser un autre compilateur" si vous souhaitez cette optimisation.


1 commentaires

Est-ce que ce compilateur spécifique est considéré comme très efficace et utilisant de très bonnes techniques d'optimisation?