Quale verrà eseguito più velocemente, if (flag == 0) o if (0 == flag)?


111

Domanda dell'intervista: quale verrà eseguita più velocemente if (flag==0)o if (0==flag)? Perché?


330
Nominato per la domanda dell'intervista più stupida di sempre. E c'è una forte concorrenza.
Konrad Rudolph

119
Tu: Indica una situazione in cui potrebbe valere la pena di preoccuparsi della differenza tra questi due. Intervistatore: Ok, sei assunto.
Chris Lutz

37
L'unica differenza tra i due è che con la convenzione successiva, sei assicurato contro i bug come if(flag = 0)al prezzo di un po 'di leggibilità.
Amarghosh

22
@Amarghosh: a costo di rendere il tuo codice difficile da leggere e non intuitivo. Usa il primo per attivare gli avvisi del compilatore, vantaggioso per tutti.
GManNickG

129
Una volta che uno scrittore di compilatori ha ottenuto questo nella sua intervista. Sussurrò in risposta, "Uno che si vuole essere più veloce?".

Risposte:


236

Non ho ancora visto alcuna risposta corretta (e ce ne sono già alcune) avvertenza: Nawaz ha sottolineato la trappola definita dall'utente . E mi dispiace di aver votato frettolosamente alla "domanda più stupida" perché sembra che molti non abbiano capito bene e dà spazio per una bella discussione sull'ottimizzazione del compilatore :)

La risposta è:

Qual è flagil tipo di?

Nel caso in cui flageffettivamente sia un tipo definito dall'utente. Quindi dipende da quale sovraccarico operator==è selezionato. Ovviamente può sembrare stupido che non siano simmetriche, ma è certamente permesso, e ho già visto altri abusi.

Se flagè un built-in, allora entrambi dovrebbero prendere la stessa velocità.

Dalla articolo di Wikipedia su x86, scommetterei di Jxxistruzione per l' ifaffermazione: forse un JNZ(Vai se non nullo) o qualche equivalente.

Dubito che il compilatore manchi un'ottimizzazione così ovvia, anche con le ottimizzazioni disattivate. Questo è il tipo di cose per cui è progettata l' ottimizzazione dello spioncino .

EDIT: Sprang up di nuovo, quindi aggiungiamo un po 'di assembly (LLVM 2.7 IR)

int regular(int c) {
  if (c == 0) { return 0; }
  return 1;
}

int yoda(int c) {
  if (0 == c) { return 0; }
  return 1;
}

define i32 @regular(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

define i32 @yoda(i32 %c) nounwind readnone {
entry:
  %not. = icmp ne i32 %c, 0                       ; <i1> [#uses=1]
  %.0 = zext i1 %not. to i32                      ; <i32> [#uses=1]
  ret i32 %.0
}

Anche se non si sa leggere l'IR, penso che si spieghi da sé.


4
@ Matthieu: hai detto che non ho ancora visto nessuna risposta corretta .. ma la mia è corretta, penso: P
Nawaz

7
bene! la tua risposta possibile trasforma "la domanda più stupida" in "i più cattivi / meschini". "scaviamo un buco per il candidato e vediamo se ci cade dentro ..." :) Immagino che tutti assumiamo automaticamente che flagdebba essere un numero intero o booleano. OTOH, avere una variabile denominata flagdi un tipo definito dall'utente è abbastanza sbagliato di per sé, IMHO
davka

@Nawaz: potrei aver saltato l'ultimo paragrafo della tua risposta: p
Matthieu M.

1
@Nawaz: Non gareggio davvero, di solito leggo le domande molto tempo dopo che hanno ricevuto risposta e le persone tendono a leggere solo le prime risposte più votate :) Ma in realtà sto leggendo cose sulle ottimizzazioni del compilatore, e questo mi ha colpito come un tipico caso di ottimizzazione banale quindi ho pensato di segnalarlo a quei lettori che effettivamente si preoccupano ... Sono piuttosto sorpreso di aver ricevuto così tanti voti positivi. Ora è la mia risposta più votata anche se non è certo quella in cui mi impegno di più: / Comunque ho modificato la mia risposta e corretto la mia affermazione :)
Matthieu M.

2
@mr_eclair: un tipo incorporato è un tipo che è (come suggerisce il nome) integrato nella lingua. Cioè, è disponibile anche senza un'unica #includedirettiva. Per ragioni di semplicità, di solito è pari a int, char, boole simili. Tutti gli altri tipi sono detti essere definite dall'utente, cioè esistono perché sono il risultato di qualche utente dichiarando: typedef, enum, struct, class. Ad esempio, std::stringè definito dall'utente, anche se di certo non lo hai definito tu stesso :)
Matthieu M.

