免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: neodreamerus
打印 上一主题 下一主题

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

求职 : 机器学习
论坛徽章:
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
11 [报告]
发表于 2015-01-09 13:08 |只看该作者
嗯,看来的确是,这种递归调用的过程可能也会使用到寄存器传递参数,而且每个递归调用的内部会将寄存器压栈操作,保护。如果是这样操作,那么就是速度会快一些。回复 8# neodreamerus


   

论坛徽章:
0
12 [报告]
发表于 2015-02-11 14:38 |只看该作者
做(DSP)编译器的,看到楼主的疑问特地来回复一下,可能说的是DSP架构的情况,但是应该区别不会太大。

通常来说,寄存器传参只要一条mov指令,而压栈在父函数里面要push,在子函数要pop,光是从指令数量上来说,寄存器传参就要有点优势(可能不大);
其次,从硬件角度考虑,内存传输通道的延时比寄存器大,而大多数运算指令都是以寄存器为基础的,压栈的数据最终都要读到寄存器上进行操作。从汇编上看到的情况可能是mov和pop的区别,但是实际执行过程中,可能mov需要1个机器周期,pop需要5个机器周期,做pop的时间足够做5条其他指令的运算了。

至于factorial(n) --> factorial(n-1)的例子,在编译器启动优化时,会做 reg = reg - 1 这样的操作,然后把new reg作为传参寄存器的。如果不停的出现计算后压栈的操作,可能是因为没启动编译器的优化功能(GCC的话,-O2可能才有效果,默认O0下对所有变量都会压栈)。
  

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
13 [报告]
发表于 2015-02-12 20:53 |只看该作者
Crofevil 发表于 2015-02-11 14:38
做(DSP)编译器的,看到楼主的疑问特地来回复一下,可能说的是DSP架构的情况,但是应该区别不会太大。

...


PC上的问题比DSP严重多了,以x64为例,
假设普通64位加法指令addq消耗一个时钟周期
那么,带进位64位加法adcq最少2时钟
64位乘法mulq要4时钟
MOVQ 寄存器 -> 内存,L1命中大约20时钟
MOVQ 寄存器 -> 内存,L1未命中100时钟起步


论坛徽章:
6
2015年辞旧岁徽章
日期:2015-03-05 16:13:092015年迎新春徽章
日期:2015-03-05 16:13:092015小元宵徽章
日期:2015-03-06 15:58:1815-16赛季CBA联赛之浙江
日期:2016-11-05 14:38:4115-16赛季CBA联赛之新疆
日期:2016-11-11 18:38:06
14 [报告]
发表于 2015-02-12 21:02 |只看该作者
对寄存器很多的处理器,寄存器传参很有优势。

对X86这样寄存器少得可怜的,还是用栈吧!

X86_64则增加了不少寄存器,所以,也。。。

论坛徽章:
0
15 [报告]
发表于 2015-07-11 12:24 |只看该作者
本帖最后由 myzhzygh 于 2015-07-11 12:28 编辑

实在看不下去了,寄存器就是CPU和主存之前的一个数据缓存,栈是在函数调用过程中帮助保存中间数据的一段内存。
在上面提到的这种递归调用中,因为函数中没有太多访问参数的代码,只是简单的进行-1操作作为下一次调用的参数,
这个过程中寄存器传递参数只是在调用新的函数之前,不需要把callee的参数压到栈上,你看到的压栈,是当前栈帧函
数自己保存自己的参数,因为保存当前调用参数的寄存器要被用于保存callee的参数,所以自己不能再占着寄存器,
所以作为中间数据保存在当前栈帧上。对于你提到的例子,其实就是少了一次压栈操作。也就少占用一个内存单元。
编译器并不是想象的那么智能,要遵循规则,所以其实这些递归调用的参数都是同一个寄存器在装载。

论坛徽章:
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
16 [报告]
发表于 2015-07-11 15:48 |只看该作者
[quote]myzhzygh 发表于 2015-07-11 12:24
实在看不下去了,寄存器就是CPU和主存之前的一个数据缓存,栈是在函数调用过程中帮助保存中间数据的一段内存 ...[/quote]
最苦恼的是系统函数:
mkcontext(),源码是32位的,处理的栈帧。64位不知道怎么办了,不知道弄哪个寄存器。

论坛徽章:
0
17 [报告]
发表于 2017-02-02 14:38 |只看该作者
楼主的问题是既然大部分函数还调用了其它函数,那么穿参的寄存器仍然需要入栈来保存其内容,寄存器传参相比栈穿参没什么优势。
实际上在编译器完全不开优化的情况下确实会把传参寄存器入栈,再从栈上取值来完成对参数的计算/处理,这么看貌似跟直接用栈传参没什么区别;但是开了优化时会避免把只使用1次的参数入栈/出栈(因为丢了没影响,gdb看此类“一次性”参数只能看到<optimized out>),这样能提升性能。

论坛徽章:
0
18 [报告]
发表于 2017-02-02 14:47 |只看该作者
farseeraliens 发表于 2017-02-02 14:38
楼主的问题是既然大部分函数还调用了其它函数,那么穿参的寄存器仍然需要入栈来保存其内容,寄存器传参相比 ...

除了有的参数只用到一次因而不需要保存也不需要恢复的情况,另外比如c++程序函数成员互相调用,由于this指针是用rdi传递的,只有最外面一帧把对象地址赋给rdi,其它地方不需要修改rdi,这就节约了若干次入栈、出栈。

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP