Che è più veloce: while (1) o while (2)?


587

Questa è stata una domanda di intervista posta da un senior manager.

Qual è più veloce?

while(1) {
    // Some code
}

o

while(2) {
    //Some code
}

Ho detto che entrambi hanno la stessa velocità di esecuzione, in quanto l'espressione interna whiledovrebbe infine valutare trueo false. In questo caso, entrambi valutano truee non vi sono istruzioni condizionali aggiuntive all'interno della whilecondizione. Quindi, entrambi avranno la stessa velocità di esecuzione e preferisco while (1).

Ma l'intervistatore ha detto con sicurezza: "Controlla le tue basi. while(1)È più veloce di while(2)". (Non stava testando la mia fiducia)

È vero?

Vedi anche: "for (;;)" è più veloce di "while (TRUE)"? In caso contrario, perché le persone lo usano?


202
Un compilatore decente ottimizzerà entrambi i moduli a nulla.

64
Nella build ottimizzata ogni volta che (n), n! = 0 o for (;;) verranno tradotti in loop infinito di Assembly con etichetta all'inizio e goto alla fine. Esattamente lo stesso codice, le stesse prestazioni.
Alex F,

60
Non sorprende, l'ottimizzazione dello stock porta 0x100000f90: jmp 0x100000f90(l'indirizzo varia, ovviamente) per entrambi i frammenti. L'intervistatore probabilmente ha fatto un test di registro contro un semplice salto contrassegnato. Sia la domanda, sia la loro supposizione, è zoppa.
WhozCraig,

50
Questa domanda dell'intervistatore rientra negli stessi auspici di dilbert.com/strips/comic/1995-11-17 - incontrerai qualcuno che crede sinceramente in ciò che sta dicendo, indipendentemente dal quoziente di stupidità nella sua affermazione. Basta scegliere tra i seguenti: un respiro profondo, una parolaccia, una risata, un pianto, una combinazione di quanto sopra :)
GMasucci,

3
@Mike W: ci si può chiedere che cosa dovrebbe fare un compilatore: tradurre in un'istruzione Halt o considerare che il ciclo esce dopo un tempo infinito e ottimizzare il ritardo infinito?
Yves Daoust,

Risposte:


681

Entrambi i loop sono infiniti, ma possiamo vedere quale richiede più istruzioni / risorse per iterazione.

Usando gcc, ho compilato i due seguenti programmi da assemblare a vari livelli di ottimizzazione:

int main(void) {
    while(1) {}
    return 0;
}


int main(void) {
    while(2) {}
    return 0;
}

Anche senza ottimizzazioni ( -O0), l'assembly generato era identico per entrambi i programmi .Pertanto, non vi è alcuna differenza di velocità tra i due loop.

Per riferimento, ecco l'assieme generato (usando gcc main.c -S -masm=intelcon un flag di ottimizzazione):

Con -O0:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    push    rbp
    .seh_pushreg    rbp
    mov rbp, rsp
    .seh_setframe   rbp, 0
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

Con -O1:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

Con -O2e -O3(stesso output):

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

In effetti, l'assemblaggio generato per il loop è identico per ogni livello di ottimizzazione:

 .L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

I bit importanti sono:

.L2:
    jmp .L2

Non riesco a leggere molto bene l'assemblaggio, ma questo è ovviamente un ciclo incondizionato. L' jmpistruzione reimposta incondizionatamente il programma .L2sull'etichetta senza nemmeno confrontare un valore con vero, e ovviamente lo fa immediatamente di nuovo fino a quando il programma non è in qualche modo terminato. Ciò corrisponde direttamente al codice C / C ++:

L2:
    goto L2;

Modificare:

È interessante notare che, anche senza ottimizzazioni , i loop seguenti hanno prodotto tutti lo stesso output esatto (incondizionato jmp) nell'assembly:

while(42) {}

while(1==1) {}

while(2==2) {}

while(4<7) {}

while(3==3 && 4==4) {}

while(8-9 < 0) {}

while(4.3 * 3e4 >= 2 << 6) {}

while(-0.1 + 02) {}

E anche con mio stupore:

#include<math.h>

while(sqrt(7)) {}

while(hypot(3,4)) {}

Le cose diventano un po 'più interessanti con le funzioni definite dall'utente:

int x(void) {
    return 1;
}

while(x()) {}


#include<math.h>

double x(void) {
    return sqrt(7);
}

while(x()) {}

A -O0, questi due esempi in realtà chiamano xed eseguono un confronto per ogni iterazione.

Primo esempio (ritorno 1):

.L4:
    call    x
    testl   %eax, %eax
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

Secondo esempio (ritorno sqrt(7)):

.L4:
    call    x
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jp  .L4
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

