2012-02-04 8 views
6

Poiché non sembra esserci un intrinseco per ADC e non posso utilizzare l'assemblatore inline per l'architettura x64 con Visual C++, cosa devo fare se voglio scrivere una funzione utilizzando aggiungi con trasportare ma includerlo in uno spazio dei nomi C++?Visual C++ x64 add con carry

(Emulando con gli operatori di confronto non è un'opzione. Questo 256 megabit aggiuntivo è la prestazione critica.)

+0

Dicci di più su questo "256 megabit add". È abbastanza probabile che fare più aggiunte contemporaneamente utilizzando SIMD sarà considerevolmente più veloce, anche considerando che le transazioni devono essere gestite come un ulteriore passaggio. –

+0

Ho già fatto un po 'di ricerca. Vedi http://stackoverflow.com/questions/8866973/can-long-integer-routines-benefit-from-sse. – jnm2

+1

@ jnm2 - Il modo x64 sembra scrivere codice assembly separato e chiamarlo dalla funzione C++. L'assemblatore fa già parte del pacchetto. –

risposta

4

There is now an instrinsic per 012.365.693.495.in MSVC: _addcarry_u64. Il seguente codice

#include <inttypes.h> 
#include <intrin.h> 
#include <stdio.h> 

typedef struct { 
    uint64_t x1; 
    uint64_t x2; 
    uint64_t x3; 
    uint64_t x4; 
} uint256; 

void add256(uint256 *x, uint256 *y) { 
    unsigned char c = 0; 
    c = _addcarry_u64(c, x->x1, y->x1, &x->x1); 
    c = _addcarry_u64(c, x->x2, y->x2, &x->x2); 
    c = _addcarry_u64(c, x->x3, y->x3, &x->x3); 
    _addcarry_u64(c, x->x4, y->x4, &x->x4); 
} 

int main() { 
    //uint64_t x1, x2, x3, x4; 
    //uint64_t y1, y2, y3, y4; 
    uint256 x, y; 
    x.x1 = x.x2 = x.x3 = -1; x.x4 = 0; 
    y.x1 = 2; y.x2 = y.x3 = y.x4 = 0; 

    printf(" %016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", x.x4, x.x3, x.x2, x.x1); 
    printf("+"); 
    printf("%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", y.x4, y.x3, y.x2, y.x1); 
    add256(&x, &y); 
    printf("="); 
    printf("%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "\n", x.x4, x.x3, x.x2, x.x1); 
} 

produce il seguente output di montaggio da Visual Studio Express 2013

mov rdx, QWORD PTR x$[rsp] 
mov r8, QWORD PTR x$[rsp+8] 
mov r9, QWORD PTR x$[rsp+16] 
mov rax, QWORD PTR x$[rsp+24] 
add rdx, QWORD PTR y$[rsp] 
adc r8, QWORD PTR y$[rsp+8] 
adc r9, QWORD PTR y$[rsp+16] 
adc rax, QWORD PTR y$[rsp+24] 

che ha una add e tre adc come previsto.

Edit:

Sembra che ci sia una certa confusione su ciò che _addcarry_u64 fa. Se si guarda la documentazione di Microsoft per ciò a cui mi sono collegato all'inizio di questa risposta, mostra che non richiede alcun hardware speciale. Questo produce adc e funzionerà su tutti i processori x86-64 (e lo standard _addcarry_u32 funzionerebbe su processori ancora più vecchi). Funziona perfettamente con il sistema Ivy Bridge che ho provato.

Tuttavia, _addcarryx_u64 richiede adx (come mostrato nella documentazione di MSFT) e infatti non riesce a funzionare sul mio Ivy Bridge System.

+1

Questa risposta richiede una dichiarazione di non responsabilità, questa istruzione può essere utilizzata solo su processori Core di 4a generazione (Haswell e versioni successive). Altri 5-10 anni e un numero di telefono di supporto prima di poter contare ciecamente su di esso per essere disponibile. –

+0

@HansPassant Non sono in grado di confermarlo. Hai un riferimento per questo? – jnm2

+0

https://software.intel.com/en-us/node/523867 –

7

VS2010 è dotato di supporto per la compilazione e il collegamento il codice scritto in assembly tradotto da MASM (ml64.exe) . Basta saltare qualche anello per attivarlo:

  • Fare clic con il tasto destro del mouse sulla finestra Solution Explorer, Build Customizations, selezionare "masm".
  • Progetto + Aggiungi nuovo elemento, scegliere il modello C++ file ma il nome è something.asm
  • Assicurarsi che hai il bersaglio piattaforma x64 per il progetto. Build + Configuration Manager, selezionare "x64" nella combinazione "Piattaforma soluzione attiva". Se mancante, selezionare <New> e scegliere x64 dalla prima combinazione. Se manca sarà necessario rieseguire la configurazione e aggiungere il supporto per i compilatori a 64 bit.

Scrittura codice assembly utilizzando la sintassi MASM, riferimento is here. Tutorial di avvio rapido is here.

Lo scheletro del codice assembly è simile al seguente:

.CODE 
PUBLIC Foo 
Foo PROC 
    ret     ; TODO: make useful 
Foo ENDP 
END 

E chiamato da codice C++ in questo modo:

extern "C" void Foo(); 

int main(int argc, char* argv[]) 
{ 
    Foo(); 
    return 0; 
} 

supporto per il debugging completa è accessibile, sarete in genere vogliono almeno utilizzare la finestra Debug + Windows + Registers.

+0

La soluzione ideale in questo caso sarebbero le funzioni inline (assemblaggio in linea). L'utilizzo di un assemblatore e il collegamento nei file oggetto non lo faranno e il codice a 64 bit in MSVC non consente l'assemblaggio in linea. Quindi questo significa che l'OP deve scrivere molte altre funzioni (che il compilatore probabilmente fa già un buon lavoro) già in assembly per evitare chiamate di funzione. –

1

Ho implementato un numero intero a 256 bit utilizzando un array di unsigned long long e un assembly x64 usato per implementare l'add con carry. Ecco il chiamante C++:

#include "stdafx.h" 

extern "C" void add256(unsigned long long *a, unsigned long long * b, unsigned long long *c); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    unsigned long long a[4] = {0x8000000000000001, 2, 3, 4}; 
    unsigned long long b[4] = {0x8000000000000005, 6, 7, 8}; 
    unsigned long long c[4] = {0, 0, 0, 0}; 
    add256(a, b, c); // c[] == {6, 9, 10, 12}; 
    return 0; 
} 

Il add256 è implementato in assemblea:

; void add256(unsigned long long *a, unsigned long long * b, unsigned long long *c) 

.CODE 
PUBLIC add256 
add256 PROC 

    mov     qword ptr [rsp+18h],r8  
    mov     qword ptr [rsp+10h],rdx  
    mov     qword ptr [rsp+8],rcx  
    push    rdi  

    ; c[0] = a[0] + b[0]; 

    mov     rax,qword ptr 16[rsp] 
    mov     rax,qword ptr [rax]  
    mov     rcx,qword ptr 24[rsp] 
    add     rax,qword ptr [rcx]  
    mov     rcx,qword ptr 32[rsp] 
    mov     qword ptr [rcx],rax  

    ; c[1] = a[1] + b[1] + CARRY; 

    mov     rax,qword ptr 16[rsp] 
    mov     rax,qword ptr [rax+8]  
    mov     rcx,qword ptr 24[rsp] 
    adc     rax,qword ptr [rcx+8]  
    mov     rcx,qword ptr 32[rsp] 
    mov     qword ptr [rcx+8],rax  

    ; c[2] = a[2] + b[2] + CARRY; 

    mov     rax,qword ptr 16[rsp] 
    mov     rax,qword ptr [rax+10h]  
    mov     rcx,qword ptr 24[rsp] 
    adc     rax,qword ptr [rcx+10h]  
    mov     rcx,qword ptr 32[rsp] 
    mov     qword ptr [rcx+10h],rax  

    ; c[3] = a[3] + b[3] + CARRY; 

    mov     rax,qword ptr 16[rsp] 
    mov     rax,qword ptr [rax+18h]  
    mov     rcx,qword ptr 24[rsp] 
    adc     rax,qword ptr [rcx+18h]  
    mov     rcx,qword ptr 32[rsp] 
    mov     qword ptr [rcx+18h],rax  

    ; } 

    pop     rdi  
    ret  

    add256    endp 

    end       

So che indica che non volevi un emulato aggiungere con la soluzione di trasporto, e volevamo una soluzione ad alte prestazioni, ma , ancora, si può considerare unica soluzione il seguente C++ che ha un bel modo di simulare 256 numeri di bit:

#include "stdafx.h" 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    unsigned long long a[4] = {0x8000000000000001, 2, 3, 4}; 
    unsigned long long b[4] = {0x8000000000000005, 6, 7, 8}; 
    unsigned long long c[4] = {0, 0, 0, 0}; 
    c[0] = a[0] + b[0]; // 6 
    c[1] = a[1] + b[1] + (c[0] < a[0]); // 9 
    c[2] = a[2] + b[2] + (c[1] < a[1]); // 10 
    c[3] = a[3] + b[3] + (c[2] < a[2]); // 12 
    return 0; 
} 
+0

Ci scusiamo per il ritardo, ma la soluzione C++ non è corretta. Come semplificazione, considera a = 01 eb = 11 con carry = 1, quindi c = 01 con carry = 1 ma c knivil