Насколько плохо продолжать вызывать malloc() и free()?

Я отправляю текстовый файл - клиент-сервер разбивает текст на пакеты по 512 байт каждый, но некоторые пакеты содержат текст меньше максимального размера, поэтому на стороне сервера при получении каждого пакета я вызываю malloc() для повторного построения строки , это плохая практика? лучше ли сохранить рабочий буфер, который может соответствовать максимальной длине, и продолжать повторять, копировать и перезаписывать его значения?

хорошо @n.m. вот код, это если внутри цикла for(;;), пробуждаемого select()

if(nbytes==2) {
            packet_size=unpack_short(short_buf);
            printf("packet size is %d\n",packet_size);
            receive_packet(i,packet_size,&buffer);
            printf("packet=%s\n",buffer);
            free(buffer);
}
//and here is receive_packet() function 
int receive_packet(int fd,int p_len,char **string) {
 *string = (char *)malloc(p_len-2); // 2 bytes for saving the length    
 char *i=*string;   
 int temp;
 int total=0;
 int remaining=p_len-2;
 while(remaining>0) {
     //printf("remaining=%d\n",remaining);
     temp = recv(fd,*string,remaining,0);
     total+=temp;
     remaining=(p_len-2)-total;
     (*string) += temp;
 }
 *string=i;
 return 0;
 }

person cap10ibrahim    schedule 30.09.2011    source источник
comment
Строка кода стоит тысячи комментариев.   -  person n. 1.8e9-where's-my-share m.    schedule 30.09.2011
comment
@н.м. Добавил код в основной пост   -  person cap10ibrahim    schedule 30.09.2011


Ответы (7)


В вашем примере ваша функция уже содержит системный вызов, поэтому относительная стоимость malloc/free будет практически неизмеримой. В моей системе цикл malloc/free туда и обратно занимает в среднем около 300 циклов, а самые дешевые системные вызовы (получение текущего времени, pid и т. д.) стоят не менее 2500 циклов. Ожидайте, что recv легко будет стоить в 10 раз дороже, и в этом случае стоимость выделения/освобождения памяти будет составлять не более 1% от общей стоимости этой операции.

Конечно, точное время будет варьироваться, но приблизительные порядки величины должны быть довольно постоянными в разных системах. Я бы даже не начал рассматривать удаление malloc/free в качестве оптимизации, за исключением функций, которые являются чисто пользовательскими. Где, вероятно, на самом деле более ценно обойтись без динамического выделения, так это в операциях, которые не должны иметь случаев сбоя. Здесь ценность заключается в том, что вы упрощаете и усложняете свой код, не прибегая к беспокоиться о том, что делать, когда malloc терпит неудачу.

person R.. GitHub STOP HELPING ICE    schedule 30.09.2011
comment
Мне трудно поверить, что ваш malloc/free занимает всего 300 циклов. Используете ли вы фрагментированную кучу и включаете malloc, которые должны получать новую память из ОС и освобождать ее? - person Zan Lynx; 04.10.2011
comment
Я измеряю время free(malloc(1)); (в Linux/glibc/i686, измеряя с помощью rdtsc), когда не нужно отображать новую память из ОС, а просто повторно использовать существующую ранее освобожденную память. Это почти всегда будет распространенным случаем. Единственный случай, когда вам следует беспокоиться о времени получения новой памяти из ОС, — это программирование в реальном времени, когда вам важна наихудшая задержка любой операции, а не общее время выполнения вашей программы. - person R.. GitHub STOP HELPING ICE; 04.10.2011
comment
Было бы лучшим тестом использовать массив из 10-100 распределений, инициализированных NULL, и слоты случайного распределения malloc/free случайных размеров. - person Zan Lynx; 04.10.2011
comment
Не стесняйтесь засечь время и опубликовать результаты в качестве комментария или ответа.. :-) - person R.. GitHub STOP HELPING ICE; 04.10.2011
comment
Хорошо. Интересный материал. Вот что я нашел для 16 случайных слотов, выделяющих до 16384 байтов: tsc средний цикл = 247 tsc самого длинного цикла = 833292 - person Zan Lynx; 04.10.2011
comment
А для 512 слотов до 163840 байт каждый: tsc среднего цикла = 408 tsc самого длинного цикла = 294350 - person Zan Lynx; 04.10.2011
comment
Я считаю, что стоимость операции malloc зависит от времени операционной системы, которая буквально должна выделять память для структуры, это хорошо известная проблема в курсе ОС. поэтому время может варьироваться в зависимости от ОС, перегрузки системы и т. д. - person TripleS; 18.04.2013
comment
Предположим, программа использует переменную массива в функции, которая вызывается миллионы раз. Оставляя в стороне все обычные проблемы с локальным и глобальным объявлением, проще ли на оборудовании выполнять malloc и освобождать массив миллионы раз (каждый раз, когда функция вызывается), чем malloc и освобождать его глобально? только раз? - person mathematrucker; 07.03.2019
comment
@mathematrucker: Что вообще означает проще на оборудовании? Два варианта, которые вы представили, не являются репрезентативными для действительно разумных вариантов, которые будут либо malloc/free каждый раз, либо наличие вызывающего объекта, предоставляющего уже выделенное рабочее пространство (не глобальное). - person R.. GitHub STOP HELPING ICE; 07.03.2019

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