56

Stesso codice per amd64 con GCC 4.1.2:

        .loc 1 4 0  # int f = argc;
        movl    -20(%rbp), %eax
        movl    %eax, -4(%rbp)
        .loc 1 6 0 # if( f == 0 ) {
        cmpl    $0, -4(%rbp)
        jne     .L2
        .loc 1 7 0 # return 0;
        movl    $0, -36(%rbp)
        jmp     .L4
        .loc 1 8 0 # }
 .L2:
        .loc 1 10 0 # if( 0 == f ) {
        cmpl    $0, -4(%rbp)
        jne     .L5
        .loc 1 11 0 # return 1;
        movl    $1, -36(%rbp)
        jmp     .L4
        .loc 1 12 0 # }
 .L5:
        .loc 1 14 0 # return 2;
        movl    $2, -36(%rbp)
 .L4:
        movl    -36(%rbp), %eax
        .loc 1 15 0 # }
        leave
        ret

18
+1 per aver fatto il possibile per dimostrare che l'ottimizzazione del compilatore è la stessa.
k rey

56

Non ci saranno differenze nelle tue versioni.

Presumo che il typeflag of non sia un tipo definito dall'utente, piuttosto è un tipo predefinito. Enum è un'eccezione! . Puoi trattare enum come se fosse integrato. In effetti, i valori sono uno dei tipi incorporati!

Nel caso in cui, se è un tipo definito dall'utente (eccetto enum), la risposta dipende interamente da come hai sovraccaricato l'operatore ==. Nota che devi sovraccaricare ==definendo due funzioni, una per ciascuna delle tue versioni!


8
questo potrebbe essere l'unico motivo possibile per porre questa domanda, IMHO
davka

15
Sarei estremamente sorpreso se i compilatori moderni perdessero un'ottimizzazione così ovvia.
Pedro d'Aquino

3
Che io sappia ! non è un'operazione bit per bit
Xavier Combelle

8
@Nawaz: non ho votato negativamente ma la tua risposta è effettivamente sbagliata ed è orribile che abbia ricevuto così tanti voti positivi. Per la cronaca, il confronto di un numero intero con 0 è una singola istruzione di assemblaggio , completamente alla pari con la negazione. In effetti, se il compilatore è leggermente stupido, questo potrebbe anche essere più veloce della negazione (non probabile però).
Konrad Rudolph

6
@Nawaz: è ancora sbagliato dire che può, sarà o di solito sarà più veloce. Se c'è una differenza, allora la versione "confronta con zero" sarà più veloce, poiché quella di negazione si traduce in realtà in due operazioni: "nega operando; controlla che il risultato sia diverso da zero". In pratica, ovviamente, il compilatore lo ottimizza per produrre lo stesso codice della semplice versione "confronta con zero", ma l'ottimizzazione viene applicata alla versione di negazione, per farla recuperare, e non viceversa. Konrad ha ragione.
jalf

27

Non c'è assolutamente alcuna differenza.

Potresti guadagnare punti rispondendo a quella domanda dell'intervista facendo riferimento all'eliminazione degli errori di battitura di assegnazione / confronto, tuttavia:

if (flag = 0)  // typo here
   {
   // code never executes
   }

if (0 = flag) // typo and syntactic error -> compiler complains
   {
   // ...
   }

Anche se è vero, che ad esempio un compilatore C avverte nel caso del primo ( flag = 0), non ci sono tali avvisi in PHP, Perl o Javascript o <insert language here>.


@ Matthieu Huh. Devo essermi perso il post sul meta che descrive lo stile di rinforzo "corretto".
Linus Kleen

7
Non ho votato affatto, ma per quello che vale: perché è così importante che le persone si spieghino ogni volta che votano? I voti sono anonimi in base alla progettazione. Sono completamente contrario all'idea che i downvoter debbano sempre commentare, perché personalmente non voglio mai essere considerato il downvoter solo perché ho lasciato un commento sottolineando un problema. Forse il downvoter pensava che la maggior parte della risposta fosse irrilevante per la domanda sulla velocità? Forse ha pensato che incoraggiasse uno stile di codifica che non approvava? Forse era un coglione e voleva che la sua risposta avesse il punteggio più alto?
David Hedlund

