Статический указатель на динамически выделенный буфер внутри функции
у меня есть функция в 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.
вам нужно знать, что использование статического буфера делает вашу функцию:
Thread-unsafe-если он вызывается из нескольких потоков одновременно, он уничтожит данные другого экземпляра. Если Python вызывается из numpy, это, вероятно, не проблема, так как потоки будут эффективно сериализованы GIL.
non-reentrant-if
other_function
вызывает некоторый код Python, который заканчивается вызовомfunction
- по какой-то причине-раньшеfunction
finishes, ваша функция снова уничтожит свои собственные данные.
если тебе не нужна настоящая параллельное выполнение и реентерабельности, это использование static
переменные в порядке, и много кода C использует их таким образом.
это прекрасный подход, и что-то вроде этого, вероятно, используется внутри многих библиотек. Память будет освобождена автоматически при выходе из программы.
возможно, вы захотите округлить buf_len
до кратного некоторого размера блока, поэтому вы не realloc()
каждый раз data_len
изменения небольшие. Но если ... --2--> почти всегда одинакового размера, это не обязательно.