程序集:数组中整数的出现



我正在用masm汇编编写一个程序来计算并返回整数在数组中出现的次数。我目前有以下代码,允许我用随机整数填充数组。我正在努力解决的是如何实现一个计数器,该计数器将在数组中的索引处存储整数的每次出现。例如,如果随机数组是 [3,4,3,3,4,5,7,8],我希望我的计数数组保持 [3, 2, 1, 1, 1],就像有一样(三个 3、两个 4 等(。

我将随机数的边界固定为 3/8,因此我知道它们将在此范围内。我目前的想法是将每个数字与 3-8 进行比较,并分别增加我的计数数组。我主要缺乏理解是如何增加数组的特定索引。这段代码是我生成随机整数数组的方式,并知道如何开始计算整数出现次数,但我不知道我是否朝着正确的方向前进。有什么建议吗?

push ebp
mov  ebp, esp
mov  esi, [ebp + 16]    ; @ holds array to store count of integer occurances
mov  edi, [ebp + 12]  ; @ holds array to be populated with random ints
mov  ecx, [ebp + 8]   ; value of request in ecx

MakeArray:              
mov     eax, UPPER          ; upper boundary for random num in array
sub     eax, LOWER          ; lower boundary for random num in array
inc     eax             
call    RandomRange
add     eax, LOWER
cmp     eax, 3          ; Where I start to compare the random numbers added
je      inc_3           ; current thought is it cmp to each num 3-8
mov     [edi], eax  ; put random number in array
add     edi, 4      ; holds address of current element, moves to next element
loop    fillArrLoop
inc_3:          ; if random num == 3
inc     esi         ; holds address of count_array, increments count_array[0] to 1?
mov     [edi], eax  ; put random number in array to be displayed
add     edi, 4      ; holds address of current element, moves to next element
loop    MakeArray

我目前的想法是将每个数字与 3-8 进行比较,因为它被添加

不,你把这复杂化了。 您不想线性搜索j(索引到计数中(以便arr[i] == j,只需使用j = arr[i]即可。

制作直方图的标准方法是++counts[ arr[i] ]. 在您的情况下,您知道可能的值为 3..8,因此您可以使用arr[i] - 3将数组值映射到计数存储桶,因此您将对counts[0..5]进行操作。给定寄存器中的元素值,具有缩放索引寻址模式的内存目标add指令可以在一条 x86 指令中执行此操作

如果可能的值不连续,您通常使用哈希表将值映射到对存储桶进行计数。 您可以将这个简单的情况视为允许一个微不足道的哈希函数。


由于您在直方图的同时生成随机数来填充arr[i],因此您可以将这两个任务结合起来,而不是减去 3,只是不要添加它。

; inputs: unsigned len, int *values, int *counts
; outputs: values[0..len-1] filled with random numbers, counts[] incremented
; clobbers: EAX, ECX, EDX  (not the other registers)
fill_array_and_counts:
push ebp
mov  ebp, esp
push esi        ; Save/restore the caller's ESI.
;; Irvine32 functions like RandomRange are special and don't clobber EAX, ECX, or EDX except as return values,
;; so we can use EDX and ECX even in a loop that makes a function call.
mov  edi, [ebp + 16]    ; int *counts  ; assumed already zeroed?
mov  edx, [ebp + 12]    ; int *values  ; output pointers
mov  ecx, [ebp + 8]     ; size_t length
MakeArray:                       ; do{
mov     eax, UPPER - LOWER + 1   ; size of random range, calculated at assemble time
call    RandomRange                  ; eax = 0 .. eax-1
add     dword ptr [edi + eax*4], 1   ; ++counts[ randval ]
add     eax, LOWER               ; map 0..n to LOWER..UPPER
mov     [edx], eax               ; *values = randval+3
add     edx, 4                   ; values++
dec     ecx
jnz     MakeArray            ; }while(--ecx);
pop   edi                  ; restore call-preserved regs
pop   ebp                  ; including tearing down the stack frame
ret

如果调用方没有为您清零counts数组,您应该自己执行此操作,也许使用 EAX=0rep stosd作为 ECX dword 元素的memset,然后从堆栈参数重新加载 EDI 和 ECX。

我假设 UPPER 和 LOWER 是汇编时间常量,如UPPER = 8LOWER equ 3,因为您对它们使用了全大写的名称,并且它们不是函数参数。 如果是这种情况,那么就没有必要在运行时进行数学运算,只需让汇编程序为您计算UPPER - LOWER + 1即可。


我避免使用loop指令,因为它很慢,并且不会做任何你用其他简单指令做不到的事情。

对于只有几个存储桶的直方图,一个标准的性能技巧是拥有多个计数数组并在其上展开:在 SIMD 中矢量化直方图的方法? 这隐藏了当同一计数器需要连续多次递增时存储/重新加载的延迟。 但是,您的随机值通常会避免相同值的长时间运行,因此可以避免最坏情况的性能。

对于大型阵列,AVX2 可能会有所收获,因为只有 6 个可能的存储桶:大型数组或列表的 4 个存储桶直方图的微优化。 (如果需要,您可以使用 AVX2 xorshift128+ PRNG 在 SIMD 向量中生成随机数。

如果你的范围是固定的(3-8(,你有一个固定长度的数组来保存你的计数:

(index0:Count of 3),(index1:Count of 4)..(index5:Count of 8s)

一旦你从随机数组中获得了一个元素,你只需获取该元素并将其通过开关:

cmp 3, [element]
jne compare4
mov ebx, [countsArrayAddress]     ; Can replace [countsArrayAddress] with  [ebp + 16]
add ebx, 0                        ; First index, can comment out this line
mov ecx, [ebx]
add ecx, 1                        ; Increment count
mov [ebx], ecx                    ; Count at the zeroth offset is now incremented
compare4:
cmp 4, [element]
jne compare5
mov ebx, [countsArrayAddress]
add ebx, 4                         ; Second index (1*4)
mov ecx, [ebx]
add ecx, 1
mov [ebx], ecx
...

这是你的意思吗?我来自使用fasm语法,但它看起来非常相似。上面的块有点未优化,但认为这显示了如何构建 counts 数组。数组有一个固定长度,必须在堆栈(sub rsp 正确的数量(或堆上分配,即使用堆/malloc 调用。(已编辑,请参阅您使用的是 32 位寄存器(

最新更新