免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 11822 | 回复: 18
打印 上一主题 下一主题

[C] 寄存器传参和栈传参相比似乎没什么太大优势 [复制链接]

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015元宵节徽章
日期:2015-03-06 15:52:30
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-01-05 15:47 |只看该作者 |倒序浏览
有个问题有点困惑我,一般的说法是寄存器传参比栈传参效率高一点,
但是我感觉寄存器传参和栈传参相比似乎没什么太大优势。


比如递归函数factorial(n) = n!
你用寄存器传参数n给factorial, factorial(n)在调用factorial(n-1),
还是会将n压栈,还不如传参的时候直接压栈呢?


一般来说程序里一个函数function的实现代码里总会调用其他函数,特别是标准库函数,
那个通过寄存器传给function的参数很可能会被压入栈内,不如调用function之前直接将该参数
压入栈内。

求职 : 机器学习
论坛徽章:
79
2015年亚洲杯纪念徽章
日期:2015-05-06 19:18:572015七夕节徽章
日期:2015-08-21 11:06:172015亚冠之阿尔纳斯尔
日期:2015-09-07 09:30:232015亚冠之萨济拖拉机
日期:2015-10-21 08:26:3915-16赛季CBA联赛之浙江
日期:2015-12-30 09:59:1815-16赛季CBA联赛之浙江
日期:2016-01-10 12:35:21技术图书徽章
日期:2016-01-15 11:07:2015-16赛季CBA联赛之新疆
日期:2016-02-24 13:46:0215-16赛季CBA联赛之吉林
日期:2016-06-26 01:07:172015-2016NBA季后赛纪念章
日期:2016-06-28 17:44:45黑曼巴
日期:2016-06-28 17:44:4515-16赛季CBA联赛之浙江
日期:2017-07-18 13:41:54
2 [报告]
发表于 2015-01-05 18:08 |只看该作者
我觉得你这样想是不对的
其一:
之所以使用寄存器传递参数,是因为对于CPU来说,寄存器的速度肯定要比内存块,对于可能经常访问的变量,肯定是寄存器中直接计算比较好。
func(int a)
{
      for(a=0;a<1000;a++)
      {
              //执行某些操作
      }
}
那么肯定每次访问a的时候,你都去内存中访问a的速度不如直接去寄存器中访问的速度要快。

其二:寄存器传递参数的使用范围是少量的参数,你的意思是递归问题无法进行寄存器传参数,这块编译器怎么实现的我不知道
但是,可以想象一下,如果每个通过递归传的参数,都压栈,之后再出栈是否可以。这样如果函数中间大量使用到该参数的话是否可以进行优化呢?

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
3 [报告]
发表于 2015-01-06 09:01 |只看该作者
你举得例子完全说错了。factorial(n)调用factorial(n-1)时需要把参数取到寄存器中,做-1操作,重新入栈。
如果仍然把n入栈,岂不是仍然调用的factorial(n)?

后面的例子也一样,只要需要对参数做运算,就要把参数从栈中拿出刀寄存器中。就算是直接向下传递,也是
先取出来再传递。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
4 [报告]
发表于 2015-01-06 11:09 |只看该作者
neodreamerus 发表于 2015-01-05 15:47
有个问题有点困惑我,一般的说法是寄存器传参比栈传参效率高一点,
但是我感觉寄存器传参和栈传参相比似乎 ...

这是标准,人家制定了ABI,你就必须遵守。

论坛徽章:
0
5 [报告]
发表于 2015-01-06 15:31 |只看该作者
本帖最后由 雷男 于 2015-01-06 15:47 编辑

麻烦问下如何使用寄存器进行传参?宏定义设置?汇编?跟操作系统相关?__attribute__((regparm(n)))?

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
6 [报告]
发表于 2015-01-07 15:34 |只看该作者
雷男 发表于 2015-01-06 15:31
麻烦问下如何使用寄存器进行传参?宏定义设置?汇编?跟操作系统相关?__attribute__((regparm(n)))?

编译器弄的,跟你没关系。除非你想在不同的语言间接口,或者写汇编小函数。

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015元宵节徽章
日期:2015-03-06 15:52:30
7 [报告]
发表于 2015-01-08 21:10 |只看该作者
回复 3# zhaohongjian000


