查看以下汇编代码:
MOV ESI, DWORD PTR [EBP + C]
MOV ECX, EDI
MOV EAX, EAX
SHR ECX, 2
LEA EDI, DWORD PTR[EBX + 18]
REP MOVS DWORD PTR ES:[EDI], DWORD PTR [ESI]
MOV ECX, EAX
AND ECX, 3
REP MOVS BYTE PTR ES:[EDI], BYTE PTR[ESI]
我从代码摘录中得到的书解释了第一个REP MOVS
复制4字节块,第二个REP MOVS
复制剩余的2字节块(如果存在)。
REP MOVS
说明如何运作?根据{{3}},"该指令可以用REP作为前缀,以重复操作ecx寄存器指定的次数。"不会一遍又一遍地重复同样的操作吗?
答案 0 :(得分:15)
有关特定指令的问题,请参阅指令集参考。
在这种情况下,您需要查找rep
和movs
(不是mov
)。
简而言之,rep
重复以下字符串操作ecx
次。 movs
将数据从ds:esi
移动到es:edi
,并根据方向标志的设置递增或递减指针。因此,重复它会将一系列内存移动到其他地方。
PS:通常将操作大小编码为指令后缀,因此人们使用movsb
和movsd
来指示byte
或dword
操作。但是,某些汇编程序允许通过byte ptr
或dword ptr
指定示例中的大小。此外,操作数隐含在指令中,您无法修改它们。
答案 1 :(得分:5)
在汇编代码级别,允许两种形式的指令:“显式操作数”形式和“nooperand”形式。显式操作数形式允许使用符号显式指定存储器的源地址和目标地址。提供此显式操作数表单以允许文档;但请注意,此表单提供的文档可能会产生误导。也就是说,符号不必指定正确的源和目标地址。源地址始终由DS指定:(RSI / ESI / SI),目标地址始终由ES:(RDI / EDI / DI)寄存器指定,必须在执行movsb
指令之前正确加载。这就是我理解英特尔在这个问题上的官方立场。
REP MOVS DWORD PTR ES:[EDI], DWORD PTR [ESI]
是REP MOVSD
的同义词;
REP MOVS BYTE PTR ES:[EDI], BYTE PTR[ESI]
是REP MOVSB
的同义词。
根据数据大小,有以下MOVS命令:
MOVS命令将数据从DS:(SI / ESI / RSI)复制到ES:(DI / EDI / RDI) - SI / DI寄存器的大小基于您当前的模式 - 16位,32-位或64位。 它还增加(减少)SI和DI寄存器(基于D标志,设置CLD以增加寄存器)。
MOVS命令不能使用除SI / DI之外的其他寄存器,因此无需指定它们。
如果MOVS命令以REP为前缀,则重复执行CX(ECX / RCX)字节数,减少CX,因此最后CX变为零。
自1993年生产第一台奔腾CPU以来,英特尔开始使更简单的命令执行得更快,复杂的命令(如REP MOVS) - 更慢。
因此,REP MOVS变得非常缓慢,没有理由使用它。
2013年,英特尔决定重新访问REP MOVS。如果CPU(2013年之后生产)具有CPUID ERMSB(Encanced REP MOVSB)位,则rep movsb和rep stosb命令的执行方式与旧处理器不同,并且应该是快速的。在实践中,它只对大块,256字节和更大的块快速,并且只有在满足某些条件时才会快速:
请参阅“英特尔优化手册”第3.7.6节“增强REP MOVSB和STOSB操作(ERMSB)http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
REP MOVS指令在小块上非常慢,因为启动成本约为35个周期。如果你在一个循环中做简单的MOV EAX,没有启动成本,你可以在这35个循环中复制大量数据。
请注意,ERMSB为REP MOVSB产生最佳结果,而不是REP MOVSD(MOVSQ)。所有REP MOVS指令都变得更快,但REP MOVSB是最快的。
因此,您所显示的代码在没有ERMSB的处理器上不是最佳的(因为普通的简单MOV EAX副本会更快),或者使用ERMSB(因为只有MOVSB很快,而不是MOVSD,尽管区别不大)
您提供的代码可能只会在1985年发布的80386这样的旧处理器上获得最佳效果。