Domanda

In più C o C++ ambienti, non c'è un "debug mode" e una "release" modalità di compilazione.
Guardando la differenza tra i due, è possibile trovare che la modalità di debug aggiunge i simboli di debug (spesso l'opzione-g, su un sacco di compilatori), ma si disattiva anche la maggior parte delle ottimizzazioni.
In "rilascio" modalità, è di solito di avere tutti i tipi di ottimizzazioni acceso.
Perché la differenza?

È stato utile?

Soluzione

Senza alcuna ottimizzazione, il flusso attraverso il codice è lineare.Se siete alla riga 5 e singolo passo, passo la linea 6.Con l'ottimizzazione, è possibile ottenere istruzioni di riordino, il loop unrolling e tutti i tipi di ottimizzazioni.
Per esempio:


void foo() {
1:  int i;
2:  for(i = 0; i < 2; )
3:    i++;
4:  return;

In questo esempio, senza ottimizzazione, si potrebbe singolo passaggio attraverso il codice e premere linee 1, 2, 3, 2, 3, 2, 4

Con l'ottimizzazione, si potrebbe ottenere un percorso di esecuzione, che si presenta come:2, 3, 3, 4 o anche solo 4!(La funzione non fa nulla, dopo tutto...)

Linea di fondo, il debug di codice con l'ottimizzazione abilitato può essere un dolore reale!Soprattutto se si dispone di grandi funzioni.

Si noti che attivando le modifiche di ottimizzazione del codice!In certi ambienti (safety critical systems), questo è inaccettabile e il codice in fase di debug deve essere il codice della spedizione.Devo eseguire il debug con l'ottimizzazione in questo caso.

Mentre l'ottimizzazione e di codice non ottimizzato dovrebbe essere "funzionalmente equivalente", in determinate circostanze, il comportamento cambia.
Ecco un semplice esempio:

    int* ptr = 0xdeadbeef;  // some address to memory-mapped I/O device
    *ptr = 0;   // setup hardware device
    while(*ptr == 1) {    // loop until hardware device is done
       // do something
    }

Con l'ottimizzazione off, questo è semplice, e un pò sapere cosa aspettarsi.Tuttavia, se si attiva l'ottimizzazione, un paio di cose potrebbe accadere:

  • Il compilatore potrebbe ottimizzare il tempo isolato di distanza (ci init a 0, non sarà mai 1)
  • Invece di memoria di accesso, accesso puntatore potrebbe essere spostato ad un registro->Nessun Aggiornamento di I/O
  • l'accesso alla memoria potrebbero essere memorizzate nella cache (non necessariamente di ottimizzazione del compilatore correlati)

In tutti questi casi, il comportamento dovrebbe essere drasticamente diverso e probabilmente sbagliato.

Altri suggerimenti

Un'altra differenza fondamentale tra il debug e release è come variabili locali vengono memorizzati.Concettualmente le variabili locali sono memoria allocata in un funzioni stack frame.Il simbolo di file generato dal compilatore dice il debugger l'offset della variabile nello stack frame, in modo che il debugger è in grado di mostrare a voi.Il debugger per controllare la posizione di memoria per fare questo.

Tuttavia, questo significa che ogni volta che una variabile locale è cambiato il codice generato per l'origine in linea deve scrivere il valore nella posizione corretta sullo stack.Questo è molto inefficiente a causa di sovraccarico di memoria.

In una build di rilascio il compilatore può assegnare una variabile locale di un registro per una porzione di una funzione.In alcuni casi potrebbe non assegnare stack di archiviazione per tutti (il più registri una macchina è il più facile questo è a che fare).

Tuttavia, il debugger non sa come si registra la mappa per le variabili locali per un particolare punto del codice (io non sono a conoscenza di qualsiasi simbolo formato che include queste informazioni), quindi non può mostrare con precisione come non so dove andare a cercarlo.

Un'altra ottimizzazione sarebbe funzione inline.In ottimizzata il compilatore può sostituire una chiamata foo() con il codice effettivo per pippo ovunque è usato perché la funzione è abbastanza piccolo.Tuttavia, quando si tenta di impostare un punto di interruzione foo() il debugger vuole conoscere l'indirizzo delle istruzioni per l'foo(), e non c'è più una risposta semplice a questa-ci possono essere migliaia di copie dei foo() byte di codice si sviluppa su un programma.Una build di debug vi garantisco che c'è da qualche parte per mettere il punto di interruzione.

L'ottimizzazione del codice è un processo automatico che migliora le prestazioni di runtime del codice preservando la semantica.Questo processo può rimuovere i risultati intermedi che sono unncessary per completare un'espressione o funzione di valutazione, ma possono essere di interesse a voi durante il debug.Allo stesso modo, le ottimizzazioni possono alterare l'apparente flusso di controllo in modo che le cose possono accadere in un ordine leggermente diverso rispetto a quello che appare nel codice sorgente.Questo viene fatto saltare inutili o ridondanti calcoli.Questo rejiggering di codice può pasticciare con la mappatura tra il codice sorgente numeri di riga e di codice oggetto indirizzi, rendendo difficile per un debugger per seguire il flusso di controllo, come è stato scritto.

Debug non ottimizzata la modalità che ti permette di vedere tutto quello che hai scritto come hai scritto senza l'ottimizzatore di rimuovere o riordinare le cose.

Una volta sono felice che il programma funziona correttamente, è possibile attivare le ottimizzazioni per ottenere migliori prestazioni.Anche se ottimizzatori sono abbastanza affidabili in questi giorni, è ancora una buona idea per costruire una buona qualità di suite di test per assicurarsi che il programma funziona in modo identico (da un punto di vista funzionale, non considerando le prestazioni) sia ottimizzato e non ottimizzata la modalità.

L'attesa è per la versione di debug di essere il debug!Impostare i punti di interruzione, l'esecuzione passo passo, mentre guardando le variabili, le tracce dello stack, e tutto il resto si fa in un debugger IDE (o altro) ha senso se ogni riga non vuota, non commento il codice sorgente corrisponde a un codice macchina dell'istruzione.

La maggior parte delle ottimizzazioni confusione con l'ordine dei codici di macchina.Il Loop unrolling è un buon esempio.Comune sottoespressioni può essere sollevato fuori loop.Con l'ottimizzazione acceso, anche il livello più semplice, si potrebbe essere cercando di impostare un punto di interruzione su una linea che, a macchina a livello di codice, non esiste.A volte non è possibile monitorare una variabile locale a causa di essa essere conservati in un registro della CPU, o forse anche ottimizzato di esistere!

Se si esegue il debug a livello di istruzione piuttosto che il livello di fonte, è un lotto terribile per voi più facile per la mappa non ottimizzata istruzioni alla fonte.Inoltre, i compilatori di tanto in tanto, buggy nella loro ottimizzazione.

Nella divisione Windows di Microsoft, tutte rilasciare i file binari sono costruiti con i simboli di debug e pieno di ottimizzazione.I simboli sono memorizzati in diversi file PDB e non influenzano le prestazioni del codice.Non forniti con il prodotto, ma la maggior parte di loro sono disponibili presso la Simbolo Di Microsoft Server.

Un altro dei problemi con le ottimizzazioni sono funzioni inline, anche nel senso che è sempre unico passaggio attraverso di loro.

Con GCC, con il debug e ottimizzazioni attivate insieme, se non si sa cosa aspettarsi, si pensi che il codice funziona in modo anomalo e ri-eseguire la stessa istruzione più volte è capitato che un paio di miei colleghi.Anche il debug info date da GCC con ottimizzazioni tendono ad essere di qualità inferiore rispetto a essi potrebbero, in realtà.

Tuttavia, in lingue ospitato da una Macchina Virtuale proprio come Java, ottimizzazioni e di debug possono convivere anche durante il debug JIT compilazione in codice nativo continua, e solo il codice di debug metodi, in modo trasparente convertito in una versione non ottimizzata.

Vorrei sottolineare che l'ottimizzazione non dovrebbe cambiare il comportamento del codice, a meno che l'usato optimizer è difettoso, o il codice stesso è pieno di bug e si basa sulla parzialmente definito semantica;la seconda è più comune nella programmazione multithreading o quando l'assembly inline viene utilizzato anche.

Codice con i simboli di debug sono più grandi, il che significa più fallimenti della cache, cioèpiù lento, che può essere un problema per il software del server.

Almeno su Linux (e non c'è motivo di Windows dovrebbe essere diverso) informazioni di debug sono confezionati in una sezione separata del binario, e non vengono caricati durante l'esecuzione normale.Essi possono essere suddivisi in un file diverso per essere utilizzato per il debug.Inoltre, su alcuni compilatori (inclusi Gcc, credo che anche con Microsoft compilatore C) informazioni per il debug e ottimizzazioni possono essere attivati insieme.Se non, ovviamente, il codice sarà più lento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top