Какое распределение быстрее? malloc против локальной переменной

Какой предпочтительный способ выделить память для функции, которая часто выделяет и освобождает память? Предположим, что эта функция вызывается от 500 до 1000 раз в секунду на процессоре с частотой 1 ГГц.

(Пожалуйста, игнорируйте статические и глобальные переменные / распределение. Меня интересует только этот конкретный случай:)

void Test()
{
    ptr=malloc(512)   // 512 bytes
    ...
    free(ptr) 
}

ИЛИ ЖЕ

void Test()
{
     struct MyStruct localvar; // 512 byte sized structure
     ... 
}

8 ответов

Решение

Выделение локальных переменных в стеке происходит быстрее, чем при распределении в куче malloc, Тем не менее, общее пространство стека ограничено (например, до нескольких мегабайт). Поэтому вы должны ограничить себя "маленькими" данными в локальном стеке. (и 512 байт мало по сегодняшнему стандарту, но 256 Кбайт будет слишком большим для локального размещения стека).

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

Но зовет malloc несколько тысяч раз в секунду должно быть безболезненно (ИМХО типичный малогабаритный malloc занимает несколько десятков микросекунд).

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

Локальные переменные распределяются по существу "бесплатно", поэтому здесь нет конкурса, если мы заинтересованы только в производительности.

Тем не мение:

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

Включите дизассемблер при входе в свою функцию и выполните 2 случая.

Локальная переменная (основанная на стеке) потребует 0 дополнительных циклов - вы даже не увидите, где происходит распределение, потому что функция будет распределять все локальные переменные за 1 цикл, просто перемещая указатель стека и освобождая все локальные переменные в 1 цикле, восстанавливая указатель стека. Неважно, если у вас есть 1 или 1000 локальных переменных, "распределение" занимает столько же времени.

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

Мое эмпирическое правило: всегда используйте стек, если это возможно, вместо malloc/free или же new/delete, В дополнение к более высокой производительности вы получаете дополнительное преимущество: вам не нужно беспокоиться об утечке памяти или ресурсов. В С это просто означает забыть позвонить free(), но в C++ исключения могут испортить ваш день, если что-то выдает исключение перед вызовом delete, Если вы используете переменные стека, все это обрабатывается автоматически! Однако используйте стек только в том случае, если речь идет о "маленьких" фрагментах памяти (байтах и ​​КБ), а не о больших объектах (не МБ или ГБ!). В любом случае, если вы говорите об огромных объектах, вы больше не говорите о производительности и, вероятно, не будете звонить free/delete в любом случае вызов той же функции.

  • Какой предпочтительный способ выделить память....
  • Какое распределение быстрее?

Хотите более быстрый или предпочтительный путь?

Во всяком случае, в случае, если вы упомянули, я думаю, что второй вариант:

struct MyStruct localvar;

является более эффективным, так как выделение памяти выполняется стеком. Что намного эффективнее, чем использование функций динамического выделения памяти, таких как malloc,

Оптимизация

Кроме того, если вы делаете это для повышения производительности и оптимизации...

На моем ПК, используя malloc выделение строк вместо объявления массива char из стека дает мне задержку ~ 73 наносекунд на строку.

если вы скопировали 50 строк в вашу программу: 4757142 / 50 = 95142 (и немного) запусков вашей программы

Если я запускаю вашу программу 50 раз в день: 95142 / 50 = 1902 (и немного) дней

1902 дня = 5 1/5 лет

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

Распределение стека происходит быстрее, чем malloc+free,

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

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

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

      /*  This question is still valid in 2023.  Like another poster, I 
discovered allocation, verifying allocation and freeing memory 
took very little processing time. */

/*  The SURPRISE was that memory ACCESS time was VASTLY 
different -- stack vs the heap! */

/*  Test Results for accessing a 500,000-byte block of memory:
    use_malloc()    elapsed_time_ms(3062)   iterations(10000)
    use_stack()     elapsed_time_ms(0)      iterations(10000)
*/

/*  - Using stack-based memory took virtually no time, but heap 
memory used 3 seconds. */

/*  I have a theory that maybe all that far-pointer arithmetic 
for accessing the heap is to blame -- the stack is nearer and 
maybe accessed with smaller (32bit?) integers?  Just a guess.*/

/*  MS says the default reserved stack space is 1MB today, if I 
understood.  (Encourage you to verify this on your own and for 
Your OS) */

/*  Hence, for my money, if I have to do some iteration-
intensive work on blocks of relatively small memory, for sure 
I'll be using the stack. */

/* Your mileage may vary; I respect your choice.  */
/* Code below... cheers! */


/*Here is the processing performed on the memory -- where 
len = 500000: */

void fill_buff(char *buff, int len)
{
    int iii;

    for(iii = 0; iii < len; iii++)
        buff[iii] = (char)(iii + 'A');
}

#define SIZE 500000

/*The two buffer-usage functions */

void use_stack(void)
{
    char buff[SIZE+1];

    fill_buff(buff, SIZE);
}

char *buff = NULL;

void use_malloc(void )
{
    fill_buff(buff, SIZE);
}

/* Test like so .... */
void run_test(long iterations)
{
    long iii;
    clock_t startt, endt, elapsed_time_ms;

    startt = clock();
    for(iii=0; iii < iterations; iii++)
    {
        use_malloc();
    }
    endt = clock();
    elapsed_time_ms = endt-startt;
    printf("use_malloc()  elapsed_time_ms(%d) iterations(%d)\n",
    elapsed_time_ms, iterations);
    
    //...
    //...
    //...
    use_stack();
    //...
    //...

}
main()
{
    buff = malloc(SIZE+1);

    if(buff)
        run_test(10*1000);
    //...
}
Другие вопросы по тегам