Rilevamento di overflow in somma


8

Supponiamo che mi venga dato un array di numeri interi a larghezza fissa (cioè si inseriscono in un registro di larghezza ), . Voglio calcolare la somma su una macchina con l'aritmetica del complemento di 2, che esegue aggiunte modulo con semantica avvolgente. È facile, ma la somma può superare la dimensione del registro e, in caso affermativo, il risultato sarà errato.nwun'1,un'2,...un'nS=un'1+...+un'n2w

Se la somma non trabocca, voglio calcolarla e verificare che non ci sia troppo pieno, il più velocemente possibile. Se la somma trabocca, voglio solo sapere che lo fa, non mi interessa alcun valore.

L'aggiunta ingenua di numeri in ordine non funziona, perché una somma parziale potrebbe traboccare. Ad esempio, con i registri a 8 bit, è valido e ha una somma di , anche se la somma parziale supera l'intervallo del registro .(120,120,-115)125120+120[-128,127]

Ovviamente potrei usare un registro più grande come accumulatore, ma ipotizziamo il caso interessante in cui sto già utilizzando la dimensione del registro più grande possibile.

Esiste una tecnica ben nota per aggiungere numeri con il segno opposto come la somma parziale corrente . Questa tecnica evita gli overflow in ogni fase, al costo di non essere compatibile con la cache e di non sfruttare molto la previsione del ramo e l'esecuzione speculativa.

Esiste una tecnica più veloce che forse sfrutta il permesso di overflow di somme parziali ed è più veloce su una macchina tipica con un flag di overflow, una cache, un predittore di diramazioni ed esecuzione e carichi speculativi?

(Questo è il seguito della somma sicura di Overflow )


Perché secondo te la soluzione di Dave non funziona bene con cache e pipeline? Se si esegue qualcosa di simile al partizionamento Quicksort sul posto con il pivot virtuale , si trattano bene le cache durante il partizionamento e la seguente somma. Non conosco le previsioni errate del ramo durante il partizionamento, ma la fase di sommatoria dovrebbe fare bene anche a questo proposito. 0
Raffaello

@Raphael Nella mia applicazione, l'overflow è il caso eccezionale. I condizionali corrispondenti a "questo overflow?" sono quindi ben serviti dalla previsione del ramo. I condizionali corrispondenti a "questo numero è positivo?" non può essere previsto. L'effetto cache è davvero leggero dato che hai due cursori invece di uno.
Gilles 'SO- smetti di essere malvagio' il

Risposte:


3

Puoi aggiungere numeri di dimensione senza overflow se stai usando l' aritmetica bits. Il mio suggerimento è di fare proprio questo e quindi verificare se il risultato è compreso nell'intervallo. Gli algoritmi per l'aritmetica multiprecisione sono ben noti (vedere la sezione TAOCP 4.3 se è necessario un riferimento), spesso è disponibile il supporto hardware per l'aggiunta ( carry flag e aggiungi con istruzione carry ), anche senza tale supporto è possibile implementarlo senza salto dipendente dai dati ( che è buono per i predittori di salti) e hai bisogno di un solo passaggio sui dati e puoi visitare i dati nell'ordine più conveniente (che è buono per la cache).nwlogn+w

Se i dati non si adattano alla memoria, il fattore limitante sarà l'IO e quanto riuscirai a sovrapporre l'IO con il calcolo.

Se i dati si adattano alla memoria, probabilmente avrai (l'unica eccezione che mi viene in mente è il microprocessore a 8 bit che di solito ha 64K di memoria), il che significa che stai facendo un'aritmetica a doppia precisione . L'overhead su un loop falognww-bit aritmetica può essere solo due istruzioni (una da firmare ext, l'altra da aggiungere con carry) e un leggero aumento della pressione del registro (ma se ho ragione, anche il registro x86 affamato ha abbastanza registri che l'unico accesso alla memoria in il ciclo interno può recuperare i dati). Penso che sia probabile che un processore OO sarà in grado di pianificare le operazioni aggiuntive durante la latenza del carico di memoria, quindi il ciclo interno verrà eseguito alla velocità della memoria e quindi l'esercizio sarà uno di massimizzare l'uso della larghezza di banda disponibile (prefetch o tecniche di interfogliatura potrebbero aiutare a seconda dell'architettura della memoria).

