【问题标题】:Static pointer to dynamically allocated buffer inside function指向函数内部动态分配缓冲区的静态指针
【发布时间】:2013-08-22 21:13:36
【问题描述】:

我在 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 */
    }
}

这意味着我永远不会直接释放我分配的内存:它会在后续调用中重用,然后一直停留在那里直到我的程序退出。这似乎不是正确的做法,但也不算太糟糕,因为分配的内存量总是很小。我是不是多虑了?有更好的方法吗?

【问题讨论】:

  • 您对动态缓冲区的大小有任何保证吗?
  • C 中没有 raii,也许您使用的编译器具有 raii 扩展,例如 gcc:gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
  • @JustinMeiners 是的,它受到非常严格的控制。最多有 65536 个项目,但这种情况很少见,通常是 16 或 32 个项目。
  • 我会使用&lt; 而不是!= ...否则您可能仍然需要进行许多分配。更好的是,如果可以,请使用 VLA(如果可以的话,请使用 alloca)。
  • 我使用的是 MSVC,我认为它是严格的 C89,所以我认为没有 VLA...

标签: c numpy malloc


【解决方案1】:

这种方法是合法的(但见下文),尽管像 valgrind 这样的工具会错误地将其标记为“泄漏”。 (这不是泄漏,因为泄漏是内存使用量的无限增加。)您可能想要准确地对mallocfree 与函数的其他内容相比损失了多少时间正在做。

如果您可以使用 C99 或 gcc,并且您的缓冲区不是太大,您还应该考虑可变长度数组,它与静态缓冲区一样快(或快于),并且不会产生碎片。如果您使用的是另一个编译器,则可以查看非标准(但 widelysupported)alloca 扩展。

您确实需要注意,使用静态缓冲区会使您的功能发挥作用:

  1. 线程不安全 - 如果同时从多个线程调用它,它将破坏另一个实例的数据。如果 Python 是从 numpy 调用的,这可能不是问题,因为 GIL 将有效地序列化线程。

  2. 不可重入 - 如果 other_function 调用了一些 Python 代码,最终调用了 function - 无论出于何种原因 - 在 function 完成之前,您的函数将再次销毁它自己的数据。

如果您不需要真正的并行执行和重入,static 变量的这种使用就可以了,很多 C 代码都是这样使用的。

【讨论】:

  • 关于线程安全的好点...如果这可以并行化,那将是multiprocessing。然后每个进程都会获得自己的静态变量副本吗?
  • @Jaime 是的 - 单独的进程不共享全局变量。
【解决方案2】:

这是一种很好的方法,许多库内部都可能使用类似的方法。程序退出时会自动释放内存。

您可能希望将buf_len 舍入到某个块大小的倍数,这样您就不会每次realloc() 更改一点点realloc()。但如果data_len 的大小几乎总是相同,则没有必要这样做。

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 2020-08-04
    • 2012-02-26
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 2022-01-24
    相关资源
    最近更新 更多