C ++での関数の実行時間の測定


138

特定の関数がC ++プログラムでLinuxで実行されるのにかかる時間を調べたい。その後、速度比較をしたいと思います。私はいくつかの時間機能を見ましたが、ブーストからこれで終わりました。クロノ:

process_user_cpu_clock, captures user-CPU time spent by the current process

さて、上記の関数を使用するかどうか明確ではありませんが、CPUがその関数に費やした唯一の時間を取得できますか?

次に、上記の関数を使用した例が見つかりませんでした。上記の機能の使い方を教えてもらえますか?

PS:現在、私はstd::chrono::system_clock::now()時間を秒単位で取得するために使用していますが、これは毎回異なるCPU負荷のために異なる結果を与えます。


2
Linuxで使用する場合:clock_gettime.. gccは他のクロックを次のようtypedef system_clock steady_clock; typedef system_clock high_resolution_clock;に定義します。Windowsでは、を使用しますQueryPerformanceCounter
ブランドン

この質問はの重複ではありませんこの1かのシナリオでは、ソリューションは異なる作るのですか?
ノーザン

関数の2つの実装があり、どちらがより良いパフォーマンスを見つけたいと思います。
ノーザン

非常に重要:最適化を有効にしてください。最適化されていないコードには、通常の最適化されたコードとは異なるボトルネックがあり、意味のあるものは何もありませ最終割り当てのためのCループ最適化ヘルプ(コンパイラー最適化を無効にした場合)。そして一般的に、マイクロベンチマークには多くの落とし穴があります。特に、CPU周波数とページフォールトで最初にウォームアップループを実行できないと、パフォーマンスを評価する慣用的な方法ですか?。そしてこの答え
Peter Cordes

関数のパフォーマンスをどのようにベンチマークするかも参照してください独自のマイクロベンチマークを展開する際の多くの落とし穴を回避するGoogleベンチマーク用。また、単純なfor()ループベンチマークは、最適化がベンチマークループとどのように相互作用するか、およびそれに対して何をすべきかについての詳細について、任意のループバインドと同じ時間を要します。
Peter Cordes

回答:


264

これは、C ++ 11で非常に使いやすいメソッドです。std::chrono::high_resolution_clockfrom <chrono>ヘッダーを使用する必要があります。

次のように使用します。

#include <iostream>
#include <chrono>

void function()
{
    long long number = 0;

    for( long long i = 0; i != 2000000; ++i )
    {
       number += 5;
    }
}

int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();
    function();
    auto t2 = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();

    std::cout << duration;
    return 0;
}

これは関数の継続時間を測定します。

注:関数のタイミングが常に同じになるとは限りません。これは、数学の演習を解くときに心が集中するのと同じように、マシンのCPUがコンピュータで実行されている他のプロセスによって使用される量が少なくなるためです。人間の頭の中では、数学の問題の解法を思い出すことができますが、コンピューターの場合、同じプロセスは常に新しいものになります。したがって、私が言ったように、あなたはいつも同じ結果を得るとは限りません!


この関数を使用すると、最初の実行で118440535マイクロ秒が得られ、同じ関数の2回目の実行で83221031マイクロ秒が得られました。その関数の期間のみを測定している場合、2つの時間測定値は等しくないのですか?
Xara、2014年

1
いいえ。コンピュータのプロセッサは、多かれ少なかれ使用できます。high_resolution_clockあなたの機能を実行するのにかかることを物理的かつリアルタイムに提供します。したがって、最初の実行では、CPUは次の実行よりも使用されていませんでした。「使用済み」とは、他のアプリケーション作業がCPUを使用することを意味します。
ビクター

1
はい、時間の平均が必要な場合、それはそれを取得する良い方法です。3回実行し、平均を計算します。
ビクター

3
一般に「名前空間を使用しない」でコードを投稿してください。どこから何が来るかを簡単に確認できます。
雪だるま

1
これはsteady_clockhigh_resolution_clock単調でない時計である可能性はありませんか?
Gillespie、

15

引数として渡された関数の実行時間を測定する関数を次に示します。

#include <chrono>
#include <utility>

typedef std::chrono::high_resolution_clock::time_point TimeVar;

