我一直在努力掌握x86汇编语言,并想知道是否有一个类似于movl $1, %eax
的简短等效方式。这时,我想到了频繁使用的习语列表可能是一个好主意。
这可能包括首选使用xorl %eax,%eax
而不是movl $0,%eax
,或者testl %eax,%eax
与cmpl $0,%eax
的比较。
哦,每个帖子请友好地发布一个例子!
这里有一个很有趣的“习语”。希望每个人都知道,与乘法相比,除法需要更多的时间。使用一些数学知识,可以通过乘以常数的倒数来代替除法。这超出了 SHR 技巧。例如,要除以 5:
mov eax, some_number
mov ebx, 3435973837 // 32-bit inverse of 5
mul ebx
现在,通过不使用慢除法指令,eax已被除以5。以下是从http://blogs.msdn.com/devdev/archive/2005/12/12/502980.aspx中窃取的有用除数常量列表。3 2863311531
5 3435973837
7 3067833783
9 954437177
11 3123612579
13 3303820997
15 4008636143
17 4042322161
对于列表中没有的数字,您可能需要在进行操作之前进行偏移(除以6,shr 1,然后乘以3的倒数)。
在x64上:
xor eax, eax
对于
xor rax, rax
第一个指令也隐式地清除了 rax
的上半部分,但具有较小的操作码。
使用LEA
进行乘法运算,例如:
lea eax, [ecx+ecx*4]
对于 EAX = 5 * ECX
你可能会问如何在汇编中进行优化。然后你必须问自己,你要优化什么:大小还是速度?无论如何,这是我的 "惯用语",可以替换 xchg
:
xor eax, ebx
xor ebx, eax
xor eax, ebx
扩展我的评论:
对于像 Pentium Pro 这样的不加区分的处理器,xorl %eax,%eax
看起来似乎依赖于 %eax
,因此必须等待该寄存器的值可用。后来的处理器实际上有额外的逻辑来识别那个指令没有任何依赖。
incl
和 decl
指令设置一些标志但保持其他标志不变。如果将标志建模为指令重新排序的单个寄存器,则情况最差:在 incl
或 decl
之后读取标志的任何指令都必须被视为依赖于 incl
或 decl
(如果它正在读取该指令设置的标志之一),并且还依赖于先前设置标志的指令(如果它正在读取该指令未设置的标志之一)。解决方案是将标志寄存器分成两个,并考虑到这种更细粒度的依赖关系……但 AMD 有一个更好的想法,并从他们几年前提出的 64 位扩展中完全删除了这些指令。
关于链接,我在英特尔手册中发现了这个链接,但提供链接是无用的,因为它们在每六个月重新组织一次公司网站,或者在 Agner Fog 的网站上:http://www.agner.org/optimize/#manuals
在循环中...
dec ecx
cmp ecx, -1
jnz Loop
is
dec ecx
jns Loop
使用SHL
和SHR
进行2的幂次方乘法/除法
y*320 = (y << 8) + (y << 6)
。但这并不总是比简单的乘法更快,这取决于你的处理器。 - csl除了 xor
之外,还有另一种方法可以实现这个功能。
mov eax, 0 ; B800000000h
is
sub eax, eax ; 29C0h
理由:更小的操作码
不确定这是否算成语,但在i7之前的大多数处理器上
movq xmm0, [eax]
movhps xmm0, [eax+8]
或者,如果SSE3可用,
lddqu xmm0, [eax]
相较于从未对齐的内存位置读取,它们更快。
movdqu xmm0, [eax]
movl $1, %eax
很快且简短。在某些处理器上,xorl %eax, %eax
实际上比movl $0, %eax
更慢。在其他处理器上,incl %eax
比addl $1, %eax
更慢。如果您在2010年编写汇编代码,应该知道您正在为哪种架构编写并相应地选择您的“方言”(为了保持与语言的比喻)。 - Pascal Cuoqincl %eax
比addl $1, %eax
慢。此外,如果您能指向一些详细说明这种行为的链接,我将不胜感激! - susmits