Sto lavorando su aritmetica per la moltiplicazione di interi molto lunghi (circa 100.000 cifre decimali). Come parte della mia biblioteca, aggiungo due lunghi numeri.Accelerare x64 assembler ADD loop
Il profilo mostra che il mio codice viene eseguito fino al 25% del tempo nelle routine add() e sub(), quindi è importante che siano il più veloci possibile. Ma non vedo molto potenziale, ancora. Forse puoi darmi un aiuto, consigli, idee o idee. Li metterò alla prova e tornerò da te.
Finora la mia routine di add fa un po 'di messa a punto e quindi utilizza un 8 volte ciclo srotolato:
mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
adc rax, r10
mov QWORD PTR [rcx+r11*8-64], rax
altre 7 blocchi con diversi offset seguono e poi loop.
Ho provato a caricare i valori dalla memoria in precedenza, ma ciò non ha aiutato. Immagino che sia a causa del buon prefetching. Io uso una CPU Intel i7-3770 Ivy Bridge a 4 core. Ma mi piacerebbe scrivere un codice che funzioni bene su qualsiasi CPU moderna.
Modifica: ho eseguito alcuni tempi: aggiunge 1k parole in circa 2,25 cicli/parola. Se rimuovo l'ADC, quindi rimangono solo i MOV, ci vogliono ancora circa 1,95 cicli/parola. Quindi il collo di bottiglia principale sembra essere l'accesso alla memoria. Una libreria memcpy()
funziona in circa 0,65 cicli/parola, ma ha solo un input, non due. Eppure, è molto più veloce a causa del suo uso di registri SSE, immagino.
Alcune domande:
- È utile usare "carico, carico, aggiungere, negozio di" struttura o sarebbe un "carico, add-a-memoria" aiutare? Finora i miei test non hanno mostrato alcun vantaggio.
- Come al solito, non è previsto alcun aiuto da SSE (2,3,4)?
- L'indirizzamento (indice scalato più base più offset) ha un impatto negativo? Potrei usare
ADD r11, 8
invece. - E il ciclo di svolgimento? Ho letto che lo srotolamento era negativo per l'architettura Sandy Bridge (Agner Fog http://www.agner.org/optimize/). È preferibile o evitato?
- (Modifica) Posso utilizzare i registri SSE per caricare e archiviare parole in blocchi più grandi dalla memoria e scambiare in modo efficiente parole con registri di uso generale e registri SSE?
Apprezzo molto qualsiasi commento.
Il modo più veloce (lo so) di moltiplicare un numero molto elevato è una trasformata di Fourier veloce http://en.wikipedia.org/wiki/Multiplication_algorithm Non ho mai provato a implementare la sua logica in assembler. Precious Prime95 contiene una veloce trasformata di Fourier nella logica di assemblaggio x86 e puoi prenderla (liberamente) da lì –
Grazie, lo so. In questo momento voglio solo aggiungere velocemente. – cxxl
È possibile esaminare le origini GMP. – zch