Статический указатель на динамически выделенный буфер внутри функции

у меня есть функция в C, которая динамически выделяет буфер, который передается другой функции для хранения ее возвращаемого значения. Что-то вроде следующего фиктивного примера:

void other_function(float in, float *out, int out_len) {
    /* Fills 'out' with 'out_len' values calculated from 'in' */
}

void function(float *data, int data_len, float *out) {
    float *buf;
    int buf_len = 2 * data_len, i;
    buf = malloc(sizeof(float) * buf_len);

    for (i = 0; i < data_len; i++, data++, out++) {
        other_function(*data, buf, buf_len);
        /* Do some other stuff with the contents of buf and write to *out */
    }
    free buf;
}

function вызывается итератором по многомерному массиву (это ядро NumPy gufunc, если быть точным), поэтому оно вызывается миллионы раз с тем же значением для data_len. Кажется расточительным создавать и разрушать буфер снова и снова. Я бы нормально переместил распределение буфера к функции, которая вызывает function, и передайте ему poiinter, но я не контролирую это напрямую, поэтому это невозможно. Вместо этого я рассматриваю возможность сделать следующее:

void function(float *data, int data_len, float *out) {
    static float *buf = NULL;
    static int buf_len = 0;
    int i;
    if (buf_len != 2 * data_len) {
        buf_len = 2 * data_len;
        buf = realloc(buf, sizeof(float) * buf_len); /* same as malloc if buf == NULL */
    }
    for (i = 0; i < data_len; i++, data++, out++) {
        other_function(*data, buf, buf_len);
        /* Do some other stuff with the contents of buf and write to *out */
    }
}

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

2 ответов


этот подход является законным (но см. ниже), хотя такие инструменты, как valgrind, неправильно помечают его как "утечку". (Это не утечка, так как утечка-это неограниченная увеличение использования памяти.) Вы можете точно определить, сколько времени потеряно на malloc и free по сравнению с другими вещами функция делает.

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

вам нужно знать, что использование статического буфера делает вашу функцию:

  1. Thread-unsafe-если он вызывается из нескольких потоков одновременно, он уничтожит данные другого экземпляра. Если Python вызывается из numpy, это, вероятно, не проблема, так как потоки будут эффективно сериализованы GIL.

  2. non-reentrant-if other_function вызывает некоторый код Python, который заканчивается вызовом function - по какой-то причине-раньше function finishes, ваша функция снова уничтожит свои собственные данные.

если тебе не нужна настоящая параллельное выполнение и реентерабельности, это использование static переменные в порядке, и много кода C использует их таким образом.


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

возможно, вы захотите округлить buf_len до кратного некоторого размера блока, поэтому вы не realloc() каждый раз data_len изменения небольшие. Но если ... --2--> почти всегда одинакового размера, это не обязательно.