#define duration(a) std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
#define timeNow() std::chrono::high_resolution_clock::now()

template<typename F, typename... Args>
double funcTime(F func, Args&&... args){
    TimeVar t1=timeNow();
    func(std::forward<Args>(args)...);
    return duration(timeNow()-t1);
}

使用例:

#include <iostream>
#include <algorithm>

typedef std::string String;

//first test function doing something
int countCharInString(String s, char delim){
    int count=0;
    String::size_type pos = s.find_first_of(delim);
    while ((pos = s.find_first_of(delim, pos)) != String::npos){
        count++;pos++;
    }
    return count;
}

//second test function doing the same thing in different way
int countWithAlgorithm(String s, char delim){
    return std::count(s.begin(),s.end(),delim);
}


int main(){
    std::cout<<"norm: "<<funcTime(countCharInString,"precision=10",'=')<<"\n";
    std::cout<<"algo: "<<funcTime(countWithAlgorithm,"precision=10",'=');
    return 0;
}

出力:

norm: 15555
algo: 2976

2
@ RestlessC0bra:実装が定義されておりhigh_resolution_clocksystem_clock(壁時計)のエイリアスsteady_clockか、3番目の独立した時計です。詳細はこちら。CPUクロックにstd::clockは、使用可能
Jahid

2
2つのマクロとグローバルなtypedef(どれも1つのキーストロークで安全ではありません)は確かにエレガントとは言えません。不便)、時限コードをラムダに入れることを要求できるだけの場合。しかし、まあ、引数を渡すことがオプションである限り。
MikeMB 2017

2
そして、これはマクロの命名に関するありとあらゆるガイドラインに違反することの正当化でしょうか?それらにプレフィックスを付けたり、大文字を使用したりせず、ローカルシンボルと衝突する可能性が非常に高い非常に一般的な名前を選択します。何よりも、(関数の代わりに)マクロを使用しているのはなぜですか。 )?そして、私たちがそうしている間:なぜ最初に期間を、ナノ秒を表すdoubleとして返すのですか?私たちはおそらく同意しないことに同意する必要があります。私の最初の意見は、「これは私がエレガントなコードと呼ぶものではありません」です。
MikeMB 2017年

1
問題はそれらがスコープされていないことです。私が心配しているのは、そのようなマクロが私のコードに含まれる(おそらくライブラリの一部として間接的に)ヘッダーファイルになってしまうことです。一般的な名前はマクロに使用され、windows.h重要なC ++プロジェクトに含まれます。まずassert第一に: "quod licet iovi non licet bovi";)。第2に、標準ライブラリのすべての決定(数十年前にさかのぼる場合もある)が、現代の標準では実際に良いアイデアであるとは限りません。c ++モジュールの設計者がデフォルトでマクロをエクスポートしないように努力するのには理由があります。
MikeMB 2017年

2
@Jahid:ありがとう。その場合、私のコメントは無効であると考えてください。
MikeMB 2017年

9

かかった関数の実行時間を見つける簡単なプログラム。

#include <iostream>
#include <ctime> // time_t
#include <cstdio>

void function()
{
     for(long int i=0;i<1000000000;i++)
     {
        // do nothing
     }
}

int main()
{

time_t begin,end; // time_t is a datatype to store time values.

time (&begin); // note time before execution
function();
time (&end); // note time after execution

double difference = difftime (end,begin);
printf ("time taken for function() %.2lf seconds.\n", difference );

return 0;
}

6
非常に不正確であり、数秒しか表示されませんが、ミリ秒は表示されません
user25

7

Scott Meyersの本で、関数の実行時間を測定するために使用できる汎用の汎用ラムダ式の例を見つけました。(C ++ 14)

auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = std::chrono::high_resolution_clock::now();
        // function invocation using perfect forwarding
        std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        // get time after function invocation
        const auto& stop = std::chrono::high_resolution_clock::now();
        return stop - start;
     };

問題は、1つの実行のみを測定するため、結果が大きく異なる可能性があることです。信頼できる結果を得るには、多数の実行を測定する必要があります。code :: dive 2015カンファレンスでのAndrei Alexandrescuの講演によると-高速コードの作成I:

測定時間:tm = t + tq + tn + to

どこ:

tm-測定された(観測された)時間

t-関心のある実際の時間

tq-量子化ノイズによって追加された時間

tn-さまざまなノイズ源によって追加された時間

to-オーバーヘッド時間(測定、ループ、関数の呼び出し)

彼が講義の後半で言ったように、あなたは結果としてこの大量の実行の最小値を取るべきです。彼がその理由を説明している講義をご覧になることをお勧めします。

また、グーグルからの非常に良いライブラリがあります-https://github.com/google/benchmark。このライブラリは非常に使いやすく、強力です。Chandler Carruthの講義をyoutubeでチェックアウトして、実際にこのライブラリを使用しています。たとえば、CppCon 2017:Chandler Carruthが「どこにも行かない」;

使用例:

#include <iostream>
#include <chrono>
#include <vector>
auto timeFuncInvocation = 
    [](auto&& func, auto&&... params) {
        // get time before function invocation
        const auto& start = high_resolution_clock::now();
        // function invocation using perfect forwarding
        for(auto i = 0; i < 100000/*largeNumber*/; ++i) {
            std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
        }
        // get time after function invocation
        const auto& stop = high_resolution_clock::now();
        return (stop - start)/100000/*largeNumber*/;
     };

void f(std::vector<int>& vec) {
    vec.push_back(1);
}

void f2(std::vector<int>& vec) {
    vec.emplace_back(1);
}
int main()
{
    std::vector<int> vec;
    std::vector<int> vec2;
    std::cout << timeFuncInvocation(f, vec).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec2).count() << std::endl;
    std::vector<int> vec3;
    vec3.reserve(100000);
    std::vector<int> vec4;
    vec4.reserve(100000);
    std::cout << timeFuncInvocation(f, vec3).count() << std::endl;
    std::cout << timeFuncInvocation(f2, vec4).count() << std::endl;
    return 0;
}

編集:もちろん、コンパイラは何かを最適化できるかどうかを常に覚えておく必要があります。そのような場合、perfなどのツールが役立ちます。


興味深い-ここで関数テンプレートよりラムダを使用する利点は何ですか?
user48956

1
主な違いは、それが呼び出し可能なオブジェクトであることですが、実際には、可変個のテンプレートとstd :: result_of_tを使用して非常によく似たものを取得できます。
Krzysztof Sommerfeld

@KrzysztofSommerfeld関数メソッドに対してこれを行う方法、timing(Object.Method1)を渡すと、エラー「標準以外の構文。 '&'を使用してメンバーへのポインターを作成する」を返します
RobinAtTech

timeFuncInvocation([&objectName](auto && ... args){objectName.methodName(std :: forward <decltype(args)>(args)...);}、arg1、arg2、...); または、objectNameの前に省略および署名(オブジェクトのコピーが作成されます)
Krzysztof Sommerfeld

4

古いC ++またはCの簡単な方法:

#include <time.h> // includes clock_t and CLOCKS_PER_SEC

int main() {

    clock_t start, end;

    start = clock();
    // ...code to measure...
    end = clock();

    double duration_sec = double(end-start)/CLOCKS_PER_SEC;
    return 0;
}

秒単位のタイミング精度は 1.0/CLOCKS_PER_SEC


1
これはポータブルではありません。Linuxではプロセッサ時間、Windowsではクロック時間を測定します。
BugSquasher

2
  • これは、C ++ 11で非常に使いやすいメソッドです。
  • ヘッダーからstd :: chrono :: high_resolution_clockを使用できます
  • メソッドを記述して、メソッドの実行時間を読みやすい形式で出力できます。

たとえば、1〜1億のすべての素数を見つけるには、約1分40秒かかります。したがって、実行時間は次のように出力されます。

Execution Time: 1 Minutes, 40 Seconds, 715 MicroSeconds, 715000 NanoSeconds

コードはここにあります:

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

typedef high_resolution_clock Clock;
typedef Clock::time_point ClockTime;

void findPrime(long n, string file);
void printExecutionTime(ClockTime start_time, ClockTime end_time);

int main()
{
    long n = long(1E+8);  // N = 100 million

    ClockTime start_time = Clock::now();

    // Write all the prime numbers from 1 to N to the file "prime.txt"
    findPrime(n, "C:\\prime.txt"); 

    ClockTime end_time = Clock::now();

    printExecutionTime(start_time, end_time);
}

void printExecutionTime(ClockTime start_time, ClockTime end_time)
{
    auto execution_time_ns = duration_cast<nanoseconds>(end_time - start_time).count();
    auto execution_time_ms = duration_cast<microseconds>(end_time - start_time).count();
    auto execution_time_sec = duration_cast<seconds>(end_time - start_time).count();
    auto execution_time_min = duration_cast<minutes>(end_time - start_time).count();
    auto execution_time_hour = duration_cast<hours>(end_time - start_time).count();

    cout << "\nExecution Time: ";
    if(execution_time_hour > 0)
    cout << "" << execution_time_hour << " Hours, ";
    if(execution_time_min > 0)
    cout << "" << execution_time_min % 60 << " Minutes, ";
    if(execution_time_sec > 0)
    cout << "" << execution_time_sec % 60 << " Seconds, ";
    if(execution_time_ms > 0)
    cout << "" << execution_time_ms % long(1E+3) << " MicroSeconds, ";
    if(execution_time_ns > 0)
    cout << "" << execution_time_ns % long(1E+6) << " NanoSeconds, ";
}

0

以下は、関数または任意のコードブロックの経過時間を測定するための優れたヘッダーのみのクラステンプレートです。

#ifndef EXECUTION_TIMER_H
#define EXECUTION_TIMER_H

template<class Resolution = std::chrono::milliseconds>
class ExecutionTimer {
public:
    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
                                     std::chrono::high_resolution_clock,
                                     std::chrono::steady_clock>;
private:
    const Clock::time_point mStart = Clock::now();

public:
    ExecutionTimer() = default;
    ~ExecutionTimer() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Destructor Elapsed: "
                  << std::chrono::duration_cast<Resolution>( end - mStart ).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }    

    inline void stop() {
        const auto end = Clock::now();
        std::ostringstream strStream;
        strStream << "Stop Elapsed: "
                  << std::chrono::duration_cast<Resolution>(end - mStart).count()
                  << std::endl;
        std::cout << strStream.str() << std::endl;
    }

}; // ExecutionTimer

#endif // EXECUTION_TIMER_H

これをいくつか使用します:

int main() {
    { // empty scope to display ExecutionTimer's destructor's message
         // displayed in milliseconds
         ExecutionTimer<std::chrono::milliseconds> timer;

         // function or code block here

         timer.stop();

    } 

    { // same as above
        ExecutionTimer<std::chrono::microseconds> timer;

        // code block here...

        timer.stop();
    }

    {  // same as above
       ExecutionTimer<std::chrono::nanoseconds> timer;

       // code block here...

       timer.stop();

    }

    {  // same as above
       ExecutionTimer<std::chrono::seconds> timer;

       // code block here...

       timer.stop();

    }              

    return 0;
}

クラスはテンプレートであるため、時間を測定および表示する方法を実際に簡単に指定できます。これはベンチマーキングを行うための非常に便利なユーティリティクラステンプレートであり、非常に使いやすいです。


個人的にはstop()、デストラクタがタイマーを停止するため、メンバー関数は必要ありません。
ケーシー

@Caseyクラスの設計は必ずしも停止関数を必要としませんが、特定の理由でそこにあります。test codeタイマーを開始する前にオブジェクトを作成するときのデフォルトの構成。次にtest code、タイマーオブジェクトを明示的に使用し、そのstopメソッドを呼び出します。stopタイマーを使用する場合は、手動で呼び出す必要があります。クラスはパラメータを取りません。また、私が示したようにこのクラスを使用した場合は、obj.stopとの呼び出しの間に最小限の時間が経過することがわかりますdestructor
Francis Cugler、2018

@Casey ...これにより、同じスコープ内に複数のtimerオブジェクトを含めることもできます。実際にそれが必要になるのではなく、もう1つの実行可能なオプションだけです。
Francis Cugler、2018

この例は、提示された形式ではコンパイルできません。エラーは「演算子<< ...に一致しません」に関連しています!
セルドール

@Celdorあなたは適切なインクルードをしなければなりませんか?など<chrono>
Francis Cugler、

0

