我在 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
的函数,并向其传递一个指针,但我不直接控制它,所以不可能。相反,我正在考虑执行以下操作:
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 */
}
}
这意味着我从不直接释放我分配的内存:它在后续调用中被重用,然后徘徊在那里直到我的程序退出。这似乎不是正确的做法,但也不算太糟糕,因为分配的内存量总是很小。是我担心过度了吗?有更好的方法吗?
这种方法是合法的(但见下文),尽管像 valgrind 这样的工具会将其标记为“泄漏”。 (这不是功能上的泄漏,因为真正的内存泄漏是内存使用量的无限制增加。)您可能需要准确地对
malloc()
和free()
与函数正在执行的其他操作相比损失了多少时间进行基准测试。
如果您可以使用 C99 或 gcc,并且您的缓冲区不是太大,您还应该考虑可变长度数组,它与静态缓冲区一样快(或更快),并且不会产生碎片。如果您使用其他编译器,则可以查看非标准(但广泛支持)
alloca()
扩展。
您确实需要注意,使用静态缓冲区会使您的功能:
线程不安全 - 如果同时从多个线程调用它,它将破坏另一个实例的数据。如果从 numpy 调用 Python,这可能不是问题,因为线程将由 GIL 有效地序列化。
不可重入 - 如果
other_function
调用一些 Python 代码,最终调用 function
- 无论出于何种原因 - 在 function
完成之前,你的函数将再次销毁自己的数据。
如果不需要真正的并行执行和重入,那么使用
static
变量就可以了,很多 C 代码都是这样使用它们的。
这是一个很好的方法,许多库可能在内部使用类似的方法。程序退出时内存会自动释放。
您可能希望将
buf_len
舍入为某个块大小的倍数,这样您就不会每次 realloc()
稍微改变一点时 data_len
。但如果 data_len
几乎总是相同的大小,则没有必要。