2009-04-11 5 views
6

Mam do czynienia z wieloma ciągami w moim programie. Te dane ciągu nie zmieniają się przez cały czas życia po tym, jak zostaną wczytane do mojego programu.Jak zwolnić nieużywaną pojemność ciągu

Ale ponieważ łańcuch C++ rezerwuje pojemność, marnują dużo miejsca, które nie będą używane na pewno. Próbowałem zwolnić te przestrzenie, ale to nie zadziałało.

Poniżej znajduje się prosty kod, który próbowałem:

string temp = "123456789"; 
string str; 

cout << str.capacity() << endl; 

str.reserve(16);  
cout << str.capacity() << endl;  
// capacity is 31 on my computer  

str += temp;  
cout << str.capacity() << endl;  

str.reserve(16);  
cout << str.capacity() << endl;  
// can't release. The capacity is still 31. 

(Kompilator Visual C++)

Jak mogę zwolnić?

+0

Have zabrakło ci pamięci? Jeśli nie, jest to przedwczesna optymalizacja. Jeśli skończy Ci się pamięć, to które obiekty RZECZYWISTE dominują przy przydzielaniu pamięci? Chodzi o to, że tego rodzaju rzeczy nie są pomocne. –

+0

Nie ma sposobu, aby wymusić na alokatorze użycie mniejszej liczby bajtów ** do wstępnej konstrukcji danego ciągu znaków ** - a przynajmniej nie bez napisania własnego szablonu klasy przydziału STL (i prawdopodobnie nawet wtedy). –

Odpowiedz

12

Po wywołaniu reserve, tworzysz żądanie, aby zmienić pojemność. Wdrożenia gwarantują tylko rezerwację liczby równej lub większej niż ta kwota. W związku z tym żądanie do zmniejszyć pojemność może być bezpiecznie ignorowane przez określoną implementację.

Zachęcam jednak do rozważenia, czy nie jest to przedwczesna optymalizacja. Czy na pewno robisz tak wiele ciągów, że jest to wąskie gardło dla Ciebie? Czy jesteś pewien, że to właśnie wspomnienie jest wąskie gardło?

Z dokumentacji reserve:

Można poszerzyć lub zmniejszyć wielkość powierzchni magazynowej w łańcuchu, chociaż zawiadomienie, że otrzymany pojemność po wywołaniu tej funkcji niekoniecznie jest równy res_arg , ale może być równy lub większy niż res_arg, dlatego zmniejszanie liczby żądań może lub nie może spowodować rzeczywistą redukcję przydzielonej przyznanej miejsca w konkretnej implementacji biblioteki . W żadnym wypadku to nigdy nie przycina zawartość ciągu (dla tego celów, zobacz zmianę rozmiaru lub wyczyść, który modyfikować treść).

+0

+1 brzmi jak przedwczesna optymalizacja. –

5

Dlaczego nie używasz tablicy znaków?

+1

+1. Tablicę znaków biblioteki można przekształcić w std :: string, jeśli chcesz go użyć i później zniszczyć. – GogaRieger

+0

Zakładając, że masz na myśli alokację dynamiczną, nawet to prawdopodobnie "zmarnuje" co najmniej 4 bajty na długość, a prawdopodobnie wewnętrzny alokator dynamiczny prawdopodobnie przydzieli w blokach nie mniejszych niż 16/32 bajtów ze względów wydajnościowych. –

3

Myślę, że możesz użyć metody zamiany w celu uwolnienia danych. zamień go na pusty lokalny łańcuch, aby po zwolnieniu lokalnego ciągu poza pamięć, pamięć została zwolniona.

+0

+1. Jest to dobry (choć nie gwarantowany) sposób na zwrócenie pamięci, jeśli chcesz ustawić ciąg znaków na coś innego. Chociaż nie pomoże to w przycinaniu miejsca, jeśli podstawowy alokator zaokrągli go do najbliższych 16 bajtów (jak wydaje się robić MSVC++), nic nie pomoże. –

0

Nie ma gwarantowanej minimalnej pojemności dla std::string. Możesz poprosić o dowolną pojemność, dzwoniąc pod numer reserve, ale konkretna implementacja gwarantuje, że ustawi pojemność na wyższą lub równą wymaganej wielkości.

Oto zmodyfikowana wersja programu, który testuje kilka metod ciąg kurczenia:

#include <string> 
#include <iostream> 
using namespace ::std; 

template< typename S > 
S & reserve_request(S & s, typename S::size_type n) { 
    s.reserve(n); return s; 
} 

template< typename S > 
S & shrink_request1(S & s) { s.reserve(); return s; } 

