2010-04-15 14 views
5

Ho appena consegnato questa funzione in un compito. È fatto (quindi nessun tag di compiti a casa). Ma mi piacerebbe vedere come questo può essere migliorato.Contribuire a migliorare una semplice funzione di assemblaggio

In sostanza, la funzione somma i quadrati di tutti i numeri interi compresi tra 1 e il numero dato, utilizzando la seguente formula:

n(n+1)(2n+1)/6 

Dove n è il numero massimo.

La funzione riportata di seguito consente di rilevare eventuali overflow e restituire 0 in caso contrario.

UInt32 sumSquares(const UInt32 number) 
{ 
    int result = 0; 
    __asm 
    { 
     mov eax, number //move number in eax 
     mov edx, 2  //move 2 in edx 
     mul edx   //multiply (2n) 
     jo end   //jump to end if overflow 
     add eax, 1  //addition (2n+1) 
     jo end   //jump to end if overflow 
     mov ecx, eax  //move (2n+1) in ecx 

     mov ebx, number //move number in ebx 
     add ebx, 1  //addition (n+1) 
     jo end   //jump to end if overflow 

     mov eax, number //move number in eax for multiplication 
     mul ebx   //multiply n(n+1) 
     jo end   //jump to end if overflow 
     mul ecx   //multiply n(n+1)(2n+1) 
     jo end   //jump to end if overflow 
     mov ebx, 6  //move 6 in ebx 
     div ebx   //divide by 6, the result will be in eax 

     mov result, eax //move eax in result 

end: 
    } 

    return result; 
} 

Fondamentalmente, voglio sapere cosa posso migliorare lì. Per lo più in termini di best-practice. Una cosa sembra ovvia: un controllo di overflow più intelligente (con un singolo controllo per qualunque input massimo causerebbe un overflow).

+1

CodeReview non è ancora dalla fase beta, ma questo sarebbe un buon candidato una volta che è. –

risposta

8
mov eax, number //move number in eax 
    mov ecx, eax  //dup in ecx 
    mul ecx   //multiply (n*n) 
    jo end   //jump to end if overflow 
    add eax, ecx  //addition (n*n+n); can't overflow 
    add ecx, ecx  //addition (2n); can't overflow 
    add ecx, 1  //addition (2n+1); can't overflow 
    mul ecx   //multiply (n*n+n)(2n+1) 
    jo end   //jump to end if overflow 
    mov ecx, 6  //move 6 in ebx 
    div ecx   //divide by 6, the result will be in eax 

    mov result, eax //move eax in result 

Riduzione della resistenza: aggiungere anziché moltiplicare.

Con l'analisi, un minor numero di controlli di overflow (è possibile fare meglio come descritto).

Mantieni i valori nei registri invece di tornare all'argomento sullo stack.

Registra attentamente i registri in modo che i valori che possono essere riutilizzati non vengano sovrascritti.

+0

Aggiungi non può overflow? Sto leggendo questo giusto? – MPelletier

+3

Impossibile eseguire l'overflow perché n * n no, quindi n * n + n non lo farà. Il più grande n tale che n * n non abbia overflow è 0xffff. 0xffff * 0xffff + 0xffff = 0xffff0000. Poiché a quel punto n <= 0xffff, 2n + 1 è al massimo 0x1ffff, di nuovo nessun overflow. –

+0

Se ti potessi, voterei due volte in questo momento. È una matematica solida! – MPelletier

3
mov eax, number ; = n 
cmp eax, 0x928  ; cannot handle n >= 0x928 
jnc end 
shl eax, 1   ; = n(2) 
add eax, 3   ; = n(2)+3 
mov ebx, number 
mul ebx   ; = n(n(2)+3) 
add eax, 1   ; = n(n(2)+3)+1 
mov ebx, number 
mul ebx   ; = n(n(n(2)+3)+1) = n(n+1)(2n+1) 
mov ebx, 6 
div ebx 
mov result, eax 

Anziché controllare l'overflow, questa soluzione controlla l'input rispetto al valore massimo noto che la funzione può gestire. Si noti che l'ultima moltiplicazione è consentita per l'overflow e sarà overflow per qualsiasi input number maggiore di 0x509. Il confronto con un valore noto anziché affidarsi ai controlli di overflow consente alla funzione di gestire quasi il doppio dei valori di input. In effetti, la funzione è in grado di gestire ogni input il cui risultato rientra in 32 bit.

3
UInt32 sumSquares(const UInt32 number) 
{ 
    __asm 
    { 
    mov eax, number  // n 
    cmd eax, MAX_VALUE 
    jg bad_value 

    lea ebx, [eax+1] // n + 1 
    lea ecx, [2*eax+1] // 2n + 1 

    mul ebx 
    mul ecx 

    shr eax, 1   // divide by 2 
    mov ebx, 2863311531 // inverse of 3 
    mul ebx    // divide by 3 

    ret 

    bad_value: 
    xor eax, eax  // return 0 
    ret 
    } 
} 

http://blogs.msdn.com/devdev/archive/2005/12/12/502980.aspx

Spara

+0

I lea tricks sono interessanti di cui parlare; Credo che possano essere più lenti della semplice aritmetica sui processori più recenti. Il moltiplicarsi per inverso è una buona tecnica che non avevo l'energia per descrivere ieri; Penso che il tuo risultato sia in edx, comunque, quindi devi spostarlo su eax. –

+0

@Doug Currie Non sono sicuro di cosa ti fa pensare che il risultato sia in EDX. Tutte le operazioni vengono eseguite su EAX e i bit di ordine elevato del multiplo sono in EDX (che vengono scartati). Mi sto perdendo qualcosa? Interessanti trucchi aritmetici sono certamente possibili, ma hai fatto un buon lavoro prima, quindi ho optato per un esempio più diretto. – Sparafusile

+0

Ho immaginato che stavi moltiplicando per 1431655765 (2^32/3). Leggendo più da vicino vedo che non lo sei. Il moltiplicare per inverso si utilizza solo opere quando il dividendo è un multiplo esatto del divisore. Sorprendentemente questo è il caso di questo algoritmo. +1 –

Problemi correlati