Выделение вручную в объекте stringbuffer - PullRequest
0 голосов
/ 02 сентября 2011

Для небольшого встраиваемого приложения я написал несколько функций + struct, которые работают как String Buffer (аналогично std :: stringstream в C ++).

Хотя код как таковой работает нормально,Есть несколько не столь незначительных проблем:

  • Я никогда прежде не писал функций на C, которые вручную распределяют и , используя растущую память, поэтому, боюсь, еще есть некоторыепричуды, которые еще должны быть учтены
  • Кажется, что код выделяет гораздо больше памяти, чем на самом деле, что ОЧЕНЬ ПЛОХО
  • Из-запредупреждения, о которых сообщал valgrind, я переключился с malloc на calloc в одном месте кода, который успешно удалил предупреждение, но я не совсем уверен, правильно ли я его использую

Пример того, что я имею в виду, что он выделяет больше, чем ему действительно нужно (используя файл 56 КБ):

==23668== HEAP SUMMARY:
==23668==     in use at exit: 0 bytes in 0 blocks
==23668==   total heap usage: 49,998 allocs, 49,998 frees, 1,249,875,362 bytes allocated

... Это просто не выглядит правильным ...

Данный код находится здесь (слишком большой, чтобы скопировать его в поле <code> на SO): http://codepad.org/LQzphUzd

Нужна помощь, и я благодарен за любой совет!

Ответы [ 4 ]

2 голосов
/ 02 сентября 2011

То, как вы увеличиваете свой буфер, довольно неэффективно. Для каждого небольшого фрагмента строки вы используете realloc () память, что может означать выделение новой памяти и копирование содержимого «старой» памяти. Это медленно и фрагментирует вашу кучу.

Лучше вырасти в фиксированных количествах или в фиксированных процентах, то есть сделать новый размер в 1,5 или 2 раза больше старого. Это также приводит к бесполезной трате памяти, но делает кучу более удобной в использовании и делает не так много копий.

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

Я бы ввел функцию "FstrBuf_Grow", которая позаботится обо всем этом. Вы просто называете это с объемом памяти, который хотите добавить, и FstrBuf_Grow позаботится о том, чтобы емкость соответствовала требованиям, перераспределяя при необходимости и, по крайней мере, столько, сколько необходимо.

...

void FstrBuf_Grow(FstringBuf *buf, size_t more)
{
    while (buf->length + more) > buf->capacity
        buf->capacity = 3 * buf->capacity / 2;
    buf->data = realloc(buf->data, buf->capacity + 1);
}            

Это умножает capacity на 1,5, пока data не станет достаточно большим. Вы можете выбирать разные стратегии в зависимости от ваших потребностей.

1 голос
/ 02 сентября 2011

strncat(ptr->data, str, len);, переместитесь перед ptr->length = ((ptr->length) + len); и используйте strncpy(ptr->data+ptr->length...ptr = NULL; в Destroy бесполезен.

Код «библиотеки» кажется правильным, НО учтите, что вы постоянно перераспределяете буфер.Обычно вы должны пытаться увеличивать буфер только в редких случаях (например, каждый раз, когда вам нужно увеличить буфер, вы используете max (2 * текущий размер, 4) в качестве нового размера), потому что увеличение буфера равно O (n).Распределение большой памяти, вероятно, связано с тем, что в первый раз вы выделяете небольшой буфер.Затем вы перераспределяете его в больший буфер.Затем вам нужно перераспределить его в буфере еще больше, и куча будет расти.

0 голосов
/ 03 сентября 2011

Как правило, для встроенных приложений гораздо лучше выделить круговой буфер FIFO, в 1-3 раза превышающий максимальный размер сообщения.

0 голосов
/ 02 сентября 2011

Похоже, что вы перераспределяете буфер для каждого добавления.Разве вы не должны выращивать его только тогда, когда хотите добавить больше, чем оно может вместить?

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

...