3
Le persone dovrebbero essere libere di votare come vogliono, indipendentemente dal motivo. Dal punto di vista della reputazione, questa è quasi sempre una buona cosa in quanto spesso provoca altre persone a votare, a contrastare il voto negativo immeritato, quando in realtà un singolo voto positivo cancellerebbe cinque voti negativi immeritati.
David Hedlund

26
@David: I downvoters dovrebbero spiegarsi perché questo sito non parla di votazioni segrete di popolarità, voto anonimo o simili. Questo sito riguarda l'apprendimento. Se qualcuno dice che una risposta è errata votandola negativamente, il votante è egoista con la sua consapevolezza se non spiega perché. Sono disposti a prendersi tutto il merito quando hanno ragione, ma non sono disposti a condividere la conoscenza quando gli altri hanno torto.
John Dibling

1
Solo per togliere di mezzo il problema dello stile tonificante, penso davvero che Matthieu lo intendesse come uno scherzo. Sarei sorpreso di vedere che qualcuno esprime i propri voti a seconda di tali questioni. Detto questo, non tutti usano i voti esattamente allo stesso modo. Ho potuto vedere la logica del downvoting perché il post sembra sostenere uno stile di codifica che l'elettore potrebbe disapprovare (nota la differenza tra il sostenere uno stile di codifica - "se scrivi il tuo codice in questo modo, otterrai un errore del compilatore quando lo fai questo errore di battitura "- e usando semplicemente uno stile di codifica, come le parentesi graffe) In quel ...
David Hedlund

16

Non ci sarà assolutamente alcuna differenza in termini di velocità. Perché dovrebbe esserci?


7
se il compilatore è stato completamente ritardato. Questa è l'unica ragione.
JeremyP

@ JeremyP: non riesco a immaginare una differenza anche se il compilatore fosse ritardato. L'autore del compilatore dovrebbe farlo apposta per quanto ne so.
Jon

2
Supponendo che il processore abbia un'istruzione "test if 0", x == 0potrebbe essere utilizzata ma 0 == xpotrebbe utilizzare un confronto normale. Ho detto che avrebbe dovuto essere ritardato.
JeremyP

8
If flag è un tipo definito dall'utente con un sovraccarico asimmetrico dell'operatore == ()
OrangeDog

Perché potremmo avere virtual operator==(int)in un tipo definito dall'utente?
lorro

12

Ebbene, c'è una differenza quando flag è un tipo definito dall'utente

struct sInt
{
    sInt( int i ) : wrappedInt(i)
    {
        std::cout << "ctor called" << std::endl;
    }

    operator int()
    {
        std::cout << "operator int()" << std::endl;
        return wrappedInt;
    }

    bool operator==(int nComp)
    {
        std::cout << "bool operator==(int nComp)" << std::endl;
        return (nComp == wrappedInt);
    }

    int wrappedInt;
};

int 
_tmain(int argc, _TCHAR* argv[])
{
    sInt s(0);

    //in this case this will probably be faster
    if ( 0 == s )
    {
        std::cout << "equal" << std::endl;
    }

    if ( s == 0 )
    {
        std::cout << "equal" << std::endl;
    }
}

Nel primo caso (0 == s) viene chiamato l'operatore di conversione e quindi il risultato restituito viene confrontato con 0. Nel secondo caso viene chiamato l'operatore ==.


3
+1 per aver menzionato che un operatore di conversione potrebbe essere rilevante quanto un operatore ==.
Tony Delroy

11

In caso di dubbio, valutalo e impara la verità.


2
cosa c'è di sbagliato nel benchmarking? a volte la pratica ti dice più della teoria
Elzo Valugi

1
Questa è la risposta che stavo cercando quando ho iniziato a leggere questo thread. Sembra che la teoria sia più attraente della pratica, guardando le risposte e i voti positivi :)
Samuel Rivas,

come potrebbe fare un benchmark durante il colloquio? inoltre penso che l'intervistatore non sappia nemmeno cosa significhi benchmarking, quindi potrebbe essersi offeso.
IAdapter

La risposta giusta alla domanda (IMO) è "Dipende più o meno dal compilatore e dal resto del programma. Scriverei un benchmark e lo testerei in 5 minuti"
Samuel Rivas

7

Dovrebbero essere esattamente gli stessi in termini di velocità.

