Frage

Ich möchte Umfang Wache in C verwenden, um zu tun Profilierungs.

Ich möchte wissen, wie viel Zeit ich in einer Funktion verbringen. Hier ist, was ich tun:

int function() {

  tic();

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

  toc();
  return 1;
}

Ich brauche eine toc Anweisung ich die Funktion verlassen jedes Mal zu platzieren. Ich möchte, das zu tun, ohne Paste toc überall kopieren zu müssen. Gibt es eine generische Art und Weise, das zu tun, einen Makro oder etwas mit? Auch möchte ich nicht die Art und Weise ändern, die Funktion aufgerufen wird, da es viele Funktionen, die ich habe zu profilieren.

Danke

War es hilfreich?

Lösung

Dies ist nicht die Art und Weise der Funktion aufgerufen wird. Wahrscheinlich nicht viel, wenn Sie in der Lage sein wollen, jede einzelne Funktion zu profilieren, though.

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

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

Wie alle anderen sagt: einen Profiler verwenden, wird es Ihnen auf lange Sicht viel Mühe sparen. Wie sie sagen nicht: wenn Ihre Plattform hat man

.

Wenn dies nicht der Fall, dann ist der einfachste (als Codierungsregel) zu sagen, könnte sein, dass Funktionen nur einen Abgang haben muss und dass Austrittsstelle muss über das Makro sein. Dann können Sie Gerät alle Funktionen mit Code an den Ein- und Ausgang von Hand. Legacy-Funktionen mit mehreren Renditen können wie oben eingewickelt werden.

Auch, denken Sie daran, wenn Sie tun so etwas wie dies, dass Ihr Compiler kann Schlamassel Ihnen. Man könnte schreiben:

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

Wenn die Compiler, dass something_else bestimmt hat keine Nebenwirkungen, dann allerdings auch something_else erhebliche Zeit in Anspruch nimmt, könnte es den Code in diesem drehen:

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

Und Ihre Profildaten werden unterschätzen die Zeit, in Ihrer Funktion ausgegeben. Ein weiterer Grund, es ist so gut, einen echten Profiler zu haben - es kann mit dem Compiler zusammenarbeiten.

Andere Tipps

Sie können ein Makro wie definieren:

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

, die überall funktionieren sollte man es ausdrückte. Dann können Sie automatisieren return *; mit TOC_RETURN(*) zu ersetzen.

Warum nicht ein tatsächliches Profilierungswerkzeug verwenden, wie gprof ?

Sie konnte es einfach " neu zu definieren " Rückkehr über ein Makro: (siehe 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;
}

Hinweis: Dies gilt hässlich und Hacky werden kann, weil:

  • Es wird nicht Arbeit für nichtig Funktionen, es sei denn Sie verwenden return;. -Aussagen
  • Es ist vielleicht nicht tragbar / Standard, auch wenn es auf MSVC8 funktioniert.
  • Man sollte nicht Schlüsselwörter definieren.

Ich würde nicht einen Makro für diese empfehlen. Sie Profil den Code nur einmal in einer Weile, und nur zu diesem Zweck mit einigen speziellen Makro ‚Rückkehr‘ zu ersetzen machen Code weniger lesbar.

Ist es nicht besser folgt zu tun, wie?

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

Dieses automatisch handles "alle Ausstiegspunkte" aus der Funktion.

P. S. Warum Sie nicht verwenden einen Profiler?

Ein echter Profiler nicht benötigen Sie den Code zu ändern, nur um es zu kompilieren sind mit Profilerstellung.

Ich bin sehr spät an die Partei, aber es eine andere Möglichkeit gibt Spielraum in C mit der GCC-Erweiterung Bewachung zu tun? cleanup Attribut. Das cleanup Attribut legt, eine Funktion zu einer Variablendeklaration, die ausgeführt wird, wenn der Variable des Umfangs erlischt. Ursprünglich gedacht Speicherfreigabe für dynamisch zugewiesenen Typen auszuführen, kann es auch als Rahmen Wache missbraucht werden.

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;
}

Diese Lösung verwendet keine Makros, aber Sie können dies natürlich wickeln in einem Makro. Zum Beispiel:

#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, vielleicht wickeln Sie den Funktionsaufruf in einem Makro (Familie von Makros, wirklich)? Hier ist eine, die keine Argumente und gibt Retval nimmt:

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

Sie werden Makros müssen für jede arity von Funktionsaufrufen Sie machen, mit einem Retval und nichtig Rückkehr Version.

Bearbeiten Vielleicht ist es nicht einmal ein Punkt die Wrapper-Funktion bei der Definition und besser, nur eine Familie von Makros (auch hier für jeden arity und Rückgabetyp / Hohlraum-Versionen), der Wrap-a Funktionsaufruf in einem tic / toc direkt an dem callsites

Haben Sie keine Angst Profilometer von instrumentiert, die im Wesentlichen das für Sie tun.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top