Tuttavia, in -O1e sopra, entrambi producono lo stesso assieme degli esempi precedenti (un jmpritorno incondizionato all'etichetta precedente).

TL; DR

Sotto GCC, i diversi loop vengono compilati in assembly identici. Il compilatore valuta i valori costanti e non si preoccupa di eseguire alcun confronto effettivo.

La morale della storia è:

  • Esiste un livello di traduzione tra il codice sorgente C ++ e le istruzioni della CPU e questo livello ha importanti implicazioni per le prestazioni.
  • Pertanto, le prestazioni non possono essere valutate solo guardando il codice sorgente.
  • Il compilatore dovrebbe essere abbastanza intelligente da ottimizzare casi così banali. I programmatori non dovrebbero perdere tempo a pensarci nella stragrande maggioranza dei casi.

196
Forse l'intervistatore non stava usando gcc
MM,

106
@Matt McNabb Questo è un buon punto, ma se l'intervistatore si basava su ottimizzazioni specifiche del compilatore, allora devono essere molto espliciti su questo nella loro domanda, e devono accettare la risposta "non c'è differenza" come corretta per alcuni (la maggior parte?) compilatori.
Jonathan Hartley,

109
Per rimuovere ogni dubbio, l'ho provato in clang 3.4.2 ed entrambi i loop producono lo stesso assieme ad ogni -Olivello.
Martin Tournoij,

16
Non lo trovo affatto sorprendente poiché tutto ciò che hai inserito nella sezione condizionale dei loop sono costanti di tempo di compilazione. In quanto tale, sospetterei che un compilatore vedrebbe che i loop saranno sempre veri o falsi e rispetti semplicemente jmpall'inizio o rimuoveranno completamente il loop.
Sherrellbc,

10
@hippietrail Non vedo come il contenuto (o la mancanza del presente) del loop possa effettuare queste ottimizzazioni (tranne la possibilità di eventuali breakdichiarazioni), ma l'ho appena provato comunque e no, anche con il codice all'interno del loop il salto è assoluto e incondizionato per entrambi while(1)e while(2). Sentiti libero di testare gli altri se sei veramente preoccupato.
avvicina

282

Sì, while(1)è molto più veloce di while(2), per un essere umano da leggere! Se vedo while(1)in una base di codice sconosciuta, so immediatamente cosa intendeva l'autore e i miei occhi possono continuare alla riga successiva.

Se vedo while(2), probabilmente mi fermerò nelle mie tracce e cercherò di capire perché l'autore non ha scritto while(1). Il dito dell'autore è scivolato sulla tastiera? I manutentori di questa base di codice usano while(n)un meccanismo di commento oscuro per rendere i loop diversi? È una soluzione approssimativa per un avviso spurio in qualche strumento di analisi statica rotto? O è un indizio che sto leggendo il codice generato? È un insetto derivante da una sconsiderata ricerca e sostituzione, da una cattiva unione o da un raggio cosmico? Forse questa riga di codice dovrebbe fare qualcosa di drammaticamente diverso. Forse avrebbe dovuto leggere while(w)o avrebbe richiesto una frazione di secondo!while(x2) . Farei meglio a trovare l'autore nella cronologia del file e inviare loro un'e-mail "WTF" ... e ora ho rotto il mio contesto mentale.while(2)while(1)

Sto esagerando, ma solo un po '. La leggibilità del codice è davvero importante. E vale la pena menzionarlo in un'intervista!


I miei pensieri esattamente, e precisamente perché while (1) è più veloce lol Anche se penso che sia stato semplicemente il vecchio caso di un manager che cerca di convincere se stesso che conosce il codice quando non lo fa, lo abbiamo visto tutti, tutti vogliono essere un jedi.
ekerner,

8
Assolutamente, questa NON è affatto un'esagerazione. Sicuramente il svn-annotate o git blame(o qualunque altra cosa) verrà usato qui, e in generale ci vogliono minuti per caricare una cronologia delle responsabilità dei file. Poi basta finalmente decidere "ah ho capito, l'autore ha scritto questa linea quando si è fuori fresco di liceo", appena perso 10 minuti ...
v.oddou

11
Votato perché sono stufo delle convenzioni. Lo sviluppatore ha questa scortese opportunità di scrivere un numero che gli piace, quindi arriva qualcuno noioso che si preoccupa del perché non lo è 1.
Utkan Gezer,

1
Sottovalutato a causa della retorica. La lettura while (1) è esattamente la stessa velocità di while (2).
hdante,

4
Ho attraversato esattamente lo stesso processo mentale descritto in questa risposta un minuto fa quando ho visto while(2)il codice (fino a considerare "I manutentori di questa base di codice usano mentre (n) come un oscuro meccanismo di commento per rendere i loop diversi?" ). Quindi no, non stai esagerando!
Hisham HM,

151

Le risposte esistenti che mostrano il codice generato da un particolare compilatore per un determinato target con un particolare set di opzioni non rispondono completamente alla domanda - a meno che la domanda non sia stata posta in quel contesto specifico ("Che è più veloce usando gcc 4.7.2 per x86_64 con opzioni predefinite? ", ad esempio).

Per quanto riguarda la definizione del linguaggio, nella macchina astratta while (1) valuta la costante intera 1e while (2)valuta la costante intera 2; in entrambi i casi il risultato viene confrontato per l'uguaglianza a zero. Lo standard linguistico non dice assolutamente nulla sulla prestazione relativa dei due costrutti.

Posso immaginare che un compilatore estremamente ingenuo possa generare un codice macchina diverso per i due moduli, almeno quando compilato senza richiedere l'ottimizzazione.

D'altra parte, i compilatori C devono assolutamente valutare alcune espressioni costanti al momento della compilazione, quando compaiono in contesti che richiedono un'espressione costante. Ad esempio, questo:

int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}

