1
votes

Calculer la mémoire allouée de std :: string (et l'utilisation des chaînes dans std :: vector)

Je veux calculer la quantité de mémoire allouée lorsque je crée et assigne des valeurs à une chaîne.

vector<string> cache;
// fill cache with strings of dynamic length
int size = sizeof(cache);
for (int i = 0; i < cache.size(); i++)
{
    size += sizeof(cache[i]);
    size += sizeof(cache[i].at(0)) * cache[i].capacity();
}

Est-ce toute la mémoire que ma chaîne consomme? La partie initiale / statique que j'obtiens en appelant simplement sizeof (s) (soit 40 octets sur ma machine) plus la partie dynamique - la taille d'un caractère multipliée par les espaces réservés alloués pour rendre les chaînes redimensionnables efficacement ( sur ma machine, la chaîne a d'abord alloué un bloc de 15 octets jusqu'au point où le texte est trop long, donc après la deuxième affectation, la partie dynamique est de 31 octets). Pourquoi pas 16 et 32 ​​octets d'ailleurs?

Cette façon de penser est-elle correcte (statique + dynamique pour chaque chaîne est toute la mémoire qu'elle occupe) correcte?

Ce qui signifie que si j'ai un std :: vecteur de chaînes, et que je voulais également calculer toute la mémoire pour ce vecteur, je devrais faire la même chose: j'ajoute l'initiale / taille statique de mon vecteur pour obtenir le plus la partie dynamique qui signifie la mémoire totale occupée par une chaîne comme je le fais ci-dessus pour chaque chaîne à l'intérieur du vecteur?

string s = "";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;
s = "1234567890qwertz";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;

Donc, pour résumer, est-ce la bonne quantité de mémoire occupée par mon "cache"?

Modifier: fort> Ou dois-je également prendre en compte le fait qu'un std :: vector lui-même a également un .capacity ()> = .size () ce qui pourrait signifier que j'aurais réellement besoin de le faire:

pour chaque cache.capacity () - J'aurais besoin d'ajouter sizeof (cache [i]) et en plus pour chaque cache.size () - ajoutez sizeof (cache [i] .at (0)) * cache [i] .capacity () ??


16 commentaires

Je veux calculer la quantité de mémoire allouée lorsque je crée et assigne des valeurs à une chaîne - Ok, je vais demander. Pourquoi as-tu besoin de cette information?


sizeof (s) est la taille de l ' objet s , pas la chaîne elle-même. À moins qu'il y ait une optimisation de chaîne courte (qui stocke le contenu dans l'objet chaîne réel), alors un std :: string n'est en réalité rien de plus que quelques tailles et un pointeur vers les données de chaîne réelles.


@Someprogrammerdude Est-ce que ces 40 ou ces couples de tailles et un pointeur vers les données réelles s'additionnent à mesure que je mets de plus en plus d'objets chaîne dans un std :: vector?


À votre question complémentaire: stackoverflow.com/a/11752722/3552770


Bien sûr, chaque objet std :: string aura besoin d'espace.


@PaulMcKenzie Je crée un cache de données logiciel (simplement un vecteur de chaînes) et je veux savoir comment ma mémoire grandit à mesure que j'y mets plus d'éléments (chaînes). Pour que je puisse dire - quand il deviendrait plus grand que 40 Ko, je veux vider le cache et ainsi de suite ...


@Someprogrammerdude Donc, il est correct de faire quelque chose comme pour chaque chaîne dans le vecteur: sizeof (s) (qui est de 40 sur ma machine) + [taille allouée dynamiquement de cette chaîne]?


@LogicStuff Ahh oui, ça a du sens. Merci. J'ai oublié celui là.


Si vous vous souciez de 40 Ko, vous pouvez éviter d'utiliser std::vector> dans ce cas.


Obtenez de la mémoire libre avant et après l'instanciation de chaîne.


@huzzm oui, bien que certaines chaînes n'allouent pas du tout de mémoire.


@Slava C'est peut-être vrai, mais je me soucie toujours d'une réponse à ma question.


@BartekBanachewicz Oh vraiment, comment ça se fait?


@huzzm chaînes globales / statiques avec optimisation de petites chaînes.


La classe std :: string a une méthode size () qui vous donnera sa taille en octets


@Nadir plus précisément, size () renvoie le nombre de char s valides dans la chaîne. Peu importe si les caractères sont stockés dans la mémoire dynamique ou dans un tampon SSO interne. La capacity () renvoie le nombre réel de caractères alloués, mais elle ne fait pas non plus la différence entre la mémoire dynamique et SSO. Vous ne pouvez pas obtenir la vraie taille d'octet d'un std :: string sans connaître les détails internes de son implémentation.


3 Réponses :


1
votes

Il sera difficile de répondre à cette question. Naïvement, on pourrait penser que la quantité totale de mémoire consommée serait

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector_where_
                                        the_capcity_is_more_than_the_sso_amount

Mais c'est plus une limite supérieure, pas ce qui pourrait être réellement consommé. Par exemple, l'optimisation des chaînes courtes permet std :: string pour stocker les données de chaîne dans le stockage que l'objet chaîne lui-même consomme (ce que vous appelez la taille statique). Si tel est le cas, l'espace réel consommé serait

vector_capacity * sizeof(std::string)

et la capacité de chaque chaîne dans le vecteur serait juste l'espace que vous occupez sans allouer d'espace supplémentaire . Vous devrez vérifier votre implémentation pour voir si elle utilise SSO et une longue chaîne qu'elle stockera dans l'objet chaîne pour savoir si la valeur de capacité utilise l'espace interne des chaînes ou consomme réellement de la mémoire supplémentaire. Cela rend l'espace réellement consommé

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector

Dans votre calcul, sizeof (cache [i] .at (0)) n'est pas nécessaire. std :: string use char et sizeof (char) est garanti à 1

p>


5 commentaires

Merci beaucoup. C'était le genre de réponse que je cherchais.


Vous n'avez pas besoin des détails d'implémentation en soi; vous avez juste besoin de savoir quand une chaîne alloue une nouvelle mémoire. Cela peut être fait par ex. à l'aide d'un allocateur personnalisé .


J'ai à nouveau édité la question un peu car un vecteur a également une capacité> = taille. Cela change-t-il quelque chose ou fait une capacité de 15 alors qu'il n'y a que 3 chaînes à l'intérieur du vecteur signifie qu'il s'agit en fait de 15 * sizeof (string) + 3 * mémoire dynamique de chaque chaîne (capacité * taille du caractère) < / i>


@BartekBanachewicz Ok. Merci!


@huzzm C'est vrai. J'ai mis à jour la réponse pour utiliser vector_capacity * sizeof (std :: string)



1
votes

Il y a une raison simple pour laquelle la capacité de la chaîne est inférieure de un à ce que vous attendez et qui est

s.c_str()

Une chaîne C ++ est stockée dans un bloc de mémoire avec une capacité donnant la taille totale et la taille de l'espace utilisé. Mais une chaîne C est terminée par 0. La chaîne C ++ réserve un octet supplémentaire à la fin du bloc de mémoire pour stocker un 0. De cette façon, s.c_str () est toujours terminé par 0.

Donc, la mémoire utilisée par la partie dynamique de la chaîne est la capacité + 1.

Quant à la mémoire totale consommée par une chaîne ou un vecteur de chaînes, NathanOliver a répondu que je pense. Mais méfiez-vous des vecteurs contenant la même chaîne plusieurs fois.


1 commentaires

Oh oui, très vrai. J'ai oublié la résiliation nulle. Merci pour la réponse supplémentaire!



1
votes

Si vous voulez savoir combien d'espace votre std::vector<:string> utilise, calculez-le:

auto netto_memory_use(std::vector<std::string> const& x) noexcept {
    return std::accumulate(
        begin(x),
        end(x),
        sizeof x + sizeof x[0] * x.capacity(),
        [](auto n, auto&& s) {
            if (std::less<void*>()(data(s), &s)
            || std::greater_eq<void*>()(data(s) + s.capacity(), &s + 1))
                return n + s.capacity() + 1;
            return n;
        });
    }

J'ai utilisé std :: less / std::greater_eq pour en tirer parti en définissant un ordre complet, contrairement à la simple utilisation des opérateurs de comparaison. p>

L'accumulateur teste l'optimisation appliquée aux petites chaînes (SSO) avant d'ajouter la capacité de la chaîne. Bien entendu, toutes les chaînes de capacité 0 peuvent partager le même terminateur alloué statiquement. Ou la capacité et / ou la longueur peuvent être allouées avec les données-caractères.
Néanmoins, cela devrait être une bonne approximation de la mémoire utilisée, mis à part la surcharge du système de gestion de la mémoire.


2 commentaires

Qu'est-ce que data () ?


@Bouncner std :: data () trouvé par ADL.