我正在尝试针对特定的 Kaby Lake CPU (i5-7300HQ) 优化以下子例程,理想情况下使代码比其原始形式至少快 10 倍。该代码在 16 位实模式下作为软盘式引导加载程序运行。它在屏幕上显示一个十位十进制计数器,计数 0 - 9999999999,然后停止。
我看过 Agner 的优化指南微架构 and Assembly, 指令性能表和英特尔的优化参考手册.
到目前为止我能做的唯一明智的优化就是交换loop
指令dec + jnz
, 解释here.
另一种可能的优化可能是交换lodsb
for mov + dec
,但我发现的相关信息是相互矛盾的,有些人说它略有帮助,而另一些人则说它实际上可能会损害现代 CPU 的性能。
我还尝试切换到 32 位模式,并将整个计数器保留在未使用的寄存器对中,以消除任何内存访问,但在阅读了一些内容后,我意识到这 10 位将立即被缓存,并且 L1 缓存之间的延迟差异和寄存器大约只有三倍,所以绝对不值得以这种格式使用计数器的额外开销。
(编者注:add reg
延迟为1个周期,add [mem]
延迟约为 6 个周期,包括 5 个周期的存储转发延迟。或者更糟糕的是如果[mem]
与视频 RAM 一样不可缓存。)
org 7c00h
pos equ 2*(2*80-2) ;address on screen
;init
cli
mov ax,3
int 10h
mov ax,0b800h
mov es,ax
jmp 0:start
start:
push cs
pop ds
std
mov ah, 4Eh
xor cx, cx
mov bl,'9'
countloop:
mov cl,10 ;number of digits to add to
mov si,counter+9 ;start of counter
mov di,pos ;screen position
stc ;set carry for first adc
next_digit:
lodsb ;load digit
adc al,0
cmp bl, al
jnc print
add al,-10 ;propagate carry if resulting digit > 9
print:
mov [si+1],al ;save new digit
stosw ;print
;replaced loop with a faster equivalent
;loop next_digit
dec cl
jnz next_digit
jnc countloop
jmp $
counter:
times 10 db '0'
times 510-($-$$) db 0
dw 0aa55h
我的问题是 - 我该怎么做才能达到预期的速度提升?我还可以学习哪些其他材料来更好地理解基本概念?
注:这个is学校作业。虽然直接的答案肯定会有所帮助,但我更希望得到相关学习材料的解释或指示,因为我们没有得到任何解释或指示。
编辑:将代码更改为最小的可重现示例