richiede una diagnosi; un compilatore pigro non ha la possibilità di rinviare la valutazione 2+2fino al tempo di esecuzione. Dato che un compilatore deve avere la capacità di valutare espressioni costanti al momento della compilazione, non c'è motivo di non sfruttare questa funzionalità anche quando non è necessario.

Lo standard C ( N1570 6.8.5p4) dice questo

Un'istruzione di iterazione provoca l' esecuzione ripetuta di un'istruzione chiamata body loop fino a quando l'espressione di controllo non è uguale a 0.

Quindi le espressioni costanti rilevanti sono 1 == 0e 2 == 0, entrambe le quali valutano il intvalore 0. (Questi confronti sono impliciti nella semantica diwhile ciclo; non esistono come espressioni C effettive).

Un compilatore perversamente ingenuo potrebbe generare un codice diverso per i due costrutti. Ad esempio, per il primo potrebbe generare un ciclo infinito incondizionato (trattando 1come un caso speciale), e per il secondo potrebbe generare un confronto di runtime esplicito equivalente a 2 != 0. Ma non ho mai incontrato un compilatore C che si comporterebbe effettivamente in quel modo, e dubito seriamente che esista un compilatore del genere.

La maggior parte dei compilatori (sono tentato di dire che tutti i compilatori di qualità di produzione) hanno opzioni per richiedere ulteriori ottimizzazioni. Con tale opzione, è ancora meno probabile che qualsiasi compilatore generi codice diverso per i due moduli.

Se il compilatore genera un codice diverso per i due costrutti, verificare innanzitutto se le diverse sequenze di codice hanno effettivamente prestazioni diverse. In tal caso, prova a compilare nuovamente con un'opzione di ottimizzazione (se disponibile). Se differiscono ancora, inviare una segnalazione di bug al fornitore del compilatore. Non è (necessariamente) un bug nel senso di una mancata conformità allo standard C, ma è quasi certamente un problema che dovrebbe essere corretto.

In conclusione: while (1)e while(2) quasi sicuramente hanno le stesse prestazioni. Hanno esattamente la stessa semantica e non c'è motivo per cui un compilatore non generi codice identico.

E sebbene sia perfettamente legale per un compilatore generare codice più veloce while(1)rispetto a quello per while(2), è ugualmente legale per un compilatore generare codice più veloce while(1)rispetto a un'altra occorrenza while(1)nello stesso programma.

(C'è un'altra domanda implicita in quella che hai posto: come gestisci un intervistatore che insiste su un punto tecnico errato. Sarebbe probabilmente una buona domanda per il sito di Workplace ).


8
"In questo caso, le espressioni costanti (implicite) rilevanti sono 1! = 0 e 2! = 0, entrambe valutano il valore int 1" ... Questo è eccessivamente complicato e inaccurato. Lo standard dice semplicemente che l'espressione di controllo di whiledeve essere di tipo scalare e il corpo del loop viene ripetuto fino a quando l'espressione non è uguale a 0. Non dice che esiste un implicito expr != 0che viene valutato ... che richiederebbe il risultato di che - 0 o 1 - a sua volta viene confrontato con 0, all'infinito. No, l'espressione viene confrontata con 0, ma tale confronto non produce un valore. PS Ho votato.
Jim Balter,

3
@JimBalter: vedo il tuo punto e aggiornerò la mia risposta per affrontarlo. Ciò che intendevo, tuttavia, era che la formulazione dello standard "... fino a quando l'espressione di controllo non si confronta uguale a 0" implica una valutazione <expr> == 0; Questo è quello che "mette a confronto pari a 0" significa in C. Questo confronto fa parte della semantica del whileciclo. Non vi sono implicazioni, né nella norma né nella mia risposta, a cui il risultato debba essere 0nuovamente confrontato . (Avrei dovuto scrivere ==piuttosto che !=.) Ma quella parte della mia risposta non era chiara e la aggiornerò.
Keith Thompson,

1
"" confronta uguale a 0 "significa in C" - ma quel linguaggio è nello standard , non in C ... l'implementazione fa il confronto, non genera un confronto in linguaggio C. Scrivi "entrambi i quali valutano al valore int 1" - ma tale valutazione non si verifica mai. Se si guarda il codice generato per while (2), while (pointer), while (9.67), dal più ingenuo, il compilatore non ottimizzata, non sarà possibile vedere alcun codice generato che i rendimenti 0o 1. "Avrei dovuto scrivere == anziché! =" - no, non avrebbe avuto senso.
Jim Balter,

2
@JimBalter: Hmmm. Non intendo suggerire che il "confronto uguale a 0" implica l'esistenza di ... == 0un'espressione C. Il mio punto è che entrambi i "confronti uguali a 0" richiesti dalla descrizione dei whileloop dello standard e x == 0un'espressione esplicita implicano logicamente la stessa operazione. E penso che un compilatore C dolorosamente ingenuo potrebbe generare codice che genera un intvalore 0o 1per qualsiasi whileciclo, anche se non credo che nessun compilatore reale sia così ingenuo.
Keith Thompson,

12
Nota: questa è già una domanda di The Workplace : workplace.stackexchange.com/questions/4314/…
Joe

141

