能力值:
( LV9,RANK:170 )
2 楼
函数内都是以 ebp 作参考的呀。
本来,ebp 就是 stack frame base pointer
能力值:
(RANK:260 )
3 楼
INTEL推荐使用EBP作为栈帧的访问基址寄存器。
实际中大多数编译器生成的标准代码也是这么做的。
但是对于某些情况,比如一个数学算法,如果开了编译器的高级优化,它会把EBP也分配给局部变量或临时变量,而只使用ESP访问栈上的局部变量和函数参数。
在特定情况下,仅仅多一个可分配寄存器却可能意味着性能成倍地提高。
能力值:
( LV9,RANK:170 )
4 楼
>> 在特定情况下,仅仅多一个可分配寄存器却可能意味着性能成倍地提高。
这句话说得太夸张了吧。
x64 处理器多了8 个 GPRs 之多,性能是否提升了一倍,都很难说
能力值:
(RANK:260 )
5 楼
编译器的寄存器分配策略,对编译代码的质量有着至关重要的影响。
有时候恰好由于少一个寄存器,编译器不得不使用栈帧上的内存,对于某些数学算法,局部变量和临时结果的访问非常频繁。有时仅仅因为一个寄存器不够,代码的效率会成倍地打折。
要知道内存的访问速度和寄存器的访问速度相差至少一个数量级!哪怕多了几条访存指令,程序的效率降低就非常明显。有些直接使用内存操作数的指令,比如add [esp+0x10],eax,实际上要两次总线操作的周期才能完成。类似的指令还有比如inc [esp+8]等等。
作为个人桌面应用,这点性能的差别是完全可以不计的。但对用作科学计算的计算机程序,好的编译器比差的编译器就不止是成倍地提高效率了。
我曾经读过一本书,讲图像处理的算法,作者自己编写一个滤镜算法的程序,执行时间是200ms,经过不断地优化,最后提高到46ms,最后使用intel的编译工具,使用intel的各种多媒体指令集,结果该算法最终的平均执行时间仅为6ms!
一个寄存器看似事小,实则影响非同小可。如果遇到了类似于访存的“颠簸”现象,那么这一个寄存器的代价可能是几个数量级的性能损失了!!!
包括Linus内核在内的GNU世界的程序员,全部倾向于使用AT&T汇编,而不愿意使用INTEL汇编语法。
原因就在于AT&T汇编原本不是为了程序员用来写程序而发明的,AT&T汇编格式本身就是编译器前端生成的、供后端汇编器使用的“中间代码”,像Linux内核那样,在C中嵌入汇编,甚至可以对编译器的寄存器分配策略进行控制,而如果嵌入INTEL格式,则是完全不同的概念了。
虽然我还没有能力使用AT&T汇编格式来写程序,不过目前读懂不太晦涩的代码还是没问题的。见识过了AT&T汇编,才知道GCC有多么强大。。。
能力值:
( LV2,RANK:10 )
6 楼
有寄存器重命名+乱序执行。
可用的寄存器就不像想像中的少了。
再加上其他的技术,寄存器不足的缺点就不容易体现出来了。
特定情况当然很难说的
能力值:
(RANK:300 )
7 楼
说实话从看at&t 和intel的汇编代码的时候没有发现谁更有优势,汇编的别名是组合。汇编代码下面实际就是机器码。这是区别于其他程序语言的原因。谁更有优势可能是其编译器的同,其优化组合的程度不同。没有看过GCC源码。天生对大程序的代码有种恐惧.有时间把GCC的汇编编译部分的源代码拿出来看看。可怜m$却没有给机会我们看masm的源码......
如果有人有兴趣倒是可以拿masm来练练内功。
能力值:
(RANK:260 )
8 楼
狐狸你是没看过AT&T的汇编。看过之后就知道它是什么了。
如果是纯汇编的还好看,如果是C中内嵌的AT&T,就不同了,因为它不与机器码完全对应!!!
这是用AT&T写的纯汇编的程序,会看INTEL汇编的,只要记住它的源与目的操作数相反就能看懂。
_pg_dir:
17startup_32:
18 cld
19 movl $0x10,%eax
20 mov %ax,%ds
21 mov %ax,%es
22 mov %ax,%fs
23 mov %ax,%gs
24 lss _stack_start,%esp
25 call setup_idt
26 call setup_gdt
27 movl $0x10,%eax # reload all the segment registers
28 mov %ax,%ds # after changing gdt. CS was already
。。。 但你要是看下面的C中内嵌的汇编,看起来只有一条call指令,而实际上4个参数需要组合,所以编译器生成的绝不是一条指令!
#define __get_user_x(size, ret, x, ptr) \
asm volatile("call __get_user_" #size \
: "=a" (ret),"=d" (x) \
: "0" (ptr)) \
我现在拜读Linux内核就因为这些AT&T汇编太难理解而几乎读不下去,郁闷。。。
能力值:
(RANK:260 )
9 楼
寄存器重命名和寄存器窗口等技术,很多高端处理器都是有的。
但INTEL桌面计算机处理器是没戏了。
但面向高端的INTEL的ITANIUM处理器,确实让人很叹服它的设计,打破INTEL给人的复杂臃肿低效的印象,它具有足够多的寄存器,并且支持寄存器重命名(旋转寄存器),而且最妙的是,它具有一个“寄存器栈”,在需要的栈空间不多时,可以完全脱离内存栈,其性能提升可想而知(当然寄存器栈的设计还有很多其它的用处)。
我特别注意到它的指令,是叫做“指令束”的,具有三条并行的指令流!
试想如果X86构架没有“向前兼容”的包袱,我想当今桌面计算机的性能也绝对不是现在这个水平。。。
能力值:
( LV2,RANK:10 )
10 楼
#define __get_user_x(size, ret, x, ptr) \
asm volatile("call __get_user_" #size \ //宏展开,
//__get_user_x,x=1,2,4
: "=a" (ret),"=d" (x) \ //输出约束,ret由eax输出
//x由edx输出
: "0" (ptr)) //输入约束,ptr约束和ret约束一样
x86应该也有寄存器重命名啊,龙芯都有了
能力值:
( LV2,RANK:10 )
11 楼
书呆彭的回答很专业,不错,谢谢!
能力值:
(RANK:300 )
12 楼
呵呵,我已经看不懂了....
能力值:
( LV9,RANK:290 )
13 楼
看了讨论,获益良多
能力值:
( LV9,RANK:170 )
14 楼
你回答得很不专业。若想说明你的观点,应该摆数据说事实。
1、拿一个程序在32位平台和64位平台的表现来说明。在 x64 平台下,要知道,x64 的 ABI 规定,有 7 个通用寄存器可以用了作为函数的参数传递,比之 x86 一般使用栈来传递参数要优越多了吧。况且多出了几个寄存器可用。那么是否,一个程序采用64位编程真的比32位要快上一倍或以上吗? 反正,我好像是没看到64位比32位快上一个数量级的结论。
2、你说的关于图象方面的例子,不知道你是看本书籍,我曾经看过一本很经典的图像编程的书籍,忘了叫什么名,很厚有1000多页,里面有很好的例子,有专门讲优化的部分。是 id 公司的人写的。 但是所说的性能大幅提升,主要是算法方面的改进,然而才到汇编级的改进。汇编级的改进,性能提升幅度并不是很大。 至于说到使用SSEx 系列指令,令当别论,且那不是通用编程。
3、你拿 unix / linux 世界的汇编说事,那更是说明你不了解。unix/linux 世界采用 gnu 语法,那是由于 AT&T 与 unix 与生俱来的惯例。并不是因为采用 gnu 语法会比 intel 语法性能上要优越。
反而是恰恰相反:gnu 语法是照顾了通用性而牺性性能,linux 能运行在多种平台上,汇编语言这种中间代码就是要为了能运行在多种平台上而产生。
采用 gnu 的语法不可能会比 intel 语法要快,难道 intel 编译器产生的代码会比 gcc 产生的代码要慢??
事实上,gcc 的嵌入语法,是给了程序员自由度挺少,由编译器自己调度分配策略。这是一种通用性方面的考虑,并不是性能方面的考虑。
能力值:
( LV9,RANK:170 )
15 楼
gcc 强大是因为它的通用性,不是它的性能
能力值:
( LV9,RANK:170 )
16 楼
寄存器栈是一个被人唾弃的结构,x86 的 浮点运算部分就是采用寄存器栈的。那是高端机器不会采用的
能力值:
(RANK:300 )
17 楼
越辨越明。
对于技术的的辩论或互相纠正其表达上的错误。
只要不要过于激进都是值得称道的...
mik期待你的大作........嘿嘿.
能力值:
( LV2,RANK:10 )
18 楼
能力值:
(RANK:260 )
19 楼
此寄存器栈非彼寄存器栈,不了解请不要乱说。
况且,80x87的栈式寄存器结构并不错,错在INTEL想在80x86的基础上不断地增加新功能,新指令集,导致它的指令过于复杂。
80x37的寄存器组不仅可以以栈式访问,也可以访问指定的寄存器,LOAD/STORE结构是非常经典的处理器构建,但是它构建于80x86之上,结果使它成为了失败的设计。
安腾处理器的设计在高端处理器中都可以说非常优雅了。它超越了一般高端机的寄存器重命名或寄存器分组的概念,在此基础上实现了一个寄存器的栈,对于系统软件来说,它可以不使用存储器栈而完成系统服务,仅在任务切换时,需要将寄存器栈与存储器栈进行同步。这种设计是非常好的设计,而不是失败的设计。
像MIPS之类的机器,由于它的寄存器不够多,只有32个,因此无法支持一个实用的栈,但对于安腾处理器,有多达127(128)个能用寄存器(外加128个浮点寄存器),它可以支持的寄存器栈的最大深度是96,足够一般的嵌套过程调用使用(栈主要用来保存层次调用路径,参数是通过能用寄存器传递的)
能力值:
( LV9,RANK:170 )
20 楼
安腾处理器,我确实不了解,这方面我保持沉默
能力值:
(RANK:260 )
21 楼
关于GNU,我了解不多,所以说得不对,多谢指教。
对于AT&T汇编,我只是能读懂而已,没有更多的了解,我只知道在GCC前端生成的汇编代码就是这个样子的。我知道我不懂,说得不对,所以不怕别人指出来。 最开始我说的一个寄存器可能造成性能相差很多,本来指得是很特殊的情况,并不说一般情况。
我只是为了说明寄存器对程序效率影响很大,可能说得大了,以为我在钻牛角。
本来我想找例子来说明一下我的意思,免得大家误会,找了半天,没找到。
我记得以前上课时,老师讲过一个“颠簸”,或“摇摆”的例子,不幸的是上课时的PPT找不到了,我实在想不起这个例子的具体情况了,所以在谷歌上找了一个类似的例子(唐志敏的一篇报告中找到的):
for ( k=0;k<10;k++)
{
for (j=0;j<1000;j++)
执行运算过程B;
}
运算过程B的大小:如果B过大,CPU内部寄存器的压力就会很大,如果寄存器的数量不足以保存B中出现的所有数据,可能会出现颠簸的现象,刚刚从寄存器中换出的数据也许就是下一个需要的数据,还得重新读入寄存器,这对效率显然是有影响的。
所以CPU用一层”透明“的CACHE,而以此来大幅提高性能,虽然CACHE也可能发生颠簸,但CACHE总归比寄存器的容量大得多,颠簸的机率也低得多。 另外,对于程序执行的效率,其实对于面向个人用户的软件开发者,基本上可以不予考虑,对个人而言,一段程序执行1ms和100ms的差别并不多,我点一下鼠标后能立即看到结果,就行了。
真正对效率斤斤计较的场合是高端应用。我们实验室6台Spark的工作站,全天24小时CPU几乎总是100,要测试一个东西,写好测试脚本,提交后至少也得十多分钟。很多师兄都是晚上下班时提交,第二天上班时看看好没好。 谢谢mik的关注。你指出的错误我会去好好查一查的。
能力值:
( LV9,RANK:170 )
22 楼
前面提到,性能大幅度提升,主要是要靠算法及数据结构的改进。单单靠多一个寄存器提升的性能是有限的。当然象安腾这样多出寄存器当一个数量级以上,另当别论
既使将vc2008最高级别的优化打开,也不会考虑从ebp下手
你提到的颠玻情况,应该好好考虑数据结构方面,充分利用cpu 内部的cache
能力值:
(RANK:260 )
23 楼
我确实碰到过一个数学库中的函数,使用除ESP外所有寄存器进行运算的。而且这个库应该是VC6编译的。
我刚才自己特意“编”了一段测试代码,发现它确实把EBP用作寄存器变量了。请看下面的程序。
27:
28: int main(void)
29: {
00401170 push ebx
00401171 push ebp
00401172 push esi
00401173 push edi
00401174 call StartMeassure (00401150)
30: StartMeassure();
31:
32:
33:
34: register int a,b,c,d,e,f,g,i;
35:
36: a = 0;
00401179 xor eax,eax
37: b = 1;
0040117B mov ecx,1
38: c = 2;
00401180 mov edx,2
39: d = 3;
00401185 mov edi,3
40: e = 4;
0040118A mov esi,4
41: f = 5;
0040118F mov ebx,5
00401194 mov ebp,5F5E100h
42: //g = 6;
43:
44: for ( i = 0; i < 100000000; ++i )
45: {
46: a = 2*a + 1;
00401199 lea eax,[eax+eax+1]
47: b += a;
0040119D add ecx,eax
48: c += b;
0040119F add edx,ecx
49: d += c;
004011A1 add edi,edx
50: e += d;
004011A3 add esi,edi
51: f += e;
004011A5 add ebx,esi
004011A7 dec ebp
004011A8 jne main+29h (00401199)
52: //g+= f;
53: }
54:
55: ::EndMeassure();
004011AA call EndMeassure (00401160)
56:
57: result = e;
004011AF mov dword ptr [result (0042a2f8)],esi
58:
59: PrintTime();
004011B5 call PrintTime (00401080)
004011BA pop edi
004011BB pop esi
004011BC pop ebp
60: return 0;
004011BD xor eax,eax
004011BF pop ebx
61: }
能力值:
(RANK:260 )
24 楼
对了,我用的是VC6,编译优化全开。
能力值:
( LV9,RANK:170 )
25 楼
这种情形下,那当然多一个寄存器快了