Как освободить неиспользованную емкость строки - PullRequest
6 голосов
/ 11 апреля 2009

Я имею дело с большим количеством строк в моей программе. Эти строковые данные не меняются в течение всей жизни после того, как они были прочитаны в моей программе.

Но поскольку строка C ++ резервирует емкость, они тратят много места, которое наверняка не будет использовано. Я пытался освободить эти пробелы, но это не сработало.

Ниже приведен простой код, который я пробовал:

string temp = "1234567890123456";
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.

(компилятор Visual C ++)

Как я мог выпустить это?

Ответы [ 8 ]

12 голосов
/ 11 апреля 2009

Когда вы звоните reserve, вы делаете запрос на изменение емкости. Реализации будут гарантировать только то, что число, равное или превышающее эту сумму, зарезервировано. Следовательно, конкретная реализация может безопасно игнорировать запрос на сокращение емкости.

Однако я рекомендую вам подумать, не является ли это преждевременной оптимизацией. Вы уверены, что действительно делаете так много строк, что для вас это узкое место в памяти? Вы уверены, что узким местом является именно память?

Из документации на reserve:

Это может увеличить или уменьшить размер место для хранения в строке, хотя обратите внимание, что в результате емкость после вызова этой функции не обязательно равен res_arg но может быть как равным, так и большим чем res_arg, следовательно, уменьшается запросы могут или не могут произвести фактическое сокращение выделенных пространство в определенной библиотеке реализация. В любом случае, это никогда обрезает содержимое строки (для этого цели, см. изменить размер или очистить, что изменить содержание).

5 голосов
/ 11 апреля 2009

Почему бы вам не использовать массив символов?

4 голосов
/ 14 апреля 2009

Попробуйте std::string трюк подкачки, чтобы уменьшить ваши строки:

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

Где str - строка, которую вы хотите сократить до размера.

4 голосов
/ 11 апреля 2009

Разъяснение Ответ Навина :

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

Обратите внимание, что если базовый распределитель выделяет более n байтов при построении string длины n (например, путем округления до ближайшего 32, как MSVC ++, кажется, делает) , нет способа заставить его использовать меньше байтов . Но вы, вероятно, не захотите делать это в любом случае, так как это «округление» сделано для того, чтобы сделать процесс динамического выделения памяти более эффективным, а также побочным эффектом ускорения конкатенации коротких строк в среднем (поскольку требуется меньше перераспределений). произойти).

3 голосов
/ 11 апреля 2009

Я думаю, вы можете использовать метод подкачки для освобождения данных. поменяйте его местами с пустой локальной строкой, чтобы, когда локальная строка вышла из области видимости, память освободилась.

1 голос
/ 11 апреля 2009

В основном это зависит от реализации. Идея состоит в том, чтобы минимизировать запросы выделения и фрагментации памяти. Легко доказать, что, удваивая существующий размер при каждом расширении блока, количество выделений и фрагментация памяти сводятся к минимуму. Поэтому обычно реализации контейнера STL удваивают существующий блок при расширении.

Одна вещь, которую вы можете сделать, это использовать пользовательский распределитель, который не будет выделять больше, чем необходимо, затем создать ваши объекты std :: string, когда вам больше не нужно манипулировать ими (или когда вы закончите манипулировать, просто поменяйте местами на новый std :: sting объект - это, в основном, то, что другие сделали в своих ответах) и, наконец, вы можете использовать распределитель памяти в пуле, чтобы минимизировать фрагментацию памяти, потратить впустую и улучшить производительность.

См:

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

Поиск "STL Allocator" и "Pool Pool"

0 голосов
/ 17 апреля 2009

емкость НИКОГДА не будет меньше 15 с dinkumware STL. std :: basic_string имеет объединение, которое является указателем на выделенный буфер или 16-байтовый буфер (если емкость () <= 15) (для строковых символов) </p>

см. Заголовочный файл xstring

в приведенном вами примере, где вы резервируете 16, вы на самом деле резервируете 17 (один для нуля), который> 16, поэтому он размещается, а не кэшируется в 16 байтах в объединении указателей кэша. Это распределение удваивает предыдущий размер (16), так что вы получите 32. Емкость этой строки будет, вероятно, 31.

Но это зависит от реализации STL.

Изменение параметра шаблона распределителя в шаблоне decl из std :: basic_string НЕ достаточно - выбор того, когда выделять и сколько, находится в алгоритме роста std :: basic_string НЕ в распределителе. Удвоение предыдущего размера (и уменьшение при <1/4) является стандартным материалом в разделе Введение в алгоритмы - Кормен Лизерсон Ривест Штайн </p>

Не уверен насчет уменьшающегося алгоритма в dinkumware .......

0 голосов
/ 11 апреля 2009

Нет гарантированной минимальной вместимости для std::string. Вы можете запросить любую емкость, которую хотите, позвонив по номеру reserve, но конкретная реализация гарантирует только установку емкости на некоторое количество, большее или равное запрошенному размеру.

Вот модифицированная версия вашей программы, которая тестирует несколько методов сжатия строк:

#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 = "1234567890123456";    // 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;
}

Может показаться, что Visual C ++ std::string обычно сохраняет некоторую свободную емкость.

Если ваш проект загружает большое количество строк, считываемых из внешнего источника, размер которого никогда не изменяется, вам может быть лучше (как другие предлагали) хранить их в одном большом блоке символьной памяти, разделенной '\0' символами (т. е. как C-струны). Если хотите, вы можете предоставить функции-оболочки, которые возвращают std::string s на лету.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...