【问题标题】:Allocating or passing a buffer?分配或传递缓冲区?
【发布时间】:2012-04-25 22:55:34
【问题描述】:

假设 buffer 是一个由多个成员组成的结构,包括一个数组。

使用通常的优化标志进行编译,运行这两个代码 sn-ps 应该会有什么样的性能差异?

buffer buf;
for (int i = 0; i < BIG_ENOUGH_NUMBER; i++) {
   init(huge_file, i, &buf);
}

// buf is not used afterward

...

void init(FILE* f, int i, buffer* b) {
   ... // b is filled using f, according to i
   do_something(b);
}

for (int i = 0; i < BIG_ENOUGH_NUMBER; i++) {
   init(huge_file, i);
}

...

void init(FILE* f, int i) {
   buffer buf;
   ... // buf is filled from scratch using f, according to i
   do_something(&buf);
}

【问题讨论】:

  • 每次迭代保留或重建buf 中的多少数据/状态?
  • 第二个例子没有声明b?那应该是buf吗?
  • 在堆栈上放置一个大缓冲区当然会导致堆栈溢出。
  • 您可以假设创建缓冲区不会破坏堆栈。

标签: c performance struct compiler-optimization


【解决方案1】:

第一个答案是:对它们进行基准测试。

第二个答案是:将它们编译为汇编语言,并查看源代码。在有或没有优化标志的情况下这样做可能是有益的。

正如 abelenky 所指出的,在这两种情况下,buf 都在堆栈中。我的一般猜测是第二种情况会稍微快一些(使用我使用的编译器),因为 buf 不必作为参数传递。它确实必须在堆栈上分配,但堆栈分配通常只是函数调用帧的大小略有不同。无论该帧有多大,都必须完成相同数量的工作(调整堆栈指针)。

所以我希望生成代码的主要区别是在第二种情况下少一条“PUSH”指令,假设有足够的参数必须进入堆栈。 (如果它们都在寄存器中,那就有点不同了。)

它可能会受到优化的影响,例如buf 是否在每种情况下都出现在寄存器中。但是缺少填充 buf 的代码可能会影响这一点,所以我不会推测。

请注意,以上是我基于编译器行为的猜测。理论上,只要生成的程序正确运行,编译器就可以按照他们喜欢的方式翻译代码,这使得很难概括他们可能会或可能不会进行哪些优化。

【讨论】:

    【解决方案2】:

    @TJD 在他的评论中是正确的。

    在这两种情况下,buf 都在堆栈中,如果它很大,则有可能导致问题。 您通常应该使用 malloc 从堆中分配大型项目。

    也就是说,在您的第二个示例中,buf 完全在 init 函数内,并在该函数返回给调用者的那一刻消失(在您的示例中调用者未命名)。

    init 实际上是无用的,因为所有完成的工作都丢失了,并且在调用函数中不可见。

    在第一个示例中,bufinit 返回后仍然有效。

    【讨论】:

    • 在这两种情况下,do_something 都使用初始化的 buf 调用。
    猜你喜欢
    • 2017-11-10
    • 2013-09-26
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 2012-03-06
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    相关资源
    最近更新 更多