template< typename S > 
S & shrink_request2(S & s) { S(s).swap(s); return s; } 

template< typename S > 
S & shrink_request3(S & s) { S(s.c_str()).swap(s); return s; } 

template< typename S > 
void test(S & s) { cout << s.capacity() << endl; } 

int main() { 
    string temp = "123456789"; // length 16 
    string str; 

    test(str);       // 15 
    test(reserve_request(str, 16)); // 31 
    test(str += temp);     // 31 
    test(reserve_request(str, 16)); // 31 
    test(shrink_request1(str));  // 31 
    test(shrink_request2(str));  // 31 
    test(shrink_request3(str));  // 31 
    return 0; 
} 

Wydaje się, że Visual C++ 's std::string zwykle utrzymuje pewne wolne moce produkcyjne.

Jeśli twój projekt ładuje dużą liczbę napisów z zewnętrznego źródła, którego rozmiar nigdy się nie zmienia, lepiej być (jak sugerowali inni) przechowywać je w jednym dużym bloku pamięci znaków oddzielonych znakami '\0' (tj. jako C-struny). Jeśli chcesz, możesz podać funkcje opakowania, które zwrócą w locie std::string.

4

literowania Naveen's answer: że jeśli bazowy podzielnik przydziela więcej niż n bajtów przy konstruowaniu n (np przez zaokrąglenie w górę do najbliższej 32 jak wynika

string x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; 
cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends not to work (although in theory it could): 
//x = "XYZ"; 
//cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends to work (although in theory it might not): 
string("XYZ").swap(x); 
cerr << x.capacity() << "\n"; // MSVC++: 15 g++: 3 

Uwaga string długości MSVC++ do do), nie ma możliwości użycia mniejszej liczby bajtów. Ale prawdopodobnie i tak byś tego nie chciał, ponieważ to "zaokrąglanie" jest wykonywane, aby proces dynamicznej alokacji pamięci był bardziej wydajny, a także ma skutek uboczny polegający na szybszym łączeniu krótkich łańcuchów (ponieważ mniej potrzebnych re-alokacji wystąpić).

1

Jest to głównie związane z konkretną realizacją. Pomysł polega na zminimalizowaniu fragmentacji pamięci. Łatwo udowodnić, że podwojenie istniejącego rozmiaru za każdym razem, gdy blok zostanie rozszerzony, ogranicza zarówno liczbę przydziałów, jak i fragmentację pamięci. Dlatego zwykle implementacje kontenerów STL podwajają istniejący blok podczas rozszerzania.

Jedną z rzeczy, które możesz zrobić, to użyć niestandardowego przydziału, który nie przydzieli więcej niż to konieczne, następnie skonstruuj obiekty std :: string, gdy nie będziesz już potrzebował ich manipulować (lub po zakończeniu manipulacji, po prostu zamień na nowy std :: sting object - jest to w zasadzie to, co inni zrobili w swoich odpowiedziach) i wreszcie można użyć pulowanego alokatora pamięci w celu zminimalizowania fragmentacji pamięci, marnowania luzu i poprawy wydajności.

Patrz:

http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079 http://www.sjbrown.co.uk/2004/05/01/pooled-allocators-for-the-stl/ http://www.codeproject.com/KB/stl/blockallocator.aspx

Szukaj "STL podzielnik" i "Memory Pool"

4

Try swapu-tricka std::string się kurczyć struny:

std::string(str.data(), str.size()).swap(str) 

Gdzie str to ciąg, który chcesz zmniejszyć.

0

pojemność nigdy nie będzie mniejsza niż 15 z dinkumware STL. std :: basic_string ma związek, który jest wskaźnikiem do przydzielonego buforu lub bufor 16 bajtów (jeśli pojemność() < = 15) (ciągów znaków)

zobacz plik nagłówka xstring

w przykład podajesz tam, gdzie rezerwujesz 16, a faktycznie rezerwujesz 17 (jeden na zero), który ma wartość> 16, więc jest przydzielany zamiast buforowany w 16 bajtach w unii wskaźnika bufora. Przydział ten podwaja poprzedni rozmiar (16), więc otrzymasz 32. Pojemność tego ciągu wynosi prawdopodobnie 31.

Ale to zależy od implementacji STL.

Zmiana parametru szablonu przydziału w szablonie decl std :: basic_string NIE jest wystarczająca - wybór kiedy i ile jest w std :: basic_string rośnie algorytm NIE w alokatorze. Podwojenie poprzedniego rozmiaru (i kurczy gdy < 1/4) jest standardem rzeczy w Wprowadzenie do algorytmów - Cormen Lieserson Rivest Stein

Nie wiesz o kurczącej algo w dinkumware .......