Considerando l'ultimo punto, è difficile pensare ad altri algoritmi con prestazioni migliori. I salti dipendenti dai dati (e quindi non prevedibili) sono fuori discussione, così come diversi passaggi sui dati. Anche tentare di utilizzare i diversi core del processore di oggi sarebbe difficile poiché la larghezza di banda della memoria sarà probabilmente satura, ma potrebbe essere un modo semplice per implementare l'accesso interfogliato.


Non riesco ad aumentare le dimensioni dei registri sulla mia macchina. Supponiamo che sto già utilizzando la dimensione del registro più grande possibile.
Gilles 'SO- smetti di essere malvagio' il

@Gilles, i processori che conosco hanno il flag di overflow che vuoi che sfruttiamo hanno anche un carry e un Add con istruzioni carry . Anche per coloro che non lo fanno (qualcosa di diverso da MIPS?), L'aritmetica multiprecisione sarebbe un candidato serio (ha solo un passaggio sui dati - buono per la cache -, accedervi in ​​sequenza - buono per il pre-riempimento della cache - - e può essere implementato senza salto dipendente dai dati - buono per i predittori di salto).
Programmatore

Cosa intendi per "aritmetica multiprecisione"? Pensavo intendessi in virgola mobile. Ma molte architetture non hanno registri in virgola mobile abbastanza grandi, se presenti. Supponiamo di aggiungere numeri interi a 64 bit su amd64 o numeri interi a 32 bit su ARM senza VFP.
Gilles 'SO- smetti di essere malvagio' il

@Gilles, intendevo ciò che è descritto nella sezione 4.3 di TAOCP: l'uso di più parole per rappresentare valori che non possono essere contenuti in una sola parola. Bignum è una variante in cui il numero di parole viene regolato in modo dinamico, la mia ipotesi è che qui puoi determinare un limite massimo per il numero di parole necessarie (cioè 2 se i tuoi dati sono in memoria; in caso contrario, lavorando sulla sovrapposizione del L'IO con il calcolo sarà il primo punto di azione, sarai legato all'IO) e usalo, sarà abbastanza basso che la gestione di un numero variabile di parole sarà più costosa.
AProgrammer

Ah ok. Potresti chiarire questo nella tua risposta? Hai riferimenti con tempistiche e confronti con altri metodi?
Gilles 'SO- smetti di essere malvagio' il

1

Su una macchina in cui i tipi interi si comportano come un anello algebrico astratto [sostanzialmente significa che avvolgono], si potrebbero calcolare le somme degli articoli [i] e (item [i] >> 16) per un massimo di 32767 articoli. Il primo valore darebbe i 32 bit inferiori della somma corretta. Quest'ultimo valore produrrebbe i bit 16-47 di qualcosa vicino alla somma corretta, e usando il valore precedente potrebbe essere facilmente regolato per dare bit 16-47 della somma esatta esatta.

Lo pseudocodice sarebbe simile a:

Sum1=0 : Sum2 = 0
For up to 32768 items L[i] in list
  Sum1 = Sum1 +L[i]
  Sum2 = Sum2 +(L[i] >> 16) ' Use sign-extending shift
Loop
Sum1MSB = Sum1 >> 16 ' Cannot use division of numbers can be negative--see below
Sum2Mid = Sum2 and 65535
Sum2Adj = Sum1MSB - Sum2Mid
If Sum2Adj >= 32768 then Sum2Adj = Sum2Adj - 65536
Sum2 += Sum2Adj

Dopo il codice sopra, Sum2 e Sum1 insieme dovrebbero produrre la somma corretta, indipendentemente dagli overflow intermedi. Se è necessario sommare più di 32768 numeri, questi possono essere divisi in gruppi di 32768 e dopo aver calcolato Sum2 per ciascun gruppo, è possibile aggiungerlo a una "somma grande" a due variabili per tutti i gruppi nel loro insieme.

In alcune lingue, l'operatore di turno a destra potrebbe essere sostituito da una divisione per 65536. In genere funziona quando si calcola Sum2, ma non quando si estrae Sum1MSB. Il problema è che alcune lingue arrotondano le divisioni verso lo zero mentre è necessario qui eseguire un arrotondamento delle divisioni al successivo numero inferiore (verso l'infinito negativo). Gli errori nel calcolo di Sum2 verrebbero corretti in seguito, ma gli errori nel calcolo di Sum2LSB influenzerebbero il risultato finale.

Si noti che nulla nei risultati finali indicherebbe se uno qualsiasi dei calcoli che coinvolgono Sum1 fosse "traboccato", ma se i valori sono garantiti per avvolgere in modo pulito il codice non dovrebbe preoccuparsi se si è verificato un trabocco.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.