Сборка: появление целых чисел в массиве - PullRequest
1 голос
/ 26 мая 2020

Я пишу программу на сборке masm для подсчета и возврата количества раз, когда целые числа появляются в массиве. В настоящее время у меня есть следующий код, который позволяет мне заполнять массив случайными целыми числами. Я борюсь с тем, как реализовать счетчик, который будет хранить каждое вхождение целого числа в индексе в массиве. например, если бы случайный массив был [3,4,3,3,4,5,7,8], я бы хотел, чтобы мой массив count содержал [3, 2, 1, 1, 1], поскольку есть (три тройки, две четверки и т. д. c).

У меня фиксированные границы случайных чисел равны 3/8, поэтому я знаю, что они будут в этом диапазоне. В настоящее время я думаю, что нужно сравнивать каждое число с 3-8 по мере его добавления и соответственно увеличивать мой массив count. Мой главный недостаток понимания заключается в том, как я могу увеличивать указанные c индексы массива. В этом коде я создаю массив случайных целых чисел, имея представление о том, как я могу начать подсчет целых чисел, но я не знаю, иду ли я в правильном направлении. Есть какие-нибудь советы?

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

Ответы [ 2 ]

0 голосов
/ 26 мая 2020

Я сейчас считаю, что сравнивать каждое число с 3-8 по мере его добавления

Нет, вы сильно усложняете это. Вы не хотите выполнять линейный поиск j (индекс по счетчикам), например arr[i] == j, просто используйте j = arr[i].

Стандартный способ построения гистограммы - ++counts[ arr[i] ]. В вашем случае вы знаете, что возможные значения - 3..8, поэтому вы можете сопоставить значение массива с ведром счетчика с помощью arr[i] - 3, поэтому вы будете работать с counts[0..5]. Команда назначения памяти add с режимом адресации с масштабированным индексом может сделать это в одной инструкции x86 , учитывая значение элемента в регистре.

Если возможные значения не являются смежными , вы обычно используете таблицу ha sh для сопоставления значений для подсчета сегментов. Вы можете думать об этом простом случае как о тривиальной функции ha sh.


Поскольку вы генерируете случайные числа для заполнения 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 за вас, вы должны сделать это самостоятельно, возможно, с помощью rep stosd с EAX = 0 как memset элементов двойного слова ECX, а затем перезагрузите EDI и ECX из аргументов стека.

Я предполагаю, что UPPER и LOWER - это константы времени сборки, например UPPER = 8 или LOWER equ 3, поскольку вы использовали для них имена в верхнем регистре, и они не являются аргументами функций. В таком случае нет необходимости выполнять математические вычисления во время выполнения, просто позвольте ассемблеру вычислить UPPER - LOWER + 1 за вас.


Я избегал инструкции loop , потому что она медленная , и не делает того, что нельзя сделать с другими простыми инструкциями.

Один стандартный трюк с производительностью для гистограмм с несколькими сегментами - иметь несколько массивов счетчиков и разворачивать их: Способы векторизации гистограммы в SIMD? . Это скрывает задержку сохранения / перезагрузки, когда один и тот же счетчик должен увеличиваться несколько раз подряд. Однако ваши случайные значения, как правило, позволяют избежать длинных прогонов одного и того же значения, поэтому производительность в наихудшем случае избегается.

Для больших массивов может быть что-то выиграть от AVX2, поскольку существует только 6 возможных блоков: * Микрооптимизация гистограммы с 4 ведрами большого массива или списка . (И вы можете сгенерировать случайные числа в векторах SIMD с помощью AVX2 xorshift128 + PRNG, если хотите.)

0 голосов
/ 26 мая 2020

Если ваш диапазон фиксирован (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), либо в куче, то есть с вызовами heapalloc / mallo c. ( Отредактировано, видите, вы используете 32-битные регистры )

...