2012-02-04 9 views
5

Ieri ho avuto un'intervista. Lì mi hanno chiesto quando è avvenuto l'ottimizzazione del codice? Say,Quando si verifica l'ottimizzazione del codice?

int abc;//Global variable 
abc = 3; 
if(abc == 3) 
{ 
    printf("abc will be always 3"); 
} 
else 
{ 
    printf("This will never executed"); 
} 

Ora la domanda è quando l'ottimizzazione accade? A ... In fase di esecuzione B ... Al momento della compilazione. Ho risposto al momento della compilazione ... Per me ho pensato, il compilatore controlla la parola chiave volatile al momento della compilazione. Se la variabile non è dichiarata come volatile, ottimizza il codice. Ma quando il compilatore arriva a saperlo, questa variabile non sarà mai più di 3? Se è in fase di esecuzione, allora quando il compilatore viene a sapere che la variabile non sarà mai più di 3? Perché se la variabile verrà modificata dopo l'esecuzione di questa parte del codice. Per favore, cancella il mio dubbio

risposta

4

maggior ottimizzazione del codice non può avvenire in fase di esecuzione, almeno non come si vuol dire. Il codice non può cambiare durante l'esecuzione per riflettere un nuovo o diverso insieme di variabili, che farebbe solo un casino assoluto.

Ciò che può essere fatto in fase di esecuzione è la scelta del percorso migliore attraverso il codice, ma che deve essere fatto principalmente manualmente per creare percorsi separati, early-out, filiali e così via per consentire l'ottimizzazione.

Codice come questo, come scritto, consente l'ottimizzazione in fase di compilazione poiché il compilatore può verificare eventuali valori alternativi di abc e, se non ne trova, ottimizzare la chiamata. Lo scopo della ricerca influenza notevolmente se ciò può accadere, tuttavia, e le impostazioni del compilatore influenzano questo.

Se il compilatore è semplicemente ottimizzando ingenuamente file oggetto singoli e la seconda linea è in un altro file dalla sezione stampa, quindi potrebbe non essere in grado di garantire abc non cambia, e quindi non sarà in grado di ottimizzare questo affatto Indipendentemente dall'uso variabile, dipende da quanto sono aggressive le impostazioni del compilatore e se sono autorizzati a scartare i rami morti o prenderà in considerazione di farlo.Ottimizzare le dimensioni potrebbe essere più probabile che rimuova il ramo rispetto alla velocità, ma le impostazioni medio/alte probabilmente lo faranno in entrambi i modi (se possibile).

I compilatori più moderni dispongono di un'opzione di ottimizzazione dell'intero programma, che ritarda gran parte dell'ottimizzazione fino alla fase di collegamento. Ciò gli consente di cercare l'intero programma, potenzialmente scoprendo che abc non viene mai modificato o utilizzato altrove, e rimuovere la variabile e il ramo in errore dalla condizione. L'ottimizzazione dell'intero programma può essere molto più efficace dell'ottimizzazione separata per ciascun oggetto, poiché può consentire una ricerca più accurata.

Nel caso in cui non sia possibile per il compilatore ritagliare il codice morto, puoi suggerirlo (costrutti recenti come constexpr possono esserti d'aiuto) o aggiungere tu stesso le ottimizzazioni. Potrebbe essere semplice come mettere prima il percorso più probabile e includere un ritorno prima del resto, salvando la CPU da un salto. È improbabile che questo tipo di micro-ottimizzazione sia necessario, certamente non in un semplice esempio come questo, dove lo if è di per sé un'ottimizzazione completa.

8

Il codice C di solito viene compilato usando la compilazione statica (ovvero in anticipo). Il codice è impostato su pietra una volta che lascia il compilatore; non può cambiare in fase di esecuzione.

Ciò è in contrasto con le lingue che utilizzano just-in-time compilation, ad esempio Java. Lì, le ottimizzazioni possono accadere in qualsiasi momento mentre il programma è in esecuzione.

+1

La cosa più vicina a runtime ottimizzazione in C è l'ottimizzazione basata profiler. Compilare il programma, quindi eseguirlo, mentre si utilizza un profiler per raccogliere statistiche. Quindi si compila di nuovo, dando queste statistiche al compilatore. La seconda compilazione userebbe queste informazioni per generare codice più veloce. – ugoren

1

Non ho molta familiarità con il modo in cui un compilatore ottimizza il codice, tuttavia so che dal codice che hai lì, il compilatore può dedurre che abc non viene mai modificato. Ciò significa che potrebbe estrarre completamente la dichiarazione if e chiamare la prima funzione printf.

Quindi, in fase di esecuzione, non è rimasta molta ottimizzazione, in quanto il codice è piuttosto semplice. Se dovessi cambiare abc, ovviamente non è possibile elidere lo if, tuttavia, in fase di esecuzione, ci sono cose come la previsione delle diramazioni sulla CPU che proveranno a prevedere il percorso del codice che viene eseguito. Che potrebbe anche essere considerato una forma di ottimizzazione.

Nota: non posso affermare che questo è ciò che accade, ma ho potuto vedere che un compilatore potrebbe ottimizzare in questo modo, in un'impostazione di ottimizzazione aggressiva.

2

a tempo di compilazione :)

