2013-01-21 12 views
5

consideri un ciclo while in ANSI C il cui unico scopo è quello di ritardare l'esecuzione:Un compilatore ANSI C può rimuovere un ciclo di ritardo?

unsigned long counter = DELAY_COUNT; 
while(counter--); 

Ho visto questo usato molto per far rispettare i ritardi nei sistemi embedded, in cui ad es. non esiste la funzione sleep e i timer o gli interrupt sono limitati.

La mia lettura dello standard ANSI C è che questo può essere completamente rimosso da un compilatore conforme. Ha nessuno degli effetti collaterali descritti in 5.1.2.3:

accesso a un oggetto volatile, modifica di un oggetto, modificando un file, o chiamando una funzione che fa una di queste operazioni sono effetti indesiderati, che sono cambiamenti nello stato dell'ambiente di esecuzione.

... e questa sezione dice anche:

Un'implementazione effettiva non deve valutare parte di un'espressione se può dedurre che il suo valore non viene utilizzato e che effetti collaterali necessari vengono prodotti (compresi quelli causati chiamando una funzione o accedendo a un oggetto volatile).

Ciò implica che il ciclo potrebbe essere ottimizzato? Anche se counter erano volatile?

Note:

  1. Che questa non è proprio la stessa Are compilers allowed to eliminate infinite loops?, perché si riferisce a infiniti loop, e domande sorgono circa quando un programma è permesso di interrompere a tutti. In questo caso, il programma procederà sicuramente oltre questa linea ad un certo punto, l'ottimizzazione o meno.
  2. So cosa fa GCC (rimuove il ciclo per -O1 o superiore, a meno che counter sia volatile), ma voglio sapere che cosa dettano gli standard.
+0

Fintanto che il * comportamento * invisibile non viene modificato, lo standard C non proibirebbe l'ottimizzazione di questo ciclo. Quindi lo standard non impone nulla in particolare. –

risposta

11

La conformità allo standard C segue la regola "as-if", in base alla quale il compilatore può generare qualsiasi codice che si comporti "come se" stesse eseguendo le istruzioni effettive sulla macchina astratta. Dal momento che non eseguire alcuna operazione ha lo stesso comportamento osservabile "come se" si eseguisse il ciclo, è del tutto lecito non generare codice per questo.

In altre parole, il tempo impiegato da qualcosa per calcolare su una macchina reale non fa parte del comportamento "osservabile" del programma, è semplicemente un fenomeno di una particolare implementazione.

La situazione è diversa per le variabili volatile, poiché l'accesso a un conteggio volatile è un effetto "osservabile".

+0

@KingsIndian: hehe, non preoccuparti, penso che la varietà tra le risposte sono sempre una buona cosa :-) –

9

Ciò implica che il ciclo potrebbe essere ottimizzato?

Sì.

Anche se il contatore era volatile?

No. Leggere e scrivere una variabile volatile, che ha un comportamento osservabile, quindi deve verificarsi.

2

Se il contatore è volatile, il compilatore non può ottimizzare legalmente il ciclo di ritardo. Altrimenti può.

I cicli di ritardo di questo tipo sono negativi perché il tempo di masterizzazione dipende da come il compilatore genera il codice per essi. Utilizzando diverse opzioni di ottimizzazione è possibile ottenere ritardi diversi, il che non è certo quello che si desidera da un ciclo di ritardo.

Per questo motivo tali ritardi devono essere implementati in linguaggio assembly, in cui il programmatore controlla completamente il codice.Questo di solito si applica a sistemi embedded con CPU semplici.

+2

Se lo si implementa in linguaggio assembly o no è irrilevante in un certo senso. I cicli di ritardo che si basano su un certo numero di iterazioni sono flakey: il "ritardo" varia in base a una serie di fattori, quasi tutti fuori dal controllo del programmatore. ** Non utilizzare questi cicli di ritardo. ** –

+1

@NikBougalis Non necessariamente vero. Se la CPU è relativamente stupida ed esegue le istruzioni usando sempre lo stesso numero di orologi (perché è stupido e non ha cache) e se la frequenza di clock non cambia, i ritardi saranno piuttosto stabili, a meno che il gli interrupt sono abilitati e gli ISR ​​vengono eseguiti frequentemente e richiedono molto tempo. E durante l'inizializzazione dell'hardware, gli interrupt vengono spesso disabilitati temporaneamente o fino al completamento dell'inizializzazione. Se tutte queste condizioni sono soddisfatte, nessun problema con i cicli di ritardo. –

+0

@NikBougalis I processori RISC su sistemi embedded hanno ritardi numerabili per la maggior parte del tempo e i loop di delay possono avere il loro time bound abbastanza bene. –

1

Lo standard determina il comportamento che si vede. Se crei un albero delle dipendenze per DELAY_COUNT, vedi che ha una modifica senza usare la proprietà, il che significa che può essere eliminato. Questo è in riferimento al caso non volatile. Nel caso volatile il compilatore non può usare l'albero delle dipendenze per tentare di rimuovere questa variabile e come tale rimane il ritardo (poiché volatile significa che l'hardware può cambiare il valore mappato in memoria O in alcuni casi significa "Ho davvero bisogno che questo non lo lanci ") Nel caso in cui stai guardando se etichettato volatile, dice al compilatore, per favore non buttarlo via, è qui per un motivo.