2013-07-02 11 views
7

Normalmente quando ho un grande ciclo for ho messo i messaggi di informarmi in quale parte del processo di mio programma è, per esempio:informativo "if" in "per" ciclo

for(i = 0; i < large_n; i++) { 
    if(i % (large_n)/1000 == 0) { 
     printf("We are at %ld \n", i); 
    } 
    // Do some other stuff 
} 

mi chiedevo se questo fa troppo male le prestazioni (a priori) e se è il caso se c'è un'alternativa più intelligente. Grazie in anticipo.

+2

comunque alla fine lo rimuovi no? – Alexis

+1

Perché fai '/ 1000'? Direi che 'i% large_n' dovrebbe essere sufficiente per questo. –

+3

Perché ti preoccupi delle prestazioni per qualcosa che usi solo nei build di debug e di sviluppo? –

risposta

5

Forse è possibile dividere il loop grande per verificare solo la condizione a volte, ma non so se questo farà risparmiare molto tempo, che dipende più dalle "altre cose".

int T = ...; // times to check the condition, make sure large_n % T == 0 
for(int t = 0; t < T; ++t) 
{ 
    for(int i = large_n/T * t; i < large_n/T * (t+1); ++i) 
    { 
    // other stuff 
    } 
    printf("We are at %ld \n", large_n/T * (t+1)); 
} 
+1

Grazie per la tua risposta! – Bunder

3

Il test mod probabilmente non fa male le prestazioni, ma se volete un test molto veloce e ti sei preparato per multipli di due quindi prendere in considerazione un matematico and prova:

if ((i & 0xFF) == 0) { 
    /* this gets printed every 256 iterations */ 
    ... 
} 

o

if ((i & 0xFFFF) == 0) { 
    /* this gets printed every 65536 iterations */ 
    ... 
} 
+0

È il printf che utilizza la CPU, non il se – LtWorf

+0

@LtWorf non sempre. Se stai codificando un ciclo molto stretto che esegue una semplice operazione aritmetica e sta girando incredibilmente velocemente, la percentuale di tempo speso nell'istruzione if è importante. Certo, è un caso raro, ma quando è importante un po 'di peggio può aiutare. Mentre un printf() richiede molto tempo diventa trascurabile se chiamato con parsimonia nel ciclo - da qui lo scopo della chiamata 'if' in primo luogo per stampare solo occasionalmente. Quindi, mentre la tua asserzione è di solito corretta, sarebbe un errore fare una chiamata coperta senza sapere cosa contiene il loop. –

4

Indipendentemente da ciò che è nel vostro ciclo, non avrebbe lasciato dichiarazioni come printf a meno che non sia indispensabile per l'applicazione/utente, e non lo usare quello che sono effettivamente ridondanti if dichiarazioni, per lo stesso motivo.

Entrambi sono esempi di debug a livello di traccia. Sono totalmente validi e in alcuni casi molto utili, ma generalmente non alla fine così nell'applicazione finale. A questo proposito, una cosa normale da fare è includerli nella build solo quando in realtà vuoi utilizzare le informazioni che forniscono. In questo caso, si potrebbe fare qualcosa di simile:

#define DEBUG 

for(i = 0; i < large_n; i++) 
{ 
    #ifdef DEBUG 
     if(i % (large_n)/1000 == 0) 
     { 
      printf("We are at %ld \n", i); 
     } 
    #endif 
} 

Per quanto riguarda il costo delle prestazioni di includere queste uscite di debug per tutto il tempo, ma sarà totalmente dipende dal sistema è in esecuzione, l'efficienza di qualsiasi "stampa" dichiarazione che stai usando per emettere i dati, i controlli che stai facendo e, naturalmente, la frequenza con cui stai cercando di eseguire l'output.

+0

+1 per la menzione DEBUG, OP probabilmente troverebbe utile che possa essere definito con il flag del compilatore '-DDEBUG = 1', facilita notevolmente il debug. – Nobilis

0

Inserendo un'istruzione di stampa all'interno del ciclo for, si stanno sacrificando alcune prestazioni.

Poiché il programma deve eseguire una chiamata di sistema per scrivere l'output sullo schermo ogni volta che il messaggio viene stampato, richiede tempo alla CPU lontano dal programma stesso.

si può vedere la differenza di prestazioni tra queste due loop:

int i; 
printf("Start Loop A\n"); 
for(i = 0; i < 100000; i++) { 
    printf("%d ", i); 
} 
printf("Done with Loop A\n"); 

printf("Start Loop B\n"); 
for(i = 0; i < 100000; i++) { 
    // Do Nothing 
} 
printf("Done with Loop B\n"); 

vorrei includere il codice tempi, ma sono nel bel mezzo di lavoro e può aggiornare in un secondo momento durante il pranzo.

Se la differenza non è visibile, è possibile aumentare il numero da 100000 a un numero maggiore (anche se un numero troppo grande farebbe sì che il primo ciclo impieghi troppo tempo per completare).

Whoops, ho dimenticato di finire la mia risposta.

Per ridurre il numero di chiamate di sistema che il programma deve eseguire, è possibile innanzitutto controllare una condizione e stampare solo se tale condizione è vera.

Ad esempio, se si contavano come nel mio codice di esempio, si poteva solo stampare tutti i numeri 100 ° utilizzando %:

int i; 
for(i = 0; i < 100000; i++) { 
    if(i%100 == 0) 
     printf("%d", i); 
} 

che ridurrà il numero di chiamate di sistema da ~ 100000 a ~ 1000 , che a sua volta aumenterebbe le prestazioni del ciclo.

+1

Questo è praticamente quello che faccio. La domanda è piuttosto che l'affermazione "se" fa male alla performance. – Bunder

+0

Mi dispiace, ho letto male la domanda. –

0

Il problema è il funzionamento IO printf richiede molto tempo rispetto al calcolo del processore. puoi ridurre il tempo se puoi aggiungerli tutti e stampare infine.

0

Notazione:

Tp = total time spent executing the progress statements. 
Tn = total time spent doing the other normal stuff. 
>> = Much greater than 

Se le prestazioni sono i vostri criteri principali, si vuole Tn >> Tp. Questo suggerisce fortemente che il codice dovrebbe essere profilato in modo che tu possa scegliere valori appropriati. La routine 'printf()' è considerata una routine lenta (molto più lenta di%) ed è una routine di blocco (ovvero, il thread che la chiama può essere in attesa di una risorsa utilizzata da esso).

Personalmente, mi piace astrarre l'indicatore di avanzamento. Può essere un meccanismo di registrazione, un printf, una finestra di avanzamento, .... diamine, potrebbe essere l'aggiornamento di una struttura che viene letta da un altro thread/task/processo.

id = progressRegister (<some predefined type of progress update mechanism>); 
for(i = 0; i < large_n; i++) { 
    progressUpdate (id, <string>, i, large_n); 
    // Do some other stuff 
} 
progressUnregister(id); 

sì, c'è un certo overhead nel chiamare la routine 'progressUpdate()' ad ogni iterazione, ma ancora una volta, fino a quando Tn >> Tp, di solito non è così importante.

Spero che questo aiuti.