Программирование на C: malloc и свободно в цикле - PullRequest
5 голосов
/ 17 марта 2010

Я только начал работать с C и очень мало знаю о проблемах производительности с malloc() и free(). Мой вопрос заключается в следующем: если бы я должен был вызвать malloc() с последующим free() внутри цикла while, который повторяет, скажем, 20 итераций, он работал бы медленнее по сравнению с вызовом free() снаружи петля?

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

Ответы [ 8 ]

13 голосов
/ 17 марта 2010

Определенно медленнее. (Но помните, что вам нужно сбалансировать количество malloc и free, иначе вы получите утечку памяти.)

Если длина варьируется, вы можете использовать realloc для увеличения размера буфера.

void* v = malloc(1024);
size_t bufsize = 1024;

while(cond) {
   size_t reqbufsize = get_length();
   if (reqbufsize > bufsize) {
      bufsize = reqbufsize * 2;
      v = realloc(v, bufsize);
   }
   // you may shrink it also.

   do_something_with_buffer(v);
}

free(v);
6 голосов
/ 17 марта 2010

Вы не можете вызывать free вне цикла, если вы вызываете malloc внутри:

char * buffer;
for (int i = 0; i < num_files; i++) {
    buffer = malloc(proper_length(i));
    // do some things with buffer
}
free(buffer);

Вы будете иметь malloc'е num_files раз, но освобождены только один раз - вы вытекли из памяти всего, кроме последнего!

Есть два основных варианта - malloc перед циклом (или просто использовать массив), если вы знаете размер, который будет работать для всего, или использовать realloc:

char * buffer = NULL;
for (int i = 0; i < num_files; i++) {
    buffer = realloc(proper_length(i));
    // do some things with buffer
}
free(buffer);
6 голосов
/ 17 марта 2010

На протяжении 20 итераций вам не нужно беспокоиться о производительности malloc / free.

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

Наконец, если вы собираетесь освободить буфер, нет необходимости сначала его очищать. Даже если бы вы переместили malloc / free за пределы цикла (используя максимальный буфер, как предложено Джастином), вам не нужно явно очищать буфер.

3 голосов
/ 17 марта 2010

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

2 голосов
/ 17 марта 2010

Это зависит от того, для чего вам нужен буфер.

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

Если вам действительно нужно очистить его, вы можете использовать bzero(). Конечно, malloc'ing и free'ing на каждой итерации - пустая трата ресурсов, так как вы можете с радостью повторно использовать буфер.

Другая проблема возникнет, если вы будете распараллеливать цикл for, т. Е. Использовать несколько одновременно работающих потоков.

Простой, реальный пример: использование ведра для переноски воды. Предположим, вам нужно совершить несколько поездок с этим ковшом: имеет ли смысл взять его, использовать, опустить, снова собрать, использовать и т. Д.? Вы можете повторно использовать ведро столько раз, сколько возможно. Если, с другой стороны, корзина нужна вам и большему количеству людей, либо вы организуете доступ к корзине, либо вам нужно больше корзин.

Последнее предложение: не беспокойтесь о выступлениях сейчас . Говорят, что ранняя оптимизация - корень всего зла, и вы скоро поймете, почему.

Во-первых, поймите проблему: напишите код, который можно выбросить. Эксперимент. Во-вторых, проверьте это. Убедитесь, что он делает то, что вам нужно. В-третьих, оптимизируйте это. Сделайте цикл десять тысяч раз и измерьте, сколько времени это займет. Затем переместите malloc наружу и повторите измерение (используйте команду оболочки time, если в UNIX). В-четвертых, перепишите его, потому что ваш первый эксперимент, скорее всего, будет путаницей патчей неработающего кода.

Промыть, повторить.

ps: развлекайся тем временем. Это должно быть интересно, а не расстраивать.

1 голос
/ 17 марта 2010

Обычно все, что может быть перемещено за пределы цикла, должно быть. Зачем повторять одно и то же действие, если вы можете сделать это один раз?

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

0 голосов
/ 17 марта 2010

Обрабатывайте это лучше. Есть псевдокод:

#define BLOCK_SIZE 1024 // or about the bigger size of your strings.

char *buffer = (char *) malloc(BLOCK_SIZE) 

for(int i=0; i<20; i++)
{
   while (more bytes left to read)
   {
    read full string or BLOCK_SIZE bytes at max // most calls work this way
    proces bytes in buffer
   }
}

free(buffer);
0 голосов
/ 17 марта 2010

Это зависит от реализации malloc и free.

Лучший способ ответить на ваш вопрос - построить эталон ...

...