兄弟,你一直在说栈传参。
你没见过寄存器传参的吗?x86-64的C函数传参前4个是通过寄存器传的。
下面是个例子

  1. factorial:
  2.         pushq        %rbp
  3.         movq        %rsp, %rbp
  4.         subq        $16, %rsp
  5.         movl        %edi, -4(%rbp)
  6.         cmpl        $1, -4(%rbp)
  7.         jne        .L2
  8.         movl        $1, %eax
  9.         jmp        .L3
  10. .L2:
  11.         movl        -4(%rbp), %eax
  12.         subl        $1, %eax
  13.         movl        %eax, %edi
  14.         call        factorial
  15.         imull        -4(%rbp), %eax
  16. .L3:
  17.         leave
  18.         ret


  19. .LC0:
  20.         .string        "%d\n"
  21.         .text
  22.         .globl        main
  23.         .type        main, @function

  24. main:
  25.         pushq        %rbp
  26.         movq        %rsp, %rbp
  27.         subq        $16, %rsp
  28.         movl        %edi, -4(%rbp)
  29.         movq        %rsi, -16(%rbp)
  30.         movl        $4, %edi
  31.         call        factorial
  32.         movl        %eax, %esi
  33.         movl        $.LC0, %edi
  34.         movl        $0, %eax
  35.         call        printf
  36.         leave
  37.         ret
复制代码

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015元宵节徽章
日期:2015-03-06 15:52:30
8 [报告]
发表于 2015-01-08 21:47 |只看该作者
本帖最后由 neodreamerus 于 2015-01-08 22:20 编辑

回复 2# zsszss0000


你说的第一点我也理解。但是我问的问题的意思是:类似你举得例子这种简单情况太少了,大部分函数会调用其他函数。用寄存器传参会有一些好处,但是感觉很多时候这种优势并不明显。


第二点嘛,自己写个简单的程序编译一下就知道了。x86-64下函数的前4个参数都是寄存器传参的。我写的程序里,在call指令之前参数放到edi,
然后call。然后在factorial函数内部由于edi要被用于另外的函数calling,所以edi的值又被压入栈中。

  1. #include <stdio.h>



  2. int factorial(int n) {



  3.         if ( n == 1 ) return 1;



  4.         return n*factorial(n-1);

  5. }



  6. int main(int argc, char* argv[]) {



  7.         printf("%d\n", factorial(4));

  8. }


复制代码
编译成


  1.         .file        "a.c"
  2.         .text
  3.         .globl        factorial
  4.         .type        factorial, @function
  5. factorial:
  6. .LFB0:
  7.         pushq        %rbp
  8.         movq        %rsp, %rbp
  9.         subq        $16, %rsp
  10.         movl        %edi, -4(%rbp)
  11.         cmpl        $1, -4(%rbp)
  12.         jne        .L2
  13.         movl        $1, %eax
  14.         jmp        .L3
  15. .L2:
  16.         movl        -4(%rbp), %eax
  17.         subl        $1, %eax
  18.         movl        %eax, %edi
  19.         call        factorial
  20.         imull        -4(%rbp), %eax
  21. .L3:
  22.         leave
  23.         ret
  24. .LFE0:
  25.         .size        factorial, .-factorial
  26.         .section        .rodata
  27. .LC0:
  28.         .string        "%d\n"
  29.         .text
  30.         .globl        main
  31.         .type        main, @function


  32. main:
  33.         pushq        %rbp
  34.         movq        %rsp, %rbp
  35.         subq        $16, %rsp
  36.         movl        %edi, -4(%rbp)
  37.         movq        %rsi, -16(%rbp)
  38.         movl        $4, %edi
  39.         call        factorial
  40.         movl        %eax, %esi
  41.         movl        $.LC0, %edi
  42.         movl        $0, %eax
  43.         call        printf
  44.         leave
  45.         ret


复制代码

论坛徽章:
12
巳蛇
日期:2013-09-16 15:32:242015年辞旧岁徽章
日期:2015-03-03 16:54:152015年亚洲杯之约旦
日期:2015-02-11 14:38:37双鱼座
日期:2015-01-05 11:05:47戌狗
日期:2014-12-08 09:41:18戌狗
日期:2014-08-15 09:29:29双子座
日期:2014-08-05 09:17:17卯兔
日期:2014-06-08 15:32:18巳蛇
日期:2014-01-27 08:47:08白羊座
日期:2013-11-28 21:04:15巨蟹座
日期:2013-11-13 21:58:012015年亚洲杯之科威特
日期:2015-04-17 16:51:51
9 [报告]
发表于 2015-01-09 09:07 |只看该作者
回复 7# neodreamerus


    没听懂我在说什么?都会自己看汇编了就自己分析吧。x86-32是通过栈传递的,对比一下吧。其他几个我见过的,ARM32、ARM64,MIPS都是通过寄存器传递参数的。

论坛徽章:
324
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
10 [报告]
发表于 2015-01-09 09:11 |只看该作者
现在很少有需要直接写汇编的时候,这种就让编译器去优化好了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP