2015-07-13 4 views
5

Ich interessiere mich für Timing der Ausführungszeit einer freien Funktion oder einer Member-Funktion (Vorlage oder nicht). Rufen Sie TheFunc die Funktion in Frage, seine Forderung zu seinTiming in eleganter Weise in C++

TheFunc(/*parameters*/); 

oder

ReturnType ret = TheFunc(/*parameters*/); 

Natürlich habe ich diese Funktion wickeln könnte Anrufe wie folgt:

double duration = 0.0 ; 
std::clock_t start = std::clock(); 
TheFunc(/*parameters*/); 
duration = static_cast<double>(std::clock() - start)/static_cast<double>(CLOCKS_PER_SEC); 

oder

double duration = 0.0 ; 
std::clock_t start = std::clock(); 
ReturnType ret = TheFunc(/*parameters*/); 
duration = static_cast<double>(std::clock() - start)/static_cast<double>(CLOCKS_PER_SEC); 

aber ich würde gerne d o etwas eleganter als das, nämlich (und ich von nun an auf den Rückgabetyp void bleiben wird) wie folgt:

Timer thetimer ; 
double duration = 0.0; 
thetimer(*TheFunc)(/*parameters*/, duration); 

wo Timer einige Timing-Klasse ist die Ich mag würde entwerfen und das würde erlauben Sie mir, den vorherigen Code in einer solchen Art und Weise schreiben, dass die doppelte Dauer der Ausführungszeit von

TheFunc(/*parameters*/); 

nach dem exectution der letzten Zeile des vorherigen Code enthalten, aber ich sehe nicht, wie dies zu tun, noch wenn die Syntax/Lösung, die ich anstrebe, ist optimal ...

+0

Ich verstehe nicht wirklich, wonach Sie fragen. Ist es besser, einen asynchronen Timer zu verwenden? –

+0

Ich werde meine Frage bearbeiten. –

+0

Edit gerade getan. –

Antwort

6

Mit variadic Schablone, können Sie tun:

template <typename F, typename ... Ts> 
double Time_function(F&& f, Ts&&...args) 
{ 
    std::clock_t start = std::clock(); 
    std::forward<F>(f)(std::forward<Ts>(args)...); 
    return static_cast<double>(std::clock() - start)/static_cast<double>(CLOCKS_PER_SEC); 
} 
+0

Das ist so eine gute Idee. Vielleicht möchten Sie einen Blick auf die Bearbeitung meiner Antwort werfen, wo ich Ihre Idee nehme und mit 'std :: chrono' arbeiten lasse. – Escualo

4

Ich mag boost::cpu_timer::auto_cpu_timer wirklich, und wenn ich nicht boost kann ich einfach meine eigene Hack:

#include <cmath> 
#include <string> 
#include <chrono> 
#include <iostream> 

class AutoProfiler { 
public: 
    AutoProfiler(std::string name) 
     : m_name(std::move(name)), 
     m_beg(std::chrono::high_resolution_clock::now()) { } 
    ~AutoProfiler() { 
    auto end = std::chrono::high_resolution_clock::now(); 
    auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end - m_beg); 
    std::cout << m_name << " : " << dur.count() << " musec\n"; 
    } 
private: 
    std::string m_name; 
    std::chrono::time_point<std::chrono::high_resolution_clock> m_beg; 
}; 

void foo(std::size_t N) { 
    long double x {1.234e5}; 
    for(std::size_t k = 0; k < N; k++) { 
    x += std::sqrt(x); 
    } 
} 


int main() { 
    { 
    AutoProfiler p("N = 10"); 
    foo(10); 
    } 

    { 
    AutoProfiler p("N = 1,000,000"); 
    foo(1000000); 
    } 

} 

Dieser Timer arbeitet dank RAII. Wenn Sie das Objekt innerhalb eines Bereichs erstellen, speichern Sie den Zeitpunkt zu diesem Zeitpunkt. Wenn Sie den Bereich verlassen (also den entsprechenden Wert }), speichert der Timer zuerst den Zeitpunkt, berechnet dann die Anzahl der Ticks (die Sie in eine für Menschen lesbare Dauer konvertieren können) und druckt sie schließlich auf dem Bildschirm aus.

Natürlich ist boost::timer::auto_cpu_timer viel aufwendiger als meine einfache Implementierung, aber ich finde oft meine Implementierung mehr als ausreichend für meine Zwecke.

Beispiel in meinem Computer laufen:

$ g++ -o example example.com -std=c++14 -Wall -Wextra 
$ ./example 
N = 10 : 0 musec 
N = 1,000,000 : 10103 musec 

EDIT

Ich mochte die von @ Jarod42 vorgeschlagen Umsetzung. Ich habe es ein wenig modifiziert, um eine gewisse Flexibilität bei den gewünschten "Einheiten" der Ausgabe zu bieten.

Standardmäßig wird die Anzahl der verstrichenen Mikrosekunden (eine Ganzzahl, normalerweise std::size_t) zurückgegeben. Sie können jedoch die Ausgabe in einer beliebigen Dauer anfordern.

Ich denke, es ist ein flexibler Ansatz als der, den ich früher vorgeschlagen habe, weil ich jetzt andere Sachen machen kann, wie die Messungen zu machen und sie in einem Container zu lagern (wie im Beispiel).

Dank @ Jarod42 für die Inspiration.

#include <cmath> 
#include <string> 
#include <chrono> 
#include <algorithm> 
#include <iostream> 

template<typename Duration = std::chrono::microseconds, 
     typename F, 
     typename ... Args> 
typename Duration::rep profile(F&& fun, Args&&... args) { 
    const auto beg = std::chrono::high_resolution_clock::now(); 
    std::forward<F>(fun)(std::forward<Args>(args)...); 
    const auto end = std::chrono::high_resolution_clock::now(); 
    return std::chrono::duration_cast<Duration>(end - beg).count(); 
} 

void foo(std::size_t N) { 
    long double x {1.234e5}; 
    for(std::size_t k = 0; k < N; k++) { 
    x += std::sqrt(x); 
    } 
} 

int main() { 
    std::size_t N { 1000000 }; 

    // profile in default mode (microseconds) 
    std::cout << "foo(1E6) takes " << profile(foo, N) << " microseconds" << std::endl; 

    // profile in custom mode (e.g, milliseconds) 
    std::cout << "foo(1E6) takes " << profile<std::chrono::milliseconds>(foo, N) << " milliseconds" << std::endl; 

    // To create an average of `M` runs we can create a vector to hold 
    // `M` values of the type used by the clock representation, fill 
    // them with the samples, and take the average 
    std::size_t M {100}; 
    std::vector<typename std::chrono::milliseconds::rep> samples(M); 
    for(auto & sample : samples) { 
    sample = profile(foo, N); 
    } 
    auto avg = std::accumulate(samples.begin(), samples.end(), 0)/static_cast<long double>(M); 
    std::cout << "average of " << M << " runs: " << avg << " microseconds" << std::endl; 
} 

Output (kompiliert mit g++ example.cpp -std=c++14 -Wall -Wextra -O3):

foo(1E6) takes 10073 microseconds 
foo(1E6) takes 10 milliseconds 
average of 100 runs: 10068.6 microseconds 
0

Ich bin ein Fan von für diese Art von Sachen RAH-Wrapper verwenden.

Das folgende Beispiel ist ein wenig weitschweifig, aber es ist flexibler, dass sie mit beliebigen Bereichen arbeiten, anstatt auf einen einzigen Funktionsaufruf beschränkt zu sein:

class timing_context { 
public: 
    std::map<std::string, double> timings; 
}; 

class timer { 
public: 
    timer(timing_context& ctx, std::string name) 
    : ctx(ctx), 
     name(name), 
     start(std::clock()) {} 

    ~timer() { 
    ctx.timings[name] = static_cast<double>(std::clock() - start)/static_cast<double>(CLOCKS_PER_SEC); 
    } 

    timing_context& ctx; 
    std::string name; 
    std::clock_t start; 
}; 

timing_context ctx; 

int main() { 
    timer_total(ctx, "total"); 
    { 
    timer t(ctx, "foo"); 
    // Do foo 
    } 

    { 
    timer t(ctx, "bar"); 
    // Do bar 
    } 
    // Access ctx.timings 
} 

Der Nachteil ist, dass Sie mit einer Menge könnten am Ende von Bereichen, die nur dazu dienen, das Timing-Objekt zu zerstören.

Dies könnte oder könnte nicht Ihren Anforderungen entsprechen, da Ihre Anfrage ein wenig vage war, aber es zeigt, wie RAII Semantik für einige wirklich schöne wiederverwendbare und sauberen Code machen kann. Es kann wahrscheinlich modifiziert werden, um auch viel besser auszusehen!

1

Sie können es MatLab Weg tun. Es ist sehr alte Schule, aber einfach ist oft gut:

tic(); 
    a = f(c); 
    toc(); //print to stdout, or 
    auto elapsed = toc(); //store in variable 

tic() und toc() können auf eine globale Variable arbeiten. Wenn das nicht ausreicht, können Sie lokale Variablen mit etwas Makro-Magie erstellen:

tic(A); 
    a = f(c); 
    toc(A);