Si noti tuttavia che alcune persone usano per mettere la costante a sinistra nei confronti di uguaglianza (i cosiddetti "condizionali di Yoda") per evitare tutti gli errori che possono sorgere se si scrive =(operatore di assegnazione) invece di ==(operatore di confronto di uguaglianza); poiché l'assegnazione a un letterale innesca un errore di compilazione, questo tipo di errore viene evitato.

if(flag=0) // <--- typo: = instead of ==; flag is now set to 0
{
    // this is never executed
}

if(0=flag) // <--- compiler error, cannot assign value to literal
{

}

D'altra parte, la maggior parte delle persone trova i "condizionali di Yoda" strani e fastidiosi, soprattutto perché la classe di errori che prevengono può essere individuata anche utilizzando adeguati avvisi del compilatore.

if(flag=0) // <--- warning: assignment in conditional expression
{

}

Grazie per l'eco. Nota, tuttavia, che PHP, ad esempio, non avviserà in caso di assegnazioni in condizionali.
Linus Kleen

5

Come altri hanno già detto, non c'è differenza.

0deve essere valutato. flagdeve essere valutato. Questo processo richiede lo stesso tempo, indipendentemente da quale lato siano posizionati.

La risposta giusta sarebbe: hanno entrambe la stessa velocità.

Anche le espressioni if(flag==0)e if(0==flag)hanno la stessa quantità di caratteri! Se uno di essi è stato scritto come if(flag== 0), il compilatore avrebbe uno spazio in più da analizzare, quindi avresti una ragione legittima per indicare il tempo di compilazione.

Ma poiché non esiste una cosa del genere, non c'è assolutamente alcun motivo per cui uno dovrebbe essere più veloce di un altro. Se c'è un motivo, il compilatore sta facendo cose molto, molto strane per il codice generato ...


5

Quale è veloce dipende dalla versione di == che stai usando. Ecco uno snippet che utilizza 2 possibili implementazioni di == e, a seconda che si scelga di chiamare x == 0 o 0 == x, viene selezionata una delle 2.

Se stai usando solo un POD, questo non dovrebbe avere importanza quando si tratta di velocità.

#include <iostream>
using namespace std;

class x { 
  public:
  bool operator==(int x) { cout << "hello\n"; return 0; }
  friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; } 
};

int main()
{ 
   x x1;
   //int m = 0;
   int k = (x1 == 0);
   int j = (0 == x1);
}

5

Bene, sono completamente d'accordo con tutto ciò che è stato detto nei commenti all'OP, per il bene dell'esercizio:

Se il compilatore non è abbastanza intelligente (anzi non dovresti usarlo) o l'ottimizzazione è disabilitata, x == 0potrebbe essere compilato in jump if zeroun'istruzione assembly nativa , mentre 0 == xpotrebbe essere un confronto più generico (e costoso) di valori numerici.

Tuttavia, non mi piacerebbe lavorare per un capo che pensa in questi termini ...


4

Sicuramente nessuna differenza in termini di velocità di esecuzione. La condizione deve essere valutata in entrambi i casi allo stesso modo.


3

Penso che la risposta migliore sia "in che lingua è questo esempio"?

La domanda non specificava la lingua ed è contrassegnata sia con "C" che con "C ++". Una risposta precisa necessita di maggiori informazioni.

È una pessima domanda di programmazione, ma potrebbe essere una buona cosa nel reparto ambiguo "diamo all'intervistato abbastanza corda per impiccarsi o costruire un'altalena". Il problema con questo tipo di domande è che di solito vengono scritte e tramandate da intervistatore a intervistatore fino a quando non arrivano a persone che non le capiscono davvero da tutte le angolazioni.


3

Costruisci due semplici programmi usando i modi suggeriti.

Assembla i codici. Guarda l'assemblea e puoi giudicare, ma dubito che ci sia una differenza!

Le interviste stanno diventando più basse che mai.


2

Per inciso (in realtà penso che qualsiasi compilatore decente renderà discutibile questa domanda, dal momento che la ottimizzerà) usando 0 == flag over flag == 0 impedisce l'errore di battitura in cui dimentichi uno dei = (cioè se digiti accidentalmente flag = 0 verrà compilato, ma 0 = flag no), che penso sia un errore che tutti hanno fatto in un punto o in un altro ...


0

Se c'era una differenza, cosa impedisce al compilatore di scegliere il più veloce una volta? Quindi, logicamente, non può esserci alcuna differenza. Probabilmente questo è ciò che si aspetta l'intervistatore. In realtà è una domanda brillante.

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.