这是我的解决方案我想知道它是否正确以及解决问题的另一种方法
include 'emu8086.inc'
org 100h
mov CX,n
lea SI,a
mov AL,0
start_sum_a: ;sum all the n elements of the first array
add AL, [SI]
inc SI
loop start_sum_a
mov CX,n
lea SI,b
start_sum_b: ;sum all the n elements of the 2nd array to
add AL, [SI] ;the first sum
inc SI
loop start_sum_b
call print_num ;print the sum
ret
a db 1,3,5,7,9,11,13,15,17,18
b db 0,2,4,6,8,10,12,14,16,19
n dw 10
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS
答案 0 :(得分:2)
我想知道它是否正确
你的解决方案看起来还不错,虽然我预感到“emu8086.inc”中定义的函数 print_num 会更期望AX
寄存器中的数字。因此,最好将mov AL,0
指令更改为xor ax,ax
,这将清除整个AX
寄存器,而不仅仅是低字节AL
。
解决问题的另一种方法是什么
如果为两个数组设置单独的指针,您可以选择在单个循环中完成工作。
lea si, a
lea di, b
mov cx, n
xor ax, ax
start_sum:
add al, [si] ;Element of a array
add al, [di] ;Element of b array
inc si
inc di
loop start_sum
但是由于这些数组的起点在内存中相距一定距离(10),因此只使用1个指针的解决方案:
lea si, a
mov cx, n
xor ax, ax
start_sum:
add al, [si] ;Element of a array
add al, [si + 10] ;Element of b array
inc si
loop start_sum
最后,由于这些数组在内存中相邻,因此循环可以更简单。只需将迭代次数加倍(Peter Cordes之一的建议之一):
lea si, a
mov cx, n
shl cx, 1 ;Double the counter
xor ax, ax
start_sum:
add al, [si] ;Element of a array or b array
inc si
loop start_sum
答案 1 :(得分:2)
解决问题的另一种方法是什么
总有很多方法可以做任何事情。有些将比其他更有效,并且有不同的效率衡量标准。不同的效率测量包括代码大小(指令字节),或小型阵列或大型阵列的性能。对于真正的8086,代码大小通常是性能的决定因素,但对于现代x86 CPU来说,这绝对不是真的。 (请参阅x86标记wiki以获取文档链接。)
没有必要在内存中存储10个;它应该是equ
常数。 IDK,如果你本来假装你正在编写一个没有利用一切都是汇编时间常数的函数。如果是这样,那么只需要小心你如何使用常量。 (比如不要写mov di
n + OFFSET a
以便在汇编时计算结束指针。)
通过从数组末尾开始计算索引并使用索引寻址模式,可以避免the slow loop
instruction而不增加循环中的指令数。
此外,由于您的阵列是相邻的,您可以使用一个从a开头开始到b结束的循环
mov bx, OFFSET a ; no point in using LEA for this
mov si, length_ab - 1 ; index of the last element
xor ax,ax
sum_loop: ; do {
add al, [bx+si]
dec si
jg sum_loop ; } while(si > 0)
jmp print_num ; tailcall optimization: print_num will return directly to our caller
;call print_num
;ret
section .rodata
a: db 1,3,5,7,9,11,13,15,17,18
b: db 0,2,4,6,8,10,12,14,16,19
end_b: ; put a label after the end of b
length_ab equ $ - a ; this is NASM syntax, IDK if emu8086 accepts it
n equ 10
或利用a
静态:add AL, [a + SI]
。在真正的8086上,这可能会更慢,因为它在循环中放置了额外的2个字节的代码,8086每次都必须重新获取。在现代CPU上,保存mov bx, OFFSET a
指令对于总代码大小是值得的。 (如果你在循环中多次使用相同的指针,那么将它放在寄存器中就可以了。)
如果您知道您的总和不会溢出一个字节,那么您可以与add ax, [si]
并行,并在末尾add al, ah
执行2个字节。但这绝对是一个特殊情况,并且处理一般情况(避免进入下一个字节)with SWAR techniques不能仅使用2字节字。在386或更新版本的16位代码中,您可以使用32位寄存器并分别屏蔽奇数和偶数字节。
在一些超标量CPU(如英特尔预Sandybridge,每个时钟周期只能执行一个负载)上,这会更快,允许您每个时钟添加近2个字节:
xor ax,ax
xor dx,dx
sum_loop: ; do{
mov cx, [si]
add al, cl
add dl, ch
add si, 2
cmp si, end_a
jb sum_loop ; } while (si < end_pointer)
add al, dl
;; mov ah,0 ; if necessary
但在其他CPU上,您可能最好只展开并使用add al, [si]
/ add dl, [si+1]
而不是使用单独的加载指令。
在英特尔P6和Sandybridge系列以外的CPU上,al
和ah
未单独重命名,因此add ah, ch
将对完整{{1}具有错误依赖关系注册。这就是我使用ax
代替dl
的原因。
请注意,ah
至少在现代英特尔CPU(Haswell / Skylake)上并非依赖性。它确实为零AX,但它不会删除旧的EAX值的无序执行数据依赖性。见How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent。它可能会在Sandybridge及更早版本中取消,但绝对更喜欢xor eax,eax
来归零寄存器。
如果您不需要您的代码与过时的8086兼容,您可以使用SSE2 psadbw
只需几步即可完成所有操作。
请参阅Sum reduction of unsigned bytes without overflow, using SSE2 on Intel以获取解释。
你的两个数组总共是20个字节,因此我们可以硬编码并将其处理为16 + 4。
xor ax,ax
是的,这将以16位模式组装(例如NASM)。
稍后截断而不是在每一步之后都可以添加,因为从低位字节开始或执行是同样的事情。
如果您无法利用pxor xmm0, xmm0 ; xmm0 = 0
movd xmm1, [a+16] ; load last 4 bytes
psadbw xmm1, xmm0 ; sum2 = xmm1 = |b[7]-0| + |b[8]-0] + ...
psadbw xmm0, [a] ; horizontal sum 16 bytes into 2 partial sums in the two 64-bit halves (sum0 and sum1)
; then combine those three 16-bit sums into a single sum.
paddw xmm1, xmm0 ; sum2 += sum0
punpckhqdq xmm0, xmm0 ; get the high half of xmm0
paddw xmm1, xmm0 ; sum2 += sum1
movd eax, xmm1
movzx eax, al ; truncate the sum to 8-bit
jmp print_num
section .rodata
ALIGN 16 ; having a aligned lets us use [a] as a memory operand, or movdqa
a: ...
b: ...
和a
相邻,您可以:
b
甚至避免读取数组的末尾:
movdqu xmm0, [a]
movdqu xmm1, [b]
paddb xmm0, xmm1 ; add packed bytes (no carry across byte boundaries)
psrldq xmm0, 6 ; shift out the high 6 bytes from past the end of a and b
我刚刚意识到,因为你显然希望将总和包裹到8位,你可以使用movq xmm0, [a]
pinsrw xmm0, [a+8], 4
来提高效率。对于大型数组,您可以使用paddb
累积并在结尾处执行一个paddb
。
psadbw