Apetta un minuto. L'intervistatore, assomigliava a questo ragazzo?

inserisci qui la descrizione dell'immagine

È abbastanza grave che l'intervistatore stesso abbia fallito questa intervista, e se altri programmatori di questa società avessero "superato" questo test?

No. Valutare le dichiarazioni 1 == 0e 2 == 0 dovrebbe essere altrettanto veloce. Potremmo immaginare cattive implementazioni del compilatore in cui una potrebbe essere più veloce dell'altra. Ma non c'è una buona ragione per cui uno dovrebbe essere più veloce dell'altro.

Anche se ci sono alcune circostanze oscure in cui l'affermazione sarebbe vera, i programmatori non dovrebbero essere valutati in base alla conoscenza di curiosità oscure (e in questo caso inquietanti). Non preoccuparti per questa intervista, la mossa migliore qui è andarsene.

Disclaimer: questo NON è un cartone originale Dilbert. Questo è semplicemente un mashup .


6
Sarebbe divertente, se dovesse contenere anche una risposta alla domanda.
Cody Grey

no ma davvero, possiamo tutti immaginare abbastanza facilmente che tutti i compilatori scritti da aziende serie produrranno un codice ragionevole. prendiamo il "caso non ottimizzato" / O0, forse finirà come ha pubblicato l'anatolyg. Quindi è una questione di CPU, l' cmpoperando funzionerà in meno cicli confrontando da 1 a 0 rispetto a 2 a 0? quanti cicli ci vogliono per eseguire cmp in generale? è variabile in base ai modelli di bit? sono schemi di bit più "complessi" che rallentano cmp? Non lo so personalmente. potresti immaginare un'implementazione super idiota che controlla bit per bit dal rango 0 ad n (es. n = 31).
v

5
Anche questo è il mio punto: l' cmpoperando dovrebbe essere altrettanto veloce per 1 e 200. Probabilmente potremmo immaginare implementazioni idiote in cui non è così. Ma possiamo immaginare un'implementazione non idiota dove while(1)è più veloce di while(200)? Allo stesso modo, se in un'epoca preistorica, l'unica implementazione disponibile fosse idiota in quel modo, dovremmo preoccuparcene oggi? Non credo, questo è un discorso da capo con i capelli a punta, e un vero gioiello!
Janos,

@ v.ouddou "l'operando cmp verrà eseguito in meno cicli confrontando 1 a 0 rispetto a 2 a 0" - No. Dovresti imparare cos'è un ciclo. "Non lo so personalmente. Potresti immaginare un'implementazione super idiota che controlla un po 'alla volta dal rango 0 a n" - o viceversa, facendo ancora dell'intervistatore un idiota senza sensi. E perché preoccuparsi di controllare a poco a poco? L'implementazione potrebbe essere un uomo in una scatola che decide di fare una pausa pranzo durante la valutazione del programma.
Jim Balter,

79

La tua spiegazione è corretta Questa sembra essere una domanda che mette alla prova la tua autostima oltre alle conoscenze tecniche.

A proposito, se hai risposto

Entrambi i pezzi di codice sono ugualmente veloci, perché entrambi richiedono un tempo infinito per il completamento

l'intervistatore direbbe

Ma while (1)può fare più iterazioni al secondo; puoi spiegare perché? (questa è una sciocchezza; prova di nuovo la tua sicurezza)

Quindi rispondendo come hai fatto, hai risparmiato un po 'di tempo che altrimenti perderesti nel discutere questa brutta domanda.


Ecco un esempio di codice generato dal compilatore sul mio sistema (MS Visual Studio 2012), con le ottimizzazioni disattivate:

yyy:
    xor eax, eax
    cmp eax, 1     (or 2, depending on your code)
    je xxx
    jmp yyy
xxx:
    ...

Con le ottimizzazioni attivate:

xxx:
    jmp xxx

Quindi il codice generato è esattamente lo stesso, almeno con un compilatore di ottimizzazione.


28
Questo codice è davvero ciò che il compilatore genera sul mio sistema. Non l'ho inventato.
Anatolyg,

10
icepack "L'operando per un po 'è di tipo booleano" - completamente senza senso. Sei l'intervistatore? Ti suggerisco di acquisire familiarità con il linguaggio C e il suo standard prima di presentare tali affermazioni.
Jim Balter,

29
"Non l'ho inventato." - Per favore, non prestare attenzione a Icepack, che sta parlando senza senso. C non ha un tipo booleano (ha _Bool in stdbool.h, ma non è lo stesso, e la semantica di whilelungo lo ha preceduto) e l'operando di whilenon è booleano o _Bool o qualsiasi altro tipo specifico. L'operando di whilepuò essere qualsiasi espressione ... mentre while si interrompe su 0 e continua su non-0.
Jim Balter,

36
"Entrambi i pezzi di codice sono ugualmente veloci, perché entrambi richiedono un tempo infinito per il completamento" mi ha fatto pensare a qualcosa di interessante. L'unico modo per terminare un ciclo infinito sarebbe che un po 'venisse capovolto da una particella carica o dall'errore hardware: cambiando l'istruzione da while (00000001) {}a while (00000000) {}. Più bit veri hai, meno possibilità hai che il valore passi a falso. Purtroppo, 2 ha anche solo un bit vero. 3, tuttavia, durerebbe molto più a lungo. Questo vale anche solo per un compilatore che non ottimizza sempre questa opzione (VC ++).
Jonathan Dickinson,

