C: 函数中的值 return 是否比 void 需要更多 CPU 周期?
C: Does value return from the function take more CPU cycles than void?
如果在 C 中使用 void 函数而不是 return 任意类型(例如 int)的函数,int
函数是否会比 [=14] 需要更多 CPU 个周期=]函数?
示例:
int a, b = 1, c = 2;
void f()
{
a = b + c;
}
int g()
{
a = b + c;
return a;
}
我的常识告诉我 return 是一个动作,所以它应该需要一些 CPU 的时间,但我没有这里需要的适当的深厚基础知识,我也不知道汇编程序可以自信地回答这个问题。谷歌搜索也没有成功。
编辑:
我的兴趣纯粹是学术性的,我不希望通过使用 void 函数与 int 函数获得任何显着(甚至接近)的性能。
我认为这个问题没有多大意义。
如果您需要 returned 值,您无法选择使用 void
来加速它 - 即使它 会更快。
如果你不需要结果,return它也没用,所以就不要这样做。
无论哪种方式,选择都取决于调用者的需求。
通常,现代编译器不会 return 值,而是就地构造它。例如,如果您编写 int sum = f(a,b);
,编译器将永远不会在您的函数中生成临时变量,而是使用 sum
的内存来存储结果。这意味着执行时间没有差异。
这完全取决于 CPU 指令集和调用约定。
如果,例如,return值总是returned在一个特定的寄存器中,编译器可以将计算b+c的结果安排在那个特定的寄存器中插入 return 指令,为这两个函数生成的代码可能相同。
但是,这不是您要考虑在程序中进行优化的事情,除非您用尽了所有其他性能改进选项。而你肯定没有。
在x86_64系统上,这两个函数可以编译成相同的代码。我已经 "glossed" 使用大致等效的 C 代码进行反汇编:
f_or_g:
pushq %rbp ; // Standard stack frame setup
movq %rsp, %rbp ; // same
movl OFFSET1(%rip), %eax ; eax = c;
addl OFFSET2(%rip), %eax ; eax += b;
movq OFFSET3(%rip), %rcx ; rcx = &a;
movl %eax, (%rcx) ; *rcx = eax;
popq %rbp ; // Standard stack frame teardown
retq ; return
由于 x86_64 使用 eax
作为 32 位值的 return 寄存器,加法的结果在 "right place" 到 return它已经——不需要额外的代码。
在更复杂的函数中,可能需要一些小的开销来确保 return 值最终出现在正确的寄存器中。不过,一般来说,这种开销应该很小。
同样的原则适用于大多数其他架构——这并不特定于 x86_64;我之所以使用它,是因为这是我手头上的第一个编译器。
我们不能说是否会有额外的周期使用,因为它完全取决于您的处理器或 cpu,此外,如果您需要 return 值,则 return 声明仅访问该元素的内存位置,因此复杂性可能只有很小的变化,可以忽略不计。
如果在 C 中使用 void 函数而不是 return 任意类型(例如 int)的函数,int
函数是否会比 [=14] 需要更多 CPU 个周期=]函数?
示例:
int a, b = 1, c = 2;
void f()
{
a = b + c;
}
int g()
{
a = b + c;
return a;
}
我的常识告诉我 return 是一个动作,所以它应该需要一些 CPU 的时间,但我没有这里需要的适当的深厚基础知识,我也不知道汇编程序可以自信地回答这个问题。谷歌搜索也没有成功。
编辑: 我的兴趣纯粹是学术性的,我不希望通过使用 void 函数与 int 函数获得任何显着(甚至接近)的性能。
我认为这个问题没有多大意义。
如果您需要 returned 值,您无法选择使用 void
来加速它 - 即使它 会更快。
如果你不需要结果,return它也没用,所以就不要这样做。
无论哪种方式,选择都取决于调用者的需求。
通常,现代编译器不会 return 值,而是就地构造它。例如,如果您编写 int sum = f(a,b);
,编译器将永远不会在您的函数中生成临时变量,而是使用 sum
的内存来存储结果。这意味着执行时间没有差异。
这完全取决于 CPU 指令集和调用约定。
如果,例如,return值总是returned在一个特定的寄存器中,编译器可以将计算b+c的结果安排在那个特定的寄存器中插入 return 指令,为这两个函数生成的代码可能相同。
但是,这不是您要考虑在程序中进行优化的事情,除非您用尽了所有其他性能改进选项。而你肯定没有。
在x86_64系统上,这两个函数可以编译成相同的代码。我已经 "glossed" 使用大致等效的 C 代码进行反汇编:
f_or_g:
pushq %rbp ; // Standard stack frame setup
movq %rsp, %rbp ; // same
movl OFFSET1(%rip), %eax ; eax = c;
addl OFFSET2(%rip), %eax ; eax += b;
movq OFFSET3(%rip), %rcx ; rcx = &a;
movl %eax, (%rcx) ; *rcx = eax;
popq %rbp ; // Standard stack frame teardown
retq ; return
由于 x86_64 使用 eax
作为 32 位值的 return 寄存器,加法的结果在 "right place" 到 return它已经——不需要额外的代码。
在更复杂的函数中,可能需要一些小的开销来确保 return 值最终出现在正确的寄存器中。不过,一般来说,这种开销应该很小。
同样的原则适用于大多数其他架构——这并不特定于 x86_64;我之所以使用它,是因为这是我手头上的第一个编译器。
我们不能说是否会有额外的周期使用,因为它完全取决于您的处理器或 cpu,此外,如果您需要 return 值,则 return 声明仅访问该元素的内存位置,因此复杂性可能只有很小的变化,可以忽略不计。