Tre modifiche, tutte a parità di risultato (verificate end-to-end):
- Ammortamento della moltiplicazione scalare: prima scalar_mult_basic
(l'operazione di gran lunga più costosa) veniva rieseguita a ogni
batch, cioè 40 volte per kernel launch. Ora viene eseguita UNA sola
volta per launch; il punto base P0 viene poi fatto avanzare di
GPU_EC_BATCH_SIZE*G a ogni round con una semplice addizione EC
affine, ripiegata nella batch inversion già presente (nuovo slot
NEXT negli array Zj/prefix). La scalar mult è così ammortizzata su
~5120 chiavi invece di 128.
- Una sola inversione di campo per round invece di due: la
normalizzazione del nuovo P0 condivide l'inversione di Montgomery
del batch.
- Inversione modulare tramite la catena di addizione di libsecp256k1
(255 quadrati + 15 moltiplicazioni) al posto del quadrato-e-moltiplica
ingenuo (~256 + ~128). Rimosso il vecchio modinv_fermat e la costante
FIELD_P_MINUS_2 non più usata.
- Rimosso l'array locale invZ[] (passaggio all'indietro fuso con la
conversione affine + match), riducendo la memoria locale per-thread e
migliorando l'occupancy.
Misurato nello stesso ambiente/scheda: 15.0M -> ~30M keys/sec.
Correttezza verificata end-to-end con privkey nota a offset 0, 50, 127,
128 (avanzamento del punto base), 200 e 5119 (40 avanzamenti
consecutivi): tutte trovate con la chiave privata corretta. Nessun
falso positivo su chiave pubblica valida casuale.
Porta la stessa strategia algoritmica della versione CPU (batch EC in
coordinate Jacobiane con Z1=1 + un'unica inversione di campo per batch
via trucco di Montgomery) su GPU NVIDIA, dove però GMP non è
disponibile: l'aritmetica a 256 bit mod il primo di secp256k1 è scritta
a mano (limb a 64 bit + __int128, riduzione veloce sfruttando
2^256 ≡ 2^32+977 mod p), e ogni thread CUDA agisce come una lane di
ricerca indipendente con la propria chiave privata iniziale casuale —
esattamente come un thread CPU, ma migliaia in parallelo invece di una
manciata.
La moltiplicazione scalare su device è un semplice double-and-add
(nessun wNAF/tabelle precalcolate come in libsecp256k1 lato host), il
principale margine di ottimizzazione futura. Il matching usa un Bloom
filter + ricerca binaria su un array di target ordinato, entrambi
caricati una volta sola in memoria device.
Aggiunge i target `make gpu` / `make gpu-info` al Makefile (NVCC_ARCH
configurabile, default sm_61/Pascal) e documenta in README che su WSL2
il driver NVIDIA arriva dall'host Windows: dentro WSL serve installare
solo il CUDA Toolkit, mai un driver.
Verificato: aritmetica di campo, moltiplicazione scalare e batch add
confrontati bit-per-bit con secp256k1_ec_pubkey_create (nessuna
discrepanza su ~30 valori di test); run end-to-end con privkey nota
trova la chiave corretta; nessun falso positivo su chiavi pubbliche
valide casuali. Su una Quadro P4000 (Pascal): ~15M keys/sec, contro
~3.5-4M keys/sec della CPU con 11 thread nello stesso ambiente.