steady_clockは異なり、単調であることが保証されているものを使用することをお勧めしhigh_resolution_clockます。

#include <iostream>
#include <chrono>

using namespace std;

unsigned int stopwatch()
{
    static auto start_time = chrono::steady_clock::now();

    auto end_time = chrono::steady_clock::now();
    auto delta    = chrono::duration_cast<chrono::microseconds>(end_time - start_time);

    start_time = end_time;

    return delta.count();
}

int main() {
  stopwatch(); //Start stopwatch
  std::cout << "Hello World!\n";
  cout << stopwatch() << endl; //Time to execute last line
  for (int i=0; i<1000000; i++)
      string s = "ASDFAD";
  cout << stopwatch() << endl; //Time to execute for loop
}

出力:

Hello World!
62
163514

0

この種の測定に使用できる単純なクラスを持つことができます。

class duration_printer {
public:
    duration_printer() : __start(std::chrono::high_resolution_clock::now()) {}
    ~duration_printer() {
        using namespace std::chrono;
        high_resolution_clock::time_point end = high_resolution_clock::now();
        duration<double> dur = duration_cast<duration<double>>(end - __start);
        std::cout << dur.count() << " seconds" << std::endl;
    }
private:
    std::chrono::high_resolution_clock::time_point __start;
};

行う必要があるのは、その関数の最初に関数内にオブジェクトを作成することだけです。

void veryLongExecutingFunction() {
    duration_calculator dc;
    for(int i = 0; i < 100000; ++i) std::cout << "Hello world" << std::endl;
}

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

以上です。クラスは、要件に合わせて変更できます。


0

提供された回答はどれも非常に正確ではなく、再現可能な結果も得られないので、サブナノ秒の精度と科学的統計を持つコードへのリンクを追加することにしました。

これは、実行に(非常に)短い時間(数クロックサイクルから数千サイクル)がかかるコードを測定する場合にのみ機能することに注意してください。実行時間が長すぎて、-heh-割り込みによって中断される可能性がある場合その場合、再現可能で正確な結果を出すことは明らかに不可能です。その結果、測定は終了しません。つまり、統計的に99.9%になるまで測定を続け、コードに時間がかかりすぎたときに他のプロセスが実行されているマシンでは絶対に正しい答えがないことを確認します。

https://github.com/CarloWood/cwds/blob/master/benchmark.h#L40


0

時間とコードの行を安全にしたい場合は、関数の実行時間の測定を1行のマクロにすることができます。

a)既に上記で提案されているように時間測定クラスを実装します(これは私のAndroidの実装です):

class MeasureExecutionTime{
private:
    const std::chrono::steady_clock::time_point begin;
    const std::string caller;
public:
    MeasureExecutionTime(const std::string& caller):caller(caller),begin(std::chrono::steady_clock::now()){}
    ~MeasureExecutionTime(){
        const auto duration=std::chrono::steady_clock::now()-begin;
        LOGD("ExecutionTime")<<"For "<<caller<<" is "<<std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()<<"ms";
    }
};

b)現在の関数名をタグとして使用する便利なマクロを追加します(ここでマクロを使用することが重要です。それ以外の場合は、測定する関数の代わりに__FUNCTION__評価されMeasureExecutionTimeます

#ifndef MEASURE_FUNCTION_EXECUTION_TIME
#define MEASURE_FUNCTION_EXECUTION_TIME const MeasureExecutionTime measureExecutionTime(__FUNCTION__);
#endif

c)測定する関数の最初にマクロを記述します。例:

 void DecodeMJPEGtoANativeWindowBuffer(uvc_frame_t* frame_mjpeg,const ANativeWindow_Buffer& nativeWindowBuffer){
        MEASURE_FUNCTION_EXECUTION_TIME
        // Do some time-critical stuff 
}

これにより、次の出力が得られます。

ExecutionTime: For DecodeMJPEGtoANativeWindowBuffer is 54ms

これは(他のすべての推奨ソリューションと同様に)、関数が呼び出されてから戻るまでの時間を測定することに注意してください。必ずしも、CPUが関数を実行していた時間ではありません。ただし、sleep()などを呼び出して実行中のコードを一時停止する変更をスケジューラに与えない場合、違いはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.