Domanda

Vorrei utilizzare guardia portata in C al fine di fare il profiling.

Vorrei sapere quanto tempo passo in una funzione. Ecco quello che faccio:

int function() {

  tic();

  ... do stuff ...
  if (something)
  {
    toc();
    return 0;
   }

  toc();
  return 1;
}

Ho bisogno di inserire un'istruzione toc ogni volta che esco la funzione. Vorrei farlo senza dover copia incolla toc ovunque. C'è un modo generico per farlo, utilizzando una macro o qualcosa del genere? Anche io non voglio cambiare il modo in cui la funzione è chiamata, in quanto vi sono molte funzioni che ho al profilo.

Grazie

È stato utile?

Soluzione

Questo non cambia il modo in cui la funzione viene chiamata. Probabilmente non molto utile se si vuole essere in grado di profilare ogni singola funzione, però.

static inline int real_function() {
    // previous contents of function(), with no tic or toc
}

int function() {
    tic();
    int r = real_function();
    toc();
    return r;
}

Come chiunque altro dice: utilizzare un profiler, ti farà risparmiare un sacco di fatica nel lungo periodo. Poiché non dire:. Se la piattaforma ha una

Se non, quindi il modo più semplice potrebbe essere quella di dire (come regola di codifica) che le funzioni devono avere un solo punto di uscita, e che il punto di uscita deve avvenire tramite la macro. Quindi è possibile strumento manualmente tutte le funzioni con il codice all'ingresso e all'uscita. funzioni legacy con rendimenti più possono essere avvolti come sopra.

Inoltre, tenere a mente quando si sta facendo nulla di simile che il compilatore può rovinare in su. Si potrebbe scrivere questo:

tic();
do_something();
int i = something_else();
toc();
return i;

Se il compilatore determina che something_else ha alcun effetto collaterale, allora anche se something_else richiede tempo significativo, potrebbe trasformare il codice in questo:

tic();
do_something();
toc();
return something_else();

i dati del profilo e sarà sotto-stimare il tempo trascorso in vostra funzione. Un altro motivo è così buono per avere un vero e proprio profiler - può cooperare con il compilatore.

Altri suggerimenti

Si potrebbe definire una macro come:

#define TOC_RETURN(x) \
    do { \
    toc(); \
    return x; \
    } while(0)

che dovrebbe funzionare ovunque lo metti. Quindi è possibile automatizzare la sostituzione return *; con TOC_RETURN(*).

Perché non usare uno strumento di profiling vero e proprio, come gprof ?

Si può solo " ridefinire " ritorno grazie ad una macro: (vedi Disclaimer)

#include <stdio.h>

void tic() { printf("tic\n"); }
void toc() { printf("toc\n"; }

#define return toc(); return
int foo() {
    tic();

    return 0;
}
#undef return

int main() {
    foo();
    return 0;
}

Avviso: Questo può essere considerato brutto e hacky perché:

  • Non funzionerà per le funzioni vuoto se non si utilizza di ritorno;. -statements
  • Potrebbe non essere portatile / standard, anche se funziona su MSVC8.
  • Non si dovrebbe definire parole chiave.

Non suggerirei una macro per questo. È profilo il codice solo una volta ogni tanto, e la sostituzione di 'ritorno' con un po 'di macro speciale solo per questo scopo rende il codice meno leggibile.

Non è meglio fare come segue?

tic();
call_function();
toc();

Questa gestisce automaticamente "tutti i punti di uscita" dalla funzione.

P.S. Perché non si utilizza un profiler?

Un vero profiler non ha bisogno di modificare il codice, solo per compilare con profiling attivato.

Sono molto tardi al partito, ma c'è un altro modo per fare ambito guardia in C utilizzando l'estensione GCC attributo cleanup . L'attributo cleanup attribuisce una funzione a una dichiarazione di variabile che viene eseguito quando la variabile passa nell'ambito. Originariamente destinato a svolgere memoria deallocazione per i tipi allocati dinamicamente, si può abusare anche come guardia portata.

void cleanup_toc(int *ignored __attribute__((__unused__))) { toc(); }

int function(void) {
    tic();
    int atexit __attribute__((__cleanup__(cleanup_toc))) = 0;

    //... do stuff ...
    if (something) {
        return 0;
    }

    return 1;
}

Questa soluzione non usa le macro, ma è possibile, naturalmente, avvolgere questo in una macro. Ad esempio:

#define CONCATENATE_IMPL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_IMPL(x, y)
#define ATEXIT(f) int CONCATENATE(atexit, __LINE__) __attribute__((__cleanup__(f))) = 0

int function(void) {
    ATEXIT(cleanup1); // These are executed in reverse order, i.e.
    ATEXIT(cleanup2); // cleanup2 will run before cleanup1.
}

Hmm, forse avvolgere la chiamata di funzione in una macro (famiglia di macro, in realtà)? Qui è uno che non accetta argomenti e restituisce Retval:

// define the wrapper for name
#define DEFTIMECALL0(Retval,name) \
    Retval timed##name() \
    { \
        Retval ret;
        tic(); \
        ret = name(); \
        toc(); \
        return ret; \
    }

Avrete bisogno di macro per ogni arity della funzione chiamate effettuate, con una versione di ritorno Retval e vuoto.

Modifica Forse c'è nemmeno un punto nella definizione della funzione wrapper, e meglio avere solo una famiglia di macro (ancora una volta, per ogni arity e tornare di tipo versioni / nulli) che avvolgono un funzione di chiamata in un tic / toc direttamente ai callsites

Non abbiate paura di strumentazione profiler, che essenzialmente fanno questo per voi.

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