Bonjour,
Je cherche a connaitre le plus précisément possible l'occupation mémoire
d'une instance de classe ayant des membres du type std::vector< std::string
> et aussi std::map< std::string, std::string >.
Le problème est : sizeof() ne donnera pas la vraie taille, ni même un
sizeof() sur chaque élément de la collection. Quant à sommer tous les
std::string.size(), cela ne marchera pas non plus (cas des petites chaines,
certaines implémentations allouant d'office 15 chars par chaine).
Y-aurait-il une méthode "générique", ou faut-il se palucher l'implémentation
de la STL à la main ?
Merci !
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation complète du process, mais juste l'occupation en mémoire des instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système général d'allocation dynamique a des frais généraux -- si tu fais un new char[1], par exemple, il est probable que ça occupe 8, 16 ou 32 octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas non plus une évaluation à l'octet près. Mais je ne veux pas avoir une erreur de l'ordre de 50 ou pire 100% !
Dans le cas suivant :
{ string s = "toto"; }
je veux juste savoir si j'ai "alloué" strlen("toto") octets+les frais (frais indépendant de ma chaine) pour pouvoir majoré mes alloc. Si mes frais dépendent de ma chaine, etc.. Si mon eval me renvoie 1Mo alors qu'en fait c'est 1Mo et 1ko, c'est pas grave. Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti, ca me gène bcp plus.
A savoir : mes string et mon map seront const apres construction, et désalloué en bloc. J'ai besoin d'un std::map pour ses capacités de recherche rapide. Je prefere les std::string pour ne pas avoir à gérer moi même les alloc dynamique. A defaut d'estimation proche, est ce que : sizeof(std::map) + map::size() * sizeof(map::element::type) + somme_des_strlen_de_tous_les_strings() ne minorera/majorera pas trop ? Merci pour vos idées.
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation
complète du process, mais juste l'occupation en mémoire des
instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système général
d'allocation dynamique a des frais généraux -- si tu fais un new
char[1], par exemple, il est probable que ça occupe 8, 16 ou 32
octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas non plus une
évaluation à l'octet près. Mais je ne veux pas avoir une erreur de l'ordre
de 50 ou pire 100% !
Dans le cas suivant :
{
string s = "toto";
}
je veux juste savoir si j'ai "alloué" strlen("toto") octets+les frais (frais
indépendant de ma chaine)
pour pouvoir majoré mes alloc. Si mes frais dépendent de ma chaine, etc..
Si mon eval me renvoie 1Mo alors qu'en fait c'est 1Mo et 1ko, c'est pas
grave.
Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti, ca me gène
bcp plus.
A savoir : mes string et mon map seront const apres construction, et
désalloué en bloc. J'ai besoin d'un std::map pour ses capacités de recherche
rapide. Je prefere les std::string pour ne pas avoir à gérer moi même les
alloc dynamique.
A defaut d'estimation proche, est ce que :
sizeof(std::map) + map::size() * sizeof(map::element::type) +
somme_des_strlen_de_tous_les_strings()
ne minorera/majorera pas trop ?
Merci pour vos idées.
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation complète du process, mais juste l'occupation en mémoire des instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système général d'allocation dynamique a des frais généraux -- si tu fais un new char[1], par exemple, il est probable que ça occupe 8, 16 ou 32 octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas non plus une évaluation à l'octet près. Mais je ne veux pas avoir une erreur de l'ordre de 50 ou pire 100% !
Dans le cas suivant :
{ string s = "toto"; }
je veux juste savoir si j'ai "alloué" strlen("toto") octets+les frais (frais indépendant de ma chaine) pour pouvoir majoré mes alloc. Si mes frais dépendent de ma chaine, etc.. Si mon eval me renvoie 1Mo alors qu'en fait c'est 1Mo et 1ko, c'est pas grave. Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti, ca me gène bcp plus.
A savoir : mes string et mon map seront const apres construction, et désalloué en bloc. J'ai besoin d'un std::map pour ses capacités de recherche rapide. Je prefere les std::string pour ne pas avoir à gérer moi même les alloc dynamique. A defaut d'estimation proche, est ce que : sizeof(std::map) + map::size() * sizeof(map::element::type) + somme_des_strlen_de_tous_les_strings() ne minorera/majorera pas trop ? Merci pour vos idées.
kanze
Amerio wrote:
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation complète du process, mais juste l'occupation en mémoire des instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système général d'allocation dynamique a des frais généraux -- si tu fais un new char[1], par exemple, il est probable que ça occupe 8, 16 ou 32 octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas non plus une évaluation à l'octet près. Mais je ne veux pas avoir une erreur de l'ordre de 50 ou pire 100% !
Le problème, c'est que tout ça tient beaucoup de l'implémentation. On peut parfois obtenir un résultat à un ordre de grandeur près (ça se mésure en centaines de kilooctets, et non en mégaoctets ou en dizaines de kilooctets), mais c'est à peu près tout.
Dans le cas suivant :
{ string s = "toto"; }
je veux juste savoir si j'ai "alloué" strlen("toto") octets+les frais (frais indépendant de ma chaine)
Dans beaucoup d'implémentations de string aujourd'hui, ça n'alloue rien en dehors des « frais » -- c-à-d qu'il utilise sizeof(string) octets, sur la pile, et ne fait pas la moindre allocation dynamique. Mais ça ne vaut que pour les chaînes jusqu'à une certaine taille (typiquement 16 ou 32 octets), et pas dans toutes les implémentations.
Typiquement aussi, la taille allouée dépend de comment on a construit la chaîne :
Ici, il y a de fortes chances que la mémoire allouée pour s2 soit plus que pour s1, même si les chaînes aient la même longueur. Combien de plus, en revanche, dépend de l'implémentation et de la longueur des chaînes. Selon l'implémentation aussi, si tu affectes la chaîne construite par s2 à une autre chaîne, l'allocation pour la nouvelle chaîne serait grande comme celle de s2 ou comme celle de s1.
Pour une utilisation donnée et une implémentation donnée, on peut parfois faire une estimation plus précise. Par exemple, si je construis les chaînes caractère par caractère (par exemple en les lisant d'un fichier), puis je ne garde qu'une copie (faite par le constructeur de copie ou l'opérateur d'affectation), avec l'implémentation de std::string qui vient avec g++, je compterais grosso modo à une utilisation de la mémoire de 1,5 fois le nombre de caractères -- pour plus de précision, j'ajouterais 14 ou 15 octets par chaîne, mais à mon avis, au moins que les chaînes soient très courtes, c'est une fausse précision ; elle changera le résultat d'un montant inférieur à l'imprécision du départ. Avec l'implémentation SGI/STLPort, en revanche, si les chaînes sont normalement assez longues, je compterais le nombre d'octets plus sizeof(string) par chaîne.
À mon avis, si c'est important, la seule solution valable consiste à mésurer. Sous un système de type Unix, par exemple, j'utiliserais quelque chose du genre :
Pour plus de précision, on mésurerait sur plusieurs changements de sbrk(0).
pour pouvoir majoré mes alloc. Si mes frais dépendent de ma chaine, etc.. Si mon eval me renvoie 1Mo alors qu'en fait c'est 1Mo et 1ko, c'est pas grave. Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti, ca me gène bcp plus.
Je doute que tu puisse faire mieux.
A savoir : mes string et mon map seront const apres construction, et désalloué en bloc. J'ai besoin d'un std::map pour ses capacités de recherche rapide. Je prefere les std::string pour ne pas avoir à gérer moi même les alloc dynamique.
Si la taille réele a de l'importance, std::string et std::map ne vont pas aider. Si elle n'a pas d'importance, ce n'est pas la peine de faire les mésures.
Ceci dit : un std::vector<std::pair>, préalablement trié, avec une recherche au moyen de std::lower_bound, peut s'avérer aussi rapide, sinon plus, que std::map. Et si tu connais le nombre d'entrées d'avance, et faire un reserve préalable, il utiliserait bien moins de mémoire. Les frais généraux d'un std::map sont de l'ordre de trois ou quatre pointeurs plus les frais d'une allocation dynamique par élément ; les frais généraux de la même solution dans un vector<pair>, où on a pu faire une reserve() suffissant au début, sont 0. De même, avec g++, il peut être intéressant de construire les chaînes finales au moyen de std::string( s.begin(), s.end() ) plutôt qu'avec le constructeur de copie.
Mais comme pour toute optimisation, ce n'est pas la peine de s'y mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on mésure, d'une part pour savoir ce qu'il faut optimiser, et de l'autre, pour bien savoir si nos optimisations ont eu un effet.
A defaut d'estimation proche, est ce que : sizeof(std::map) + map::size() * sizeof(map::element::type) + somme_des_strlen_de_tous_les_strings() ne minorera/majorera pas trop ?
On doit s'y rétrouver à une facteur de deux ou de trois près. Selon l'implémentation, la longueur des chaînes, et comment elles ont été créées.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Amerio wrote:
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation
complète du process, mais juste l'occupation en mémoire
des instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système
général d'allocation dynamique a des frais généraux -- si tu
fais un new char[1], par exemple, il est probable que ça
occupe 8, 16 ou 32 octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas
non plus une évaluation à l'octet près. Mais je ne veux pas
avoir une erreur de l'ordre de 50 ou pire 100% !
Le problème, c'est que tout ça tient beaucoup de
l'implémentation. On peut parfois obtenir un résultat à un ordre
de grandeur près (ça se mésure en centaines de kilooctets, et
non en mégaoctets ou en dizaines de kilooctets), mais c'est à
peu près tout.
Dans le cas suivant :
{
string s = "toto";
}
je veux juste savoir si j'ai "alloué" strlen("toto")
octets+les frais (frais indépendant de ma chaine)
Dans beaucoup d'implémentations de string aujourd'hui, ça
n'alloue rien en dehors des « frais » -- c-à-d qu'il utilise
sizeof(string) octets, sur la pile, et ne fait pas la moindre
allocation dynamique. Mais ça ne vaut que pour les chaînes
jusqu'à une certaine taille (typiquement 16 ou 32 octets), et
pas dans toutes les implémentations.
Typiquement aussi, la taille allouée dépend de comment on a
construit la chaîne :
Ici, il y a de fortes chances que la mémoire allouée pour s2
soit plus que pour s1, même si les chaînes aient la même
longueur. Combien de plus, en revanche, dépend de
l'implémentation et de la longueur des chaînes. Selon
l'implémentation aussi, si tu affectes la chaîne construite par
s2 à une autre chaîne, l'allocation pour la nouvelle chaîne
serait grande comme celle de s2 ou comme celle de s1.
Pour une utilisation donnée et une implémentation donnée, on
peut parfois faire une estimation plus précise. Par exemple, si
je construis les chaînes caractère par caractère (par exemple en
les lisant d'un fichier), puis je ne garde qu'une copie (faite
par le constructeur de copie ou l'opérateur d'affectation), avec
l'implémentation de std::string qui vient avec g++, je
compterais grosso modo à une utilisation de la mémoire de 1,5
fois le nombre de caractères -- pour plus de précision,
j'ajouterais 14 ou 15 octets par chaîne, mais à mon avis, au
moins que les chaînes soient très courtes, c'est une fausse
précision ; elle changera le résultat d'un montant inférieur à
l'imprécision du départ. Avec l'implémentation SGI/STLPort, en
revanche, si les chaînes sont normalement assez longues, je
compterais le nombre d'octets plus sizeof(string) par chaîne.
À mon avis, si c'est important, la seule solution valable
consiste à mésurer. Sous un système de type Unix, par exemple,
j'utiliserais quelque chose du genre :
Pour plus de précision, on mésurerait sur plusieurs changements
de sbrk(0).
pour pouvoir majoré mes alloc. Si mes frais dépendent de ma
chaine, etc.. Si mon eval me renvoie 1Mo alors qu'en fait
c'est 1Mo et 1ko, c'est pas grave.
Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti,
ca me gène bcp plus.
Je doute que tu puisse faire mieux.
A savoir : mes string et mon map seront const apres
construction, et désalloué en bloc. J'ai besoin d'un std::map
pour ses capacités de recherche rapide. Je prefere les
std::string pour ne pas avoir à gérer moi même les alloc
dynamique.
Si la taille réele a de l'importance, std::string et std::map ne
vont pas aider. Si elle n'a pas d'importance, ce n'est pas la
peine de faire les mésures.
Ceci dit : un std::vector<std::pair>, préalablement trié, avec
une recherche au moyen de std::lower_bound, peut s'avérer aussi
rapide, sinon plus, que std::map. Et si tu connais le nombre
d'entrées d'avance, et faire un reserve préalable, il
utiliserait bien moins de mémoire. Les frais généraux d'un
std::map sont de l'ordre de trois ou quatre pointeurs plus les
frais d'une allocation dynamique par élément ; les frais
généraux de la même solution dans un vector<pair>, où on a pu
faire une reserve() suffissant au début, sont 0. De même, avec
g++, il peut être intéressant de construire les chaînes finales
au moyen de std::string( s.begin(), s.end() ) plutôt qu'avec le
constructeur de copie.
Mais comme pour toute optimisation, ce n'est pas la peine de s'y
mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on
mésure, d'une part pour savoir ce qu'il faut optimiser, et de
l'autre, pour bien savoir si nos optimisations ont eu un effet.
A defaut d'estimation proche, est ce que :
sizeof(std::map) + map::size() * sizeof(map::element::type) +
somme_des_strlen_de_tous_les_strings()
ne minorera/majorera pas trop ?
On doit s'y rétrouver à une facteur de deux ou de trois près.
Selon l'implémentation, la longueur des chaînes, et comment
elles ont été créées.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Non, ca ne va pas. Je ne souhaite pas avoir l'occupation complète du process, mais juste l'occupation en mémoire des instances d'UNE classe parmi d'autres.
D'abord, définis ce que tu entends par ça. Tout système général d'allocation dynamique a des frais généraux -- si tu fais un new char[1], par exemple, il est probable que ça occupe 8, 16 ou 32 octets en fait.
Je ne cherche pas à quantifier ces pertes. Je ne cherche pas non plus une évaluation à l'octet près. Mais je ne veux pas avoir une erreur de l'ordre de 50 ou pire 100% !
Le problème, c'est que tout ça tient beaucoup de l'implémentation. On peut parfois obtenir un résultat à un ordre de grandeur près (ça se mésure en centaines de kilooctets, et non en mégaoctets ou en dizaines de kilooctets), mais c'est à peu près tout.
Dans le cas suivant :
{ string s = "toto"; }
je veux juste savoir si j'ai "alloué" strlen("toto") octets+les frais (frais indépendant de ma chaine)
Dans beaucoup d'implémentations de string aujourd'hui, ça n'alloue rien en dehors des « frais » -- c-à-d qu'il utilise sizeof(string) octets, sur la pile, et ne fait pas la moindre allocation dynamique. Mais ça ne vaut que pour les chaînes jusqu'à une certaine taille (typiquement 16 ou 32 octets), et pas dans toutes les implémentations.
Typiquement aussi, la taille allouée dépend de comment on a construit la chaîne :
Ici, il y a de fortes chances que la mémoire allouée pour s2 soit plus que pour s1, même si les chaînes aient la même longueur. Combien de plus, en revanche, dépend de l'implémentation et de la longueur des chaînes. Selon l'implémentation aussi, si tu affectes la chaîne construite par s2 à une autre chaîne, l'allocation pour la nouvelle chaîne serait grande comme celle de s2 ou comme celle de s1.
Pour une utilisation donnée et une implémentation donnée, on peut parfois faire une estimation plus précise. Par exemple, si je construis les chaînes caractère par caractère (par exemple en les lisant d'un fichier), puis je ne garde qu'une copie (faite par le constructeur de copie ou l'opérateur d'affectation), avec l'implémentation de std::string qui vient avec g++, je compterais grosso modo à une utilisation de la mémoire de 1,5 fois le nombre de caractères -- pour plus de précision, j'ajouterais 14 ou 15 octets par chaîne, mais à mon avis, au moins que les chaînes soient très courtes, c'est une fausse précision ; elle changera le résultat d'un montant inférieur à l'imprécision du départ. Avec l'implémentation SGI/STLPort, en revanche, si les chaînes sont normalement assez longues, je compterais le nombre d'octets plus sizeof(string) par chaîne.
À mon avis, si c'est important, la seule solution valable consiste à mésurer. Sous un système de type Unix, par exemple, j'utiliserais quelque chose du genre :
Pour plus de précision, on mésurerait sur plusieurs changements de sbrk(0).
pour pouvoir majoré mes alloc. Si mes frais dépendent de ma chaine, etc.. Si mon eval me renvoie 1Mo alors qu'en fait c'est 1Mo et 1ko, c'est pas grave. Mais si j'ai estimé à 1 Mo, mais qu'en fait j'ai 2Mo de parti, ca me gène bcp plus.
Je doute que tu puisse faire mieux.
A savoir : mes string et mon map seront const apres construction, et désalloué en bloc. J'ai besoin d'un std::map pour ses capacités de recherche rapide. Je prefere les std::string pour ne pas avoir à gérer moi même les alloc dynamique.
Si la taille réele a de l'importance, std::string et std::map ne vont pas aider. Si elle n'a pas d'importance, ce n'est pas la peine de faire les mésures.
Ceci dit : un std::vector<std::pair>, préalablement trié, avec une recherche au moyen de std::lower_bound, peut s'avérer aussi rapide, sinon plus, que std::map. Et si tu connais le nombre d'entrées d'avance, et faire un reserve préalable, il utiliserait bien moins de mémoire. Les frais généraux d'un std::map sont de l'ordre de trois ou quatre pointeurs plus les frais d'une allocation dynamique par élément ; les frais généraux de la même solution dans un vector<pair>, où on a pu faire une reserve() suffissant au début, sont 0. De même, avec g++, il peut être intéressant de construire les chaînes finales au moyen de std::string( s.begin(), s.end() ) plutôt qu'avec le constructeur de copie.
Mais comme pour toute optimisation, ce n'est pas la peine de s'y mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on mésure, d'une part pour savoir ce qu'il faut optimiser, et de l'autre, pour bien savoir si nos optimisations ont eu un effet.
A defaut d'estimation proche, est ce que : sizeof(std::map) + map::size() * sizeof(map::element::type) + somme_des_strlen_de_tous_les_strings() ne minorera/majorera pas trop ?
On doit s'y rétrouver à une facteur de deux ou de trois près. Selon l'implémentation, la longueur des chaînes, et comment elles ont été créées.
-- James Kanze GABI Software http://www.gabi-soft.fr Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Amerio
Mais comme pour toute optimisation, ce n'est pas la peine de s'y mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on mésure, d'une part pour savoir ce qu'il faut optimiser, et de l'autre, pour bien savoir si nos optimisations ont eu un effet.
Sage rappel :-) même si c'était bien mon intention dès le début. (je voulais juste être sûr de ne pas faire un choix trop trop pénalisant). Je vais donc partir en première implémentation sur un std::map avec des std::string pour la rapidité de mise en oeuvre, puis redescendre si les mesures effectives (par top() par exemple) me donnent un trop gros écart. Merci à tous.
Mais comme pour toute optimisation, ce n'est pas la peine de s'y
mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on
mésure, d'une part pour savoir ce qu'il faut optimiser, et de
l'autre, pour bien savoir si nos optimisations ont eu un effet.
Sage rappel :-) même si c'était bien mon intention dès le début.
(je voulais juste être sûr de ne pas faire un choix trop trop pénalisant).
Je vais donc partir en première implémentation sur un std::map avec des
std::string pour la rapidité de mise en oeuvre, puis redescendre si les
mesures effectives (par top() par exemple) me donnent un trop gros écart.
Merci à tous.
Mais comme pour toute optimisation, ce n'est pas la peine de s'y mettre tant qu'on n'en a pas constaté la nécessité. Et puis, on mésure, d'une part pour savoir ce qu'il faut optimiser, et de l'autre, pour bien savoir si nos optimisations ont eu un effet.
Sage rappel :-) même si c'était bien mon intention dès le début. (je voulais juste être sûr de ne pas faire un choix trop trop pénalisant). Je vais donc partir en première implémentation sur un std::map avec des std::string pour la rapidité de mise en oeuvre, puis redescendre si les mesures effectives (par top() par exemple) me donnent un trop gros écart. Merci à tous.