(Beh, in linea di principio dipende dal compilatore, ecc)

1

Mi chiedo se stessero cercando una risposta o alcune risposte possibili.

Il compilatore non esegue nulla in fase di esecuzione, di solito. Il binario del compilatore e i suoi componenti non devono essere presenti (per il linguaggio C) nell'ambiente di runtime.

Il tipo di ottimizzazione che eliminerebbe questo ramo morto userebbe determinate tecniche per scrivere i compilatori di ottimizzazione. Vedi il libro di Muchnik sui compilatori. Le tecniche utilizzate potrebbe consistere creando un grafo orientato di blocchi di base, quindi utilizzando uno di:

  • def usare analisi
  • statica singola assegnazione (SSA) analisi
    insieme
  • costante di propagazione (trasformazione del se in se (3 == 3)
    quindi
  • eliminazione espressione costante
    poi rimozione
  • codice morto/ramo morto

D'altra parte, alcuni compilatori potrebbero non calcolare abbastanza per rimuoverlo durante l'ottimizzazione.

In tal caso, se si eseguiva il codice su un chip con previsione ramo, il chip "apprendeva" che il primo ramo è previsto e cache (recupero) che si dirama favorevolmente. Ma questo non è un meccanismo di compilazione, di solito non si chiama ottimizzazione.

1

Risposta più semplice: L'ottimizzazione del codice si verifica al momento della scrittura.

Risposta semplice: forse, dipende dal compilatore in base all'ambito.

Risposta difficile: Dato l'ambito globale, il compilatore dovrebbe lasciarlo da solo con l'ipotesi che fosse possibile accedervi altrove nel file. Più passate potrebbero ottimizzarlo. Se non è considerato statico per il file dal compilatore (si pensi ai sistemi con modelli di memoria flat), allora globale è veramente globale e l'ipotesi dovrebbe essere qualsiasi cosa potrebbe cambiare il valore. Questo è il motivo per cui eviti i globali a meno che l'intento sia davvero l'accesso globale.

A seconda del processore, si applicherebbe l'argomento di previsione del ramo, ma per lo più è tempo di compilazione o non del tutto.

ps: Non mi piacciono molto le domande di intervista come questa.

+0

Concordato con la tua risposta, ma un altro dubbio è venuto fuori, se la variabile globale (Non-Volatile) può essere modificata in qualsiasi punto del tempo, allora perché abbiamo bisogno di volatile? –

+0

volatile è necessario per un contesto che appare statico. Pensa a un valore passato da un puntatore. Inoltre, se si indirizza la mappa di un registro hardware (si pensi alla mappa della memoria periferica o al registro del dispositivo), il valore può cambiare senza che venga eseguito alcun codice. – Dtyree

3

Non risponde alla domanda ma fornisce un esempio di ottimizzazione della durata della compilazione. gcc ottimizza il codice quando viene richiesto di farlo. L'opzione -O (Ottimizza) consente l'ottimizzazione in diversi livelli. Può essere usato come -O1, -O2 e -O3. La pagina man di gcc descrive precisamente il significato di ogni livello.

L'opzione -S converte C in assembly e salva su file .s.

test.c

#include <stdio.h> 

int abc;//Global variable 

void main() 
{ 
    abc = 3; 
    if(abc == 3) 
     printf("abc will be always 3"); 
    else 
     printf("This will never executed"); 
} 

Whitout ottimizzazione gcc le due stringhe appaiono sul codice assembly.

$ gcc -S test.c; gatto test.s

.file "test.c" 
    .comm abc,4,4 
    .section .rodata 
.LC0: 
    .string "abc will be always 3" 
.LC1: 
    .string "This will never executed" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    movl $3, abc(%rip) 
    movl abc(%rip), %eax 
    cmpl $3, %eax 
    jne .L2 
    movl $.LC0, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
    jmp .L1 
.L2: 
    movl $.LC1, %eax 
    movq %rax, %rdi 
    movl $0, %eax 
    call printf 
.L1: 
    popq %rbp 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 

Whit livello gcc 1 ottimizzazione una sola corda viene tradotto in assemblea

$ gcc -O1 -S test. c; gatto test.s

.file "test.c" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "abc will be always 3" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB11: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, abc(%rip) 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE11: 
    .size main, .-main 
    .comm abc,4,4 
    .ident "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)" 
    .section .note.GNU-stack,"",@progbits 
+0

Eccellente .... spiegazione –