所以这应该是一个非常容易回答的问题。
假设我有两个非常大的变量 a 和 b ,我想从 a中减去 b 即可。假设每个变量都是30个字。我不能只使用 sub 指导员吗?我被告知默认为 sub.w ,并且只会从 a 的第一个单词中减去 b 的第一个单词。
那我该怎么做呢?
答案 0 :(得分:2)
要执行多字减法,您可以从低位字开始,然后从两个变量中减去相应的字。使用sbb
指令处理借用,并仅在第1次减法时使用sub
。
mov dx, [ebx] ;First word of b
sub [eax], dx ;Subtract from 1st word of a
mov dx, [ebx+2] ;Second word of b
sbb [eax+2], dx ;Subtract from 2nd word of a
mov dx, [ebx+4] ;Third word of b
sbb [eax+4], dx ;Subtract from 3rd word of a
...
mov dx, [ebx+58] ;Thirtieth word of b
sbb [eax+58], dx ;Subtract from 30th word of a
更实用的解决方案是使用循环:
mov ecx, 30
xor esi, esi ;This clears the CF, needed for the very first SBB
Again:
mov dx, [ebx+esi]
sbb [eax+esi], dx
lea esi, [esi+2]
loop Again ; loop without clobbering CF.
有better ways to write fast adc
/ sbb
loops,但最佳选择因微体系结构而异。减少slow loop
instruction开销的一种简单方法是展开一点。
mov ecx, 15
xor esi, esi ;This clears the CF, needed for the very first SBB
Again:
mov dx, [ebx+esi]
sbb [eax+esi], dx
mov dx, [ebx+esi+2]
sbb [eax+esi+2], dx
lea esi, [esi+4]
loop Again
优化此任务的下一步是停止使用16位寄存器DX,而是使用更大的EDX寄存器。这将使完全展开版本中的指令数量减半,或者使循环版本中的迭代次数减半。我们可以这样做,因为“30个字长的变量”可以被认为是“15个双字长的变量”。
这是完全展开的版本:
mov edx, [ebx] ;First dword of b
sub [eax], edx ;Subtract from 1st dword of a
mov edx, [ebx+4] ;Second dword of b
sbb [eax+4], edx ;Subtract from 2nd dword of a
mov edx, [ebx+8] ;Third dword of b
sbb [eax+8], edx ;Subtract from 3rd dword of a
...
mov edx, [ebx+56] ;Fifteenth dword of b
sbb [eax+56], edx ;Subtract from 15th dword of a
和部分展开的循环版本:
mov ecx, 5
clc ;This clears the CF, needed for the very first SBB
Again:
mov edx, [ebx] ; <1>
sbb [eax], edx
mov edx, [ebx+4] ; <2>
sbb [eax+4], edx
mov edx, [ebx+8] ; <3>
sbb [eax+8], edx
lea ebx, [ebx+12]
lea eax, [eax+12]
loop Again
显然在x86-64上类似地使用RDX会进一步改进这个代码。请注意,30个单词对应7个qwords和1个双字。