person user957902    schedule 30.09.2011
comment
Если он снова и снова выделяет и освобождает блок фиксированного размера, он почти наверняка ничего не фрагментирует. (Это не означает, что накладных расходов нет... Но в данном случае это, вероятно, не фрагментация.) - person Nemo; 30.09.2011
comment
Предполагая, что ничто другое не выделяет память из кучи в программе, и она однопоточна, я согласен. Вероятно, каждый раз возвращается один и тот же блок памяти. Однако мы понятия не имеем, что еще в программе выделяется из кучи или как реализован этот конкретный менеджер кучи. - person user957902; 30.09.2011
comment
Имея в виду первоначальную цель экранных заставок, если мы говорим здесь о долго работающей программе, то может быть проще на оборудовании выделить malloc и освободить массив внутри функции, которая получает вызывается миллионы раз, чем malloc и освобождает его глобально только один раз. Честно говоря, я понятия не имею, является ли это обоснованным соображением или нет... Я знаю, что аппаратно-вредоносный код существует, но я подозреваю, что глобальные mallocs не попадают в эту категорию :) - person mathematrucker; 07.03.2019

Я обнаружил, что malloc, realloc и free довольно дороги. Если вы можете избежать использования malloc, лучше повторно использовать память, которая у вас уже есть.

Изменить:
Похоже, я ошибаюсь в том, насколько дорогой malloc. Некоторые временные тесты с библиотекой GNU C версии 2.14 в Linux показывают, что для теста, который повторяется 100 000 раз и выделяет и освобождает 512 слотов со случайными размерами от 1 до 163840 байт:

tsc average loop = 408
tsc of longest loop = 294350

Так что тратить 408 циклов на malloc или new в тесном внутреннем цикле было бы глупо. В остальном не беспокойтесь об этом.

person Zan Lynx    schedule 30.09.2011

Маллок, как правило, довольно недорогой. Это дорого, только если он генерирует системный вызов, чтобы получить больше места в куче. Например, в UNIX-подобных системах в конечном итоге будет сгенерирован вызов sbrk, который будет дорогостоящим. Если вы неоднократно выполняете malloc и освобождаете память одного и того же размера, это будет происходить безумно быстро. Например, рассмотрим следующую небольшую тестовую программу:

#include <stdlib.h>


int main()
{
  int i=0;
  int *ptr;

  for(int i=0; i<1e6; i++) {
    ptr = malloc(1024*sizeof(int));
    free(ptr);
  }
}

Он выделяет 1024 целых числа и освобождает их, и делает это миллион раз. Запустив это на моем довольно скромном маленьком хромбуке, переделанном в Linux, я получаю такие тайминги:

time ./test

real    0m0.125s
user    0m0.122s
sys     0m0.003s

Теперь, если я закомментирую часть цикла malloc и free, я получу следующие тайминги:

time ./test

real    0m0.009s
user    0m0.005s
sys 0m0.005s

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

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

Конечно, ваш пробег может варьироваться в зависимости от ОС, компилятора и реализации stdlib.

person Robert Lowe    schedule 24.05.2018

Вызов нескольких malloc/free может фактически увеличить объем памяти, используемой вашим процессом (без каких-либо утечек), если размер, передаваемый в malloc, является переменным, что подтверждается этим вопросом:

Использование памяти программы C - сообщается больше памяти, чем выделено

Таким образом, подход с одним буфером, вероятно, лучше.

person mihai    schedule 30.09.2011

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

person hugomg    schedule 30.09.2011

Измерьте производительность двух решений. Либо путем профилирования, либо измерения пропускной способности. Нельзя ничего сказать наверняка.

person onemasse    schedule 30.09.2011