8
@ mr5 no. Affinché un po 'di vibrazione si traduca in qualcosa del genere, stai parlando di tempi di esecuzione in decine di migliaia di anni. Solo un esperimento mentale . Se provieni da una razza immortale, potresti voler usare -1 per evitare che il bit-flipping influenzi il tuo programma.
Jonathan Dickinson,

60

La spiegazione più probabile per la domanda è che l'intervistatore pensa che il processore controlli i singoli bit dei numeri, uno per uno, fino a raggiungere un valore diverso da zero:

1 = 00000001
2 = 00000010

Se "è zero?" l'algoritmo inizia dal lato destro del numero e deve controllare ogni bit fino a quando non raggiunge un bit diverso da zero, il while(1) { }ciclo dovrebbe controllare il doppio dei bit per iterazione rispetto al while(2) { }ciclo.

Ciò richiede un modello mentale molto sbagliato su come funzionano i computer, ma ha una sua logica interna. Un modo per verificare sarebbe quello di chiedere se while(-1) { }o while(3) { }sarebbe altrettanto veloce, o se while(32) { }sarebbe ancora più lento .


39
Supponevo che la cattiva comprensione dell'intervistatore sarebbe più simile a "2 è un int che deve essere convertito in un valore booleano per essere utilizzato in un'espressione condizionale, mentre 1 è già booleano".
Russell Borogove,

7
E se l'algoritmo di confronto inizia da sinistra, è il contrario.
Paŭlo Ebermann,

1
+1, è esattamente quello che pensavo. si potrebbe perfettamente immaginare qualcuno che creda fondamentalmente che l' cmpalgoritmo di una CPU è un controllo di bit lineare con uscita anticipata del loop sulla prima differenza.
v

1
@PeterCordes "Non ho mai sentito nessuno (tranne te) sostenere che l' -O0output di gcc o clang non è abbastanza letterale." Non ho mai detto una cosa del genere e non avevo in mente gcc o clang. Mi stai leggendo male. Non credo che ciò che MSVC produce sia divertente.
blubberdiblub,

1
@PeterCordes Ho sbagliato, in MSVC un punto di interruzione while(1)non si interrompe prima del ciclo, ma sull'espressione. Ma collassa ancora all'interno del ciclo quando ottimizza l'espressione. Prendi questo codice con molte pause. In modalità di debug non ottimizzata si ottiene questo . Durante l'ottimizzazione, molti punti di interruzione cadono insieme o addirittura si riversano nella funzione successiva (l'IDE mostra i punti di interruzione del risultato durante il debug) - disassemblaggio corrispondente .
Blubberdiblub

32

Naturalmente non conosco le vere intenzioni di questo manager, ma propongo una visione completamente diversa: quando assume un nuovo membro in una squadra, è utile sapere come reagisce alle situazioni di conflitto.

Ti hanno portato in conflitto. Se questo è vero, sono intelligenti e la domanda era buona. Per alcuni settori, come quello bancario, la pubblicazione del problema su Stack Overflow potrebbe essere un motivo di rifiuto.

Ma ovviamente non lo so, propongo solo un'opzione.


È davvero eccellente, ma while (2) vs while (1) è ovviamente preso dai fumetti di Dilbert. NON PUO 'essere inventato da qualcuno nella sua mente giusta (come fa qualcuno a pensare mentre (2) come una cosa possibile da scrivere comunque?). Se la tua ipotesi fosse vera, sicuramente daresti un problema così unico che puoi cercarlo su Google. Come "is while (0xf00b442) più lento di while (1)", in che modo la banca troverebbe altrimenti la domanda dell'inerviewee? pensi che siano la NSA e abbiano accesso al keycore?
v

25

Penso che l'indizio si trovi in ​​"chiesto da un senior manager". Questa persona ovviamente ha smesso di programmare quando è diventato manager e poi ci sono voluti diversi anni per diventare senior manager. Non ho mai perso interesse per la programmazione, ma non ho mai scritto una riga da quei giorni. Quindi il suo riferimento non è "nessun compilatore decente là fuori" come menzionano alcune risposte, ma "il compilatore con cui questa persona ha lavorato 20-30 anni fa".

A quel tempo, i programmatori impiegavano una considerevole percentuale del loro tempo a provare vari metodi per rendere il loro codice più veloce ed efficiente poiché il tempo della CPU del "minicomputer centrale" era così prezioso. Come hanno fatto le persone a scrivere compilatori. Immagino che il solo ed unico compilatore che la sua azienda ha reso disponibile in quel momento ottimizzato sulla base di "dichiarazioni frequentemente incontrate che possono essere ottimizzate" e abbia preso un po 'di scorciatoia quando ha incontrato un po' (1) e valutato tutto altro, incluso un po '(2). Avere avuto un'esperienza simile potrebbe spiegare la sua posizione e la sua fiducia in essa.

L'approccio migliore per farti assumere è probabilmente quello che consente al senior manager di lasciarti trasportare e di insegnarti 2-3 minuti sui "bei vecchi tempi della programmazione" prima che TU lo guidi senza problemi verso il prossimo argomento dell'intervista. (Un buon tempismo è importante qui - troppo veloce e stai interrompendo la storia - troppo lento e sei etichettato come qualcuno con una concentrazione insufficiente). Digli alla fine del colloquio che saresti molto interessato a saperne di più su questo argomento.


19

Avresti dovuto chiedergli come è arrivato a quella conclusione. Sotto qualsiasi compilatore decente là fuori, i due compilano le stesse istruzioni asm. Quindi, avrebbe dovuto dirti anche il compilatore per iniziare. E anche così, dovresti conoscere molto bene il compilatore e la piattaforma per fare anche un'ipotesi educata teorica. E alla fine, non ha molta importanza in pratica, poiché ci sono altri fattori esterni come la frammentazione della memoria o il carico del sistema che influenzeranno il ciclo più di questo dettaglio.


14
@GKFX Se hai dato la tua risposta e ti hanno detto che hai torto, non c'è motivo per cui non puoi chiedere loro di spiegare il perché. Se Anatolyg è corretto ed è una prova della tua autostima, allora dovresti spiegare perché hai risposto come hai fatto e chiedere loro lo stesso.
P.Turpie,

Intendevo la prima cosa che dici a loro. Non può essere "Perché x è più veloce?" "Non lo so; perché x è più veloce?". Ovviamente, dopo aver risposto correttamente, puoi quindi chiedere.
GKFX,

18

Per il bene di questa domanda, dovrei aggiungere che ricordo Doug Gwyn del Comitato C che scrisse che alcuni primi compilatori C senza il pass per l'ottimizzatore avrebbero generato un test in assembly per while(1)(rispetto al for(;;)quale non l'avrebbero).

