查看以下汇编代码:
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]
Run Code Online (Sandbox Code Playgroud)
我得到代码摘录的书解释了第一个REP MOVS复制4字节块,第二个REP MOVS复制剩余的2字节块(如果存在).
REP MOVS说明书如何运作?根据MSDN,"该指令可以用REP作为前缀,重复操作ecx寄存器指定的次数." 难道不会一遍又一遍地重复同样的操作吗?
Jes*_*ter 15
有关特定指令的问题,请参阅指令集参考.
在这种情况下,您需要查找rep和movs(不是mov).简而言之,rep重复以下字符串操作ecx时间.根据方向标志的设置movs将数据移动ds:esi到es:edi指针并递增或递减指针.因此,重复它会将一系列内存移动到其他地方.
PS:通常将操作大小编码为指令后缀,因此人们使用movsb和movsd指示byte或dword操作.然而,一些编译器允许指定大小为你的榜样,通过byte ptr或dword ptr.此外,操作数隐含在指令中,您无法修改它们.
在汇编代码级别,允许使用此指令的两种形式:"显式操作数"形式和"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体系结构优化-手册.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这样的旧处理器上获得最佳结果.