A n = 2 * A n-1 + A n-2 - это почти та же формула, что и последовательность Фибоначчи, поэтому вы можете найтимного полезного, ища это.(например, это q & a ).Но вместо простого добавления нам нужны 2a + b, и x86 может сделать это в одной инструкции LEA.
Вам никогда не нужно хранить переменные цикла в памяти, для этого нужны регистры.Таким образом, вместо каждой итерации, требующей извлечения данных из памяти (задержка ~ 5 циклов в оба конца), он может просто использовать регистры (дополнительная задержка 0 циклов).
Ваш массив может быть в .bss,вместо .data, поэтому вы не сохраняете эти нули в своем объектном файле.
arraysize equ 10 ; not DWORD: this is an assemble-time constant, not a value stored in memory
.bss
seq DWORD 0 DUP(arraysize) ; I think this is the right MASM syntax?
; NASM equivalent: seq RESD arraysize
.code
_main:
mov edx, 1 ; A[0]
mov [seq], edx
mov eax, 2 ; A[1]
mov [seq+4], eax
mov ecx, 8 ; first 8 bytes stored
; assume the arraysize is > 2 and even, so no checks here
seqloop:
lea edx, [eax*2 + edx] ; edx=A[n], eax=A[n-1]
mov [seq + ecx], edx ; or edx=A[n-1], eax=A[n-2]
lea eax, [edx*2 + eax]
mov [seq + ecx + 4], eax
; unrolled by two, so we don't need any MOV or XCHG instructions between registers, or any reloading from memory.
add ecx, 8 ; +8 bytes
cmp ecx, arraysize*4 ; (the *4 happens at assemble time)
jb seqloop
ret
Использование индекса массива для условия цикла означает, что мы использовали всего 3 регистра, и все же не нужносохранить / восстановить любой из обычных регистров ESI, EDI, EBX или EBP с сохранением вызовов.(И, конечно, ESP вызывающей стороны также восстанавливается).
Если вы заботитесь о производительности, цикл составляет всего 6 моп (слитый домен) на процессорах семейства Intel SnB.Для большего размера массива он может работать с одним результатом за такт (одна итерация на 2 такта).