Risponderei all'intervistatore dando questa nota storica e poi direi che anche se sarei molto sorpreso da qualsiasi compilatore, questo potrebbe avere:

  • senza ottimizzatore passa il compilatore genera un test per entrambi while(1)ewhile(2)
  • con l'ottimizzatore passa al compilatore viene ordinato di ottimizzare (con un salto incondizionato) tutto while(1)perché sono considerati idiomatici. Ciò lascerebbe il while(2)test con una prova e quindi fa una differenza di prestazioni tra i due.

Ovviamente aggiungerei all'intervistatore che non considerare while(1)e while(2)lo stesso costrutto è un segno di ottimizzazione di bassa qualità in quanto si tratta di costrutti equivalenti.


10

Un'altra idea su una domanda del genere sarebbe vedere se hai il coraggio di dire al tuo manager che ha torto! E con che dolcezza puoi comunicarlo.

Il mio primo istinto sarebbe stato quello di generare l'output dell'assemblaggio per mostrare al gestore che qualsiasi compilatore decente dovrebbe occuparsene, e se non lo fa, invierai la prossima patch :)


8

Per vedere così tante persone approfondire questo problema, mostra esattamente perché questo potrebbe benissimo essere un test per vedere quanto velocemente vuoi micro-ottimizzare cose.

La mia risposta sarebbe; non importa molto, mi concentro piuttosto sul problema aziendale che stiamo risolvendo. Dopotutto, è per questo che sarò pagato.

Inoltre, opterei per while(1) {} perché è più comune e gli altri compagni di squadra non avrebbero bisogno di perdere tempo per capire perché qualcuno dovrebbe scegliere un numero maggiore di 1.

Ora vai a scrivere un po 'di codice. ;-)


1
A meno che non vi venga pagato per ottimizzare un codice in tempo reale per il quale è necessario radere solo 1 o 2 millisecondi per adattarsi alle esigenze del tempo di esecuzione. Ovviamente questo è un lavoro per l'ottimizzatore, alcuni direbbero - cioè se hai un ottimizzatore per la tua architettura.
Neowizard,

6

Se sei così preoccupato per l'ottimizzazione, dovresti usare

for (;;)

perché questo non ha test. (modalità cinica)


2
Ecco perché è saggio da usare while(2), lascia spazio all'ottimizzazione, quindi passa a for (;;)quando sei pronto per un aumento delle prestazioni.
Groo

5

Mi sembra che questa sia una di quelle domande sul colloquio comportamentale mascherate da una domanda tecnica. Alcune aziende lo fanno - faranno una domanda tecnica che dovrebbe essere abbastanza facile per qualsiasi programmatore competente rispondere, ma quando l'intervistato dà la risposta corretta, l'intervistatore dirà loro che si sbagliano.

La compagnia vuole vedere come reagirai in questa situazione. Ti siedi lì in silenzio e non speri che la tua risposta sia corretta, a causa del dubbio o della paura di turbare l'intervistatore? O sei disposto a sfidare una persona in autorità che sai essere sbagliata? Vogliono vedere se sei disposto a difendere le tue convinzioni e se riesci a farlo in modo rispettoso e rispettoso.


3

Programmavo il codice C e Assembly quando questo tipo di assurdità avrebbe potuto fare la differenza. Quando ha fatto la differenza, l'abbiamo scritto in Assemblea.

Se mi avessero fatto questa domanda avrei ripetuto la famosa citazione del 1974 di Donald Knuth sull'ottimizzazione prematura e avrei camminato se l'intervistatore non avesse riso e proseguito.


1
Dato che ha anche detto "Nelle discipline ingegneristiche consolidate un miglioramento del 12%, facilmente ottenibile, non è mai considerato marginale e credo che lo stesso punto di vista dovrebbe prevalere nell'ingegneria del software", penso che camminare sia ingiustificato.
Alice,

3

Forse l'intervistatore ha posto intenzionalmente una domanda così stupida e ha voluto che tu facessi 3 punti:

  1. Ragionamento di base.Entrambi i loop sono infiniti, è difficile parlare di prestazioni.
  2. Conoscenza dei livelli di ottimizzazione. Voleva sentirti se lasci che il compilatore esegua l'ottimizzazione per te, ottimizzerebbe la condizione, specialmente se il blocco non fosse vuoto.
  3. Conoscenza dell'architettura a microprocessore. La maggior parte delle architetture ha un'istruzione CPU speciale per il confronto con 0 (anche se non necessariamente più veloce).

2

Ecco un problema: se si scrive effettivamente un programma e si misura la sua velocità, la velocità di entrambi i loop potrebbe essere diversa! Per un ragionevole confronto:

unsigned long i = 0;
while (1) { if (++i == 1000000000) break; }

unsigned long i = 0;
while (2) { if (++i == 1000000000) break; }

con l'aggiunta di un codice che stampa l'ora, alcuni effetti casuali come il posizionamento del loop all'interno di una o due righe della cache potrebbero fare la differenza. Per puro caso, un loop potrebbe trovarsi completamente all'interno di una riga della cache o all'inizio di una riga della cache, oppure potrebbe trovarsi a cavallo tra due righe della cache. Di conseguenza, qualunque cosa l'intervistatore sostenga sia la più veloce potrebbe effettivamente essere la più veloce, per coincidenza.

Scenario peggiore: un compilatore ottimizzato non capisce cosa fa il loop, ma capisce che i valori prodotti quando viene eseguito il secondo loop sono gli stessi di quelli prodotti dal primo. E genera il codice completo per il primo ciclo, ma non per il secondo.


1
Il ragionamento "Linee cache" funzionerà solo su un chip che ha una cache estremamente piccola.
sharptooth,

10
Questo è a parte il punto. Le domande sulla velocità assumono "tutto il resto uguale".
Jim Balter,

6
@JimBalter: questa non era una questione di velocità, era una domanda su una discussione con un intervistatore. E la possibilità che fare un test effettivo possa dimostrare che l'intervistatore è "giusto" - per ragioni che non hanno nulla a che fare con la sua argomentazione ma sono pura coincidenza. Il che ti metterebbe in una situazione imbarazzante se provassi a dimostrare che si sbagliava.
gnasher729,

1
@ gnasher729 A parte tutti gli argomenti logici, vale la pena esaminare il caso di coincidenza di cui parli. Ma credere ancora ciecamente a un caso del genere non è solo ingenuo ma stupido. Finché non spieghi cosa, come o perché ciò accada, questa risposta è inutile.
user568109

4
@ gnasher729 "Questa non era una questione di velocità" - Non discuterò di persone che impiegano tecniche retoriche disoneste.
Jim Balter,

2

Sono entrambi uguali - lo stesso.

Secondo le specifiche tutto ciò che non è 0 è considerato vero, quindi anche senza alcuna ottimizzazione, e un buon compilatore non genererà alcun codice per while (1) o while (2). Il compilatore genererebbe un semplice controllo per != 0.


@djechlin - perché entrambi richiedono 1 ciclo di CPU.
Zio King,

La costante del compilatore la piega, quindi non viene nemmeno valutata in fase di esecuzione.
PaulHK,

2

A giudicare dalla quantità di tempo e sforzo che le persone hanno speso per testare, provare e rispondere a questa domanda molto diretta, direi che entrambi sono stati fatti molto lentamente ponendo la domanda.

E così per passare ancora più tempo su di esso ...

"while (2)" è ridicolo perché,

"while (1)" e "while (true)" sono storicamente usati per creare un loop infinito che si aspetta che "break" venga chiamato in qualche fase all'interno del loop in base a una condizione che si verificherà sicuramente.

L '"1" è semplicemente lì per valutare sempre il vero e quindi, per dire "while (2)" è circa sciocco come dire "while (1 + 1 == 2)" che valuterà anche su true.

E se vuoi essere completamente sciocco, usa: -

while (1 + 5 - 2 - (1 * 3) == 0.5 - 4 + ((9 * 2) / 4)) {
    if (succeed())
        break;
}

Mi piacerebbe pensare che il tuo programmatore abbia fatto un errore di battitura che non ha influito sull'esecuzione del codice, ma se ha usato intenzionalmente il "2" solo per essere strano, allora licenziarlo prima che metta tutto strano nel tuo codice rendendolo difficile leggi e lavora con.


Il rollback effettuato ripristina la modifica da parte di un utente con una reputazione molto elevata. Queste persone di solito conoscono molto bene le politiche e sono qui per insegnartele. Trovo che l'ultima riga del tuo post non aggiunga nulla di utile al tuo post. Anche se avessi la battuta che mi manca ovviamente, lo considererei troppo chiacchierone, non vale il paragrafo in più.
Palec,

@Palec: grazie per il commento. Ho scoperto che lo stesso ragazzo ha modificato molti dei miei post, principalmente solo per rimuovere la firma. Quindi ho pensato che fosse solo un troll di reputazione, e quindi la sua reputazione. I forum online hanno esaurito la lingua - e le cortesie comuni - così tanto che non firmiamo più i nostri scritti? forse sono un po 'vecchia scuola ma sembra scortese omettere hellos e addii. Stiamo usando app, ma siamo ancora umani.
ekerner,

1
Su Stack Exchange , saluti, slogan, grazie ecc. Non sono i benvenuti . Lo stesso è menzionato nel centro assistenza , in particolare nel comportamento previsto . Nessuna parte di Stack Exchange , nemmeno Stack Overflow , è un forum: sono siti di domande e risposte. La modifica è una delle differenze. Inoltre i commenti dovrebbero servire solo a fornire un feedback sul post, non per la discussione ( Stack Overflow Chat ). E ci sono molti altri modi in cui le domande e risposte differiscono da un forum. Altre informazioni in merito nel Centro assistenza e in Overflow Meta Stack .
Palec,

Si noti che quando si dispone di oltre 2000 rep (modifica privilegio), non si ottiene più rep dalle modifiche. In caso di dubbi sul perché e la modifica di cui non sei d'accordo sono stati applicati al tuo post o vedi il comportamento scorretto di qualcuno, chiedi su Meta Stack Overflow . Se l'editore ha fatto la cosa sbagliata, potrebbero essere avvisati da una mod (forse non se ne sono accorti) o addirittura essere puniti in qualche modo (se il comportamento era intenzionalmente dannoso). In caso contrario, vengono visualizzati dei suggerimenti per spiegare perché la modifica / il comportamento sono corretti. La cronologia delle revisioni di rollback non necessaria e può portarti nella guerra di rollback. Eseguo il rollback solo quando sono davvero sicuro che la modifica non sia corretta.
Palec,

Infine su firma, saluto, ...: può sembrare scortese non includere queste formalità, ma aumenta il rapporto segnale rumore e nessuna informazione viene persa. Puoi scegliere qualsiasi nickname che desideri, questa è la tua firma. Nella maggior parte dei posti hai anche il tuo avatar.
Palec,

1

Dipende dal compilatore.

Se ottimizza il codice o se valuta 1 e 2 su true con lo stesso numero di istruzioni per un determinato set di istruzioni, la velocità di esecuzione sarà la stessa.

In casi reali sarà sempre altrettanto veloce, ma sarebbe possibile immaginare un compilatore particolare e un sistema particolare quando questo sarebbe valutato in modo diverso.

Voglio dire: questa non è davvero una domanda relativa al linguaggio (C).


0

Dato che le persone che cercano di rispondere a questa domanda desiderano il ciclo più veloce, avrei risposto che entrambi si stanno ugualmente compilando nello stesso codice assembly, come indicato nelle altre risposte. Tuttavia puoi suggerire all'intervistatore usando 'loop unrolling'; a do {} ciclo while anziché ciclo while.

Prudenza: è necessario assicurarsi che il ciclo venga eseguito almeno una volta .

Il loop dovrebbe avere una condizione di interruzione all'interno.

Anche per quel tipo di loop preferirei personalmente l'uso di do {} while (42) poiché qualsiasi numero intero, tranne 0, farebbe il lavoro.


Perché consiglierebbe di fare {} while loop? While (1) (o while (2)) fa la stessa cosa.
Catsunami,

fare mentre rimuove un salto in più quindi prestazioni migliori. Ovviamente nel caso in cui tu sappia che il loop verrebbe eseguito almeno una volta
Antonin GAVREL il

0

La risposta ovvia è: come pubblicato, entrambi i frammenti eseguiranno un ciclo infinito altrettanto occupato, il che rende il programma infinitamente lento .

Sebbene la ridefinizione delle parole chiave C come macro avrebbe tecnicamente un comportamento indefinito, è l'unico modo in cui posso pensare di rendere veloce uno dei frammenti di codice: puoi aggiungere questa riga sopra i 2 frammenti:

#define while(x) sleep(x);

sarà infatti while(1)due volte più veloce (o metà più lento) di while(2).


@MaxB: in effetti il ​​trucco con le macro deve essere ancora più contorto. Penso che la nuova versione dovrebbe funzionare, sebbene non sia garantita dallo standard C.
Chqrlie,

-4

L'unica ragione per cui riesco a pensare al motivo per cui while(2)sarebbe più lento è:

  1. Il codice ottimizza il loop su

    cmp eax, 2

  2. Quando si verifica la sottrazione si sta essenzialmente sottraendo

    un. 00000000 - 00000010 cmp eax, 2

    invece di

    b. 00000000 - 00000001 cmp eax, 1

cmpimposta solo flag e non imposta un risultato. Quindi sui bit meno significativi sappiamo se dobbiamo prendere in prestito o meno con b . Considerando che con un devi eseguire due sottrazioni prima di ottenere un prestito.


15
cmp prenderà 1 ciclo di CPU indipendentemente da entrambi i casi.
Zio King,

8
Quel codice sarebbe errato. Il codice corretto carica 2 o 1 nel registro eax, quindi confronta eax con 0.
gnasher729
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.