2015-02-15 20 views
6

Sono bloccato lì cercando di capire come convertire le ultime due istruzioni "if" del codice seguente in uno stato senza ramo.Conversione a diramazioni if ​​if consecutive

int u, x, y; 
x = rand() % 100 - 50; 
y = rand() % 100 - 50; 

u = rand() % 4; 
if (y > x) u = 5; 
if (-y > x) u = 4; 

Oppure, nel caso in cui quanto sopra risulta essere troppo difficile, si può considerare loro come:

if (x > 0) u = 5; 
if (y > 0) u = 4; 

penso che quello che mi viene è il fatto che coloro che non hanno un else Catcher. Se fosse il caso, avrei probabilmente potuto adattare una variante di una funzione senza ramo abs (o max/min).

Le funzioni rand() visualizzate non fanno parte del codice reale. Li ho aggiunti in questo modo solo per suggerire gli intervalli previsti che le variabili e u possono avere nel momento in cui i due rami si verificano.

Il codice macchina di assemblaggio è consentito per lo scopo.

EDIT:

Dopo un po 'di braingrinding sono riuscito a mettere insieme una versione senza rami di lavoro:

int u, x, y; 
x = rand() % 100 - 50; 
y = rand() % 100 - 50; 

u = rand() % 4; 
u += (4-u)*((unsigned int)(x+y) >> 31); 
u += (5-u)*((unsigned int)(x-y) >> 31); 

Purtroppo, a causa della aritmetica intera coinvolta, la versione originale con se le dichiarazioni risulta essere più veloce di un intervallo del 30%.

Il compilatore sa dove si trova la festa.

+0

È possibile utilizzare x e y altrove? Altrimenti, ognuna di queste linee potrebbe essere 'if (rand()% 2) ...'. –

+1

'u + = (5 - u) * (y> x);'? –

+0

@OliverCharlesworth vedo. Sì, sono usati altrove. – user2464424

risposta

2

[Tutti: questa risposta è stata scritta supponendo che le chiamate su rand() facessero parte del problema. Offro miglioramenti sotto a questa ipotesi. OP chiarisce tardivamente che ha usato solo rand per dirci intervalli (e presumibilmente distribuzione) dei valori di x e y. Non chiaro se intendesse il valore anche per te. Ad ogni modo, goditi la mia migliorata risposta al problema che non ha veramente posto].

penso che si sarebbe meglio ricodificazione questo come:

int u, x, y; 
x = rand() % 100 - 50; 
y = rand() % 100 - 50; 

if (y > x) u = 5; 
else if (-y > x) u = 4; 
else u = rand() % 4; 

Ciò richiede l'ultima rand soltanto 1/4 come spesso come codice originale del PO. Dal momento che presumo che rand (e le divisioni) sono molto più costose rispetto a confronto-e-ramo, questo sarebbe un risparmio significativo.

Se il generatore rand produce un sacco di bit veramente casuali (ad esempio 16) su ogni chiamata come dovrebbe, si può chiamare solo una volta (rand ho assunto è più costoso di dividere, YMMV):

int u, x, y, t; 
t = rand() ; 
u = t % 4; 
t = t >> 2; 
x = t % 100 - 50; 
y = (t/100) %100 - 50; 

if (y > x) u = 5; 
else if (-y > x) u = 4; 

Penso che la funzione rand nella libreria MS C non sia abbastanza buona per questo se si vogliono valori veramente casuali. Ho dovuto programmare il mio; è risultato più veloce comunque.

Si potrebbe anche eliminare il divario, utilizzando moltiplicazione per un reciproco (non testata):

int u, x, y; 
unsigned int t; 
unsigned long t2; 
t = rand() ; 
u = t % 4; 

{ // Compute value of x * 2^32 in a long by multiplying. 
    // The (unsigned int) term below should be folded into a single constant at compile time. 
    // The remaining multiply can be done by one machine instruction 
    // (typically 32bits * 32bits --> 64bits) widely found in processors. 
    // The "4" has the same effect as the t = t >> 2 in the previous version 
    t2 = (t * ((unsigned int)1./(4.*100.)*(1<<32)); 
} 
x = (t2>>32)-50; // take the upper word (if compiler won't, do this in assembler) 
{ // compute y from the fractional remainder of the above multiply, 
    // which is sitting in the lower 32 bits of the t2 product 
    y = (t2 mod (1<<32)) * (unsigned int)(100.*(1<<32)); 
} 

if (y > x) u = 5; 
else if (-y > x) u = 4; 

Se il compilatore non produrrà le istruzioni "giuste", dovrebbe essere semplice scrivere di montaggio codice per farlo.

+0

Mentre sono completamente d'accordo sul fatto che le funzioni rand() e modulo non siano esattamente le migliori per il codice critico delle prestazioni, in realtà le metto semplicemente lì per dare al lettore un suggerimento sull'intervallo di valori atteso che la "x", Le variabili "y" e "u" possono eventualmente contenere al momento della sezione di codice in argomento (la più importante "u"). Scusa per l'inconveniente, prometto che sarò più chiaro la prossima volta .. – user2464424

+0

Come la condizione di ramo è casuale, sarà in realtà molto costosa, in quanto il predittore del ramo CPU è impotente ad aiutare.Penso almeno tanto quanto il divario o la chiamata a rand() (che è speculazione, non prove a sostegno, anche se .. .). –

+1

Come per tutto, misura, misura, misura ... e poi considera cosa succede quando cambia la tecnologia di implementazione. (Se i rami sono ancora relativamente costosi, in tutte le mie varianti possono essere trasformati in mosse condizionali come suggerito da altre risposte. –

0

Alcuni trucchi che utilizzano gli indici di array, possono essere piuttosto veloci se il compilatore/CPU ha istruzioni in una sola fase per convertire i risultati di confronto in valori 0-1 (ad esempio "sete" di x86 e simili).

int ycpx[3]; 

/* ... */ 
ycpx[0] = 4; 
ycpx[1] = u; 
ycpx[2] = 5; 
u = ycpx[1 - (-y <= x) + (y > x)]; 

forma alternativa

int v1[2]; 
int v2[2]; 

/* ... */ 
v1[0] = u; 
v1[1] = 5; 
v2[1] = 4; 
v2[0] = v1[y > x]; 
u = v2[-y > x]; 

Quasi illeggibile ...

NOTA: In entrambi i casi l'inizializzazione di elementi array contenenti 4 e 5 può essere incluso nella dichiarazione e matrici possono essere apportate statico se la rientranza non è un problema per te.