寄存器传参和栈传参相比似乎没什么太大优势
有个问题有点困惑我,一般的说法是寄存器传参比栈传参效率高一点,但是我感觉寄存器传参和栈传参相比似乎没什么太大优势。
比如递归函数factorial(n) = n!
你用寄存器传参数n给factorial, factorial(n)在调用factorial(n-1),
还是会将n压栈,还不如传参的时候直接压栈呢?
一般来说程序里一个函数function的实现代码里总会调用其他函数,特别是标准库函数,
那个通过寄存器传给function的参数很可能会被压入栈内,不如调用function之前直接将该参数
压入栈内。 我觉得你这样想是不对的
其一:
之所以使用寄存器传递参数,是因为对于CPU来说,寄存器的速度肯定要比内存块,对于可能经常访问的变量,肯定是寄存器中直接计算比较好。
func(int a)
{
for(a=0;a<1000;a++)
{
//执行某些操作
}
}
那么肯定每次访问a的时候,你都去内存中访问a的速度不如直接去寄存器中访问的速度要快。
其二:寄存器传递参数的使用范围是少量的参数,你的意思是递归问题无法进行寄存器传参数,这块编译器怎么实现的我不知道
但是,可以想象一下,如果每个通过递归传的参数,都压栈,之后再出栈是否可以。这样如果函数中间大量使用到该参数的话是否可以进行优化呢? 你举得例子完全说错了。factorial(n)调用factorial(n-1)时需要把参数取到寄存器中,做-1操作,重新入栈。
如果仍然把n入栈,岂不是仍然调用的factorial(n)?
后面的例子也一样,只要需要对参数做运算,就要把参数从栈中拿出刀寄存器中。就算是直接向下传递,也是
先取出来再传递。 neodreamerus 发表于 2015-01-05 15:47 static/image/common/back.gif
有个问题有点困惑我,一般的说法是寄存器传参比栈传参效率高一点,
但是我感觉寄存器传参和栈传参相比似乎 ...
这是标准,人家制定了ABI,你就必须遵守。 本帖最后由 雷男 于 2015-01-06 15:47 编辑
麻烦问下如何使用寄存器进行传参?宏定义设置?汇编?跟操作系统相关?__attribute__((regparm(n)))? 雷男 发表于 2015-01-06 15:31 static/image/common/back.gif
麻烦问下如何使用寄存器进行传参?宏定义设置?汇编?跟操作系统相关?__attribute__((regparm(n)))?
编译器弄的,跟你没关系。除非你想在不同的语言间接口,或者写汇编小函数。 回复 3# zhaohongjian000
兄弟,你一直在说栈传参。
你没见过寄存器传参的吗?x86-64的C函数传参前4个是通过寄存器传的。
下面是个例子
factorial:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
cmpl $1, -4(%rbp)
jne .L2
movl $1, %eax
jmp .L3
.L2:
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, %edi
call factorial
imull -4(%rbp), %eax
.L3:
leave
ret
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $4, %edi
call factorial
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
ret
本帖最后由 neodreamerus 于 2015-01-08 22:20 编辑
回复 2# zsszss0000
你说的第一点我也理解。但是我问的问题的意思是:类似你举得例子这种简单情况太少了,大部分函数会调用其他函数。用寄存器传参会有一些好处,但是感觉很多时候这种优势并不明显。
第二点嘛,自己写个简单的程序编译一下就知道了。x86-64下函数的前4个参数都是寄存器传参的。我写的程序里,在call指令之前参数放到edi,
然后call。然后在factorial函数内部由于edi要被用于另外的函数calling,所以edi的值又被压入栈中。
#include <stdio.h>
int factorial(int n) {
if ( n == 1 ) return 1;
return n*factorial(n-1);
}
int main(int argc, char* argv[]) {
printf("%d\n", factorial(4));
}
编译成
.file "a.c"
.text
.globl factorial
.type factorial, @function
factorial:
.LFB0:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
cmpl $1, -4(%rbp)
jne .L2
movl $1, %eax
jmp .L3
.L2:
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, %edi
call factorial
imull -4(%rbp), %eax
.L3:
leave
ret
.LFE0:
.size factorial, .-factorial
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $4, %edi
call factorial
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
ret
回复 7# neodreamerus
没听懂我在说什么?都会自己看汇编了就自己分析吧。x86-32是通过栈传递的,对比一下吧。其他几个我见过的,ARM32、ARM64,MIPS都是通过寄存器传递参数的。 现在很少有需要直接写汇编的时候,这种就让编译器去优化好了