web-dev-qa-db-ja.com

標準のC ++ 11は、high_resolution_clockがリアルタイム(CPUサイクル以外)を測定することを保証しますか?

知られているように、clock()はリアルタイムの値よりも小さいか大きい場合があります。どちらの場合も、次の例1と2に示されています。

C++ 11での時間の高精度測定には、次のものを使用できます。

  • std::chrono::high_resolution_clock::now();-高精度を保証します
  • std::chrono::steady_clock::now();-リアルタイムで測定することを保証します
  • clock();-高精度を保証しますが、時間ではなくCPUサイクルを測定します
  • time(&t_start);-高精度ではありませんが、リアルタイムで測定します

1-例: http://ideone.com/SudWTM

_#include <stdio.h>
#include <time.h>
#include <thread>
#include <iostream>
#include <chrono>

int main(void) {

    std::cout << "sleep(3) took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now(); 

    std::this_thread::sleep_for(std::chrono::seconds(3));

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    return 0;
}
_

G ++での結果(Debian 4.9.2-10)4.9.2:clock()= 0.00秒

_sleep(3) took: 

highres = 3.00098 s 
steady = 3.00098 s 
clock() = 0.00 seconds 
time() = 3.00 seconds 
_

C++ MSVS 2013 v120(Windows 7x64)での結果:

_sleep(3) took:

highres = 3.00017 s
steady = 3.00017 s
clock() = 3.00 seconds
time() = 3.00 seconds
_

2-2番目の例OpenMPまたは_<thread>_: http://coliru.stacked-crooked.com/a/2922c85385d197e1

_#include <stdio.h>
#include <time.h>
#include <thread>
#include <iostream>
#include <chrono>
#include <vector>

int main(void) {

    std::cout << "for-loop took: \n\n";

    clock_t c_start, c_end;
    time_t t_start, t_end;
    std::chrono::high_resolution_clock::time_point h_start, h_end;
    std::chrono::steady_clock::time_point steady_start, steady_end;

    time(&t_start);  // less precise than clock() but always get the real actual time
    c_start = clock(); // clock() get only CPU-time, it can be more than real or less - sleep(3); took 0.00 seconds 
    h_start = std::chrono::high_resolution_clock::now();
    steady_start = std::chrono::steady_clock::now();

    #pragma omp parallel num_threads(10)
    {
        for (volatile int i = 0; i < 200000000; ++i);
    }

    steady_end = std::chrono::steady_clock::now();
    h_end = std::chrono::high_resolution_clock::now();
    c_end = clock();
    time(&t_end);

    std::cout << "highres = " << std::chrono::duration<double>(h_end - h_start).count() << " s \n";
    std::cout << "steady = " << std::chrono::duration<double>(steady_end - steady_start).count() << " s \n";

    printf("clock() = %.2lf seconds \n", (c_end - c_start) / (double)CLOCKS_PER_SEC);
    printf("time() = %.2lf seconds \n", difftime(t_end, t_start));

    int b = getchar();

    return 0;
}
_

G ++での結果(Debian 4.9.2-10)4.9.2:clock()= 1.35秒

_for-loop took: 

highres = 0.213906 s 
steady = 0.213905 s 
clock() = 1.35 seconds 
time() = 0.00 seconds 
_

C++ MSVS 2013 v120(Windows 7x64)での結果:

_for-loop took:

highres = 1.49109 s
steady = 1.49109 s
clock() = 1.49 seconds
time() = 2.00 seconds
_

履歴書:

  1. スレッドがスリープ状態になると、g ++ 4.9.2のclock()は他の関数とは異なり時間を測定しません。

  2. OpenMPまたは_<thread>_( link )を使用してマルチスレッドを使用する場合、g ++ 4.9.2のclock()はすべてのスレッドのCPUサイクルを測定します。

また、Windows MSVS 2013では、どちらの場合もclock()はリアルタイムで測定する必要がありますが、これはclock()が他のプラットフォームでも同じように測定することを保証するものではありません(Linuxではg ++はスリープに対して0であり、マルチスレッドの場合はx-fold)。

これに基づいて、Windows MSVS2013とg ++ 4.9.2の両方でstd::chrono::high_resolution_clock::now();が必要なリアルタイムを測定する場合、これは他のすべてのプラットフォームで実際の高解像度時間を測定することを保証し、保証するかどうかを確認します標準C++ 11/14?

18
Alex

簡単な答え:C++ 14標準の時点では、high_resolution_clockはあなたが探している保証を明示的に提供していません。

今のところ、steady_clocksystem_clockは、より適切でより明確な保証を提供します。ただし、ほとんどの実装おそらくスレッドがスリープしている間にHRCが確実に進むようにします。それでも、独自のタイプエイリアスを実行する方が望ましい場合があります。以下の「編集」セクションとコメントの説明を参照してください。

長い答え:

ドラフト標準 は、実際には、Clockオブジェクトがnotであることを暗黙的に確認します(注30.2.4「タイミング仕様」の注5)。 -)関連するスレッドがスリープしている間に進む必要があります。コンテキストとして、このセクションでは、標準ライブラリのタイマーオブジェクトがどのように機能するかを説明します。タイマーの動作は、タイマーの設定に使用されるクロックの動作に基づいています。

[注:クロックが安定したクロック(CPUタイムクロックなど)と同期していない場合、これらのタイムアウトは有用な機能を提供しない可能性があります。 — エンドノート]

この場合、「タイムアウトは有用な機能を提供しない可能性がある」ということは、タイマーを使用して特定のクロック時間をsleep_untilする場合非同期(非リアルタイム)クロックを使用する、スレッドを意味することに注意してください。 ウェイクアップしません。したがって、上記の注記は少し控えめな表現です。

そして、実際、クロック仕様(20.13.3)には、安定したクロックとの同期を実際に必要とするものはありません。

ただし、この標準は、20.13.7.3の定義でhigh_resolution_clockの2つの潜在的なエイリアスを暗黙的に容認しているようです。

high_resolution_clockは、system_clockまたはsteady_clockの同義語である可能性があります。

steady_clockは、もちろん安定しています。 system_clocknotです。これは、システム時刻が変更される可能性があるためです(たとえば、NTP更新の結果として) )プログラムの実行中。

ただし、system_clock(20.13.7.1)isは依然として「リアルタイム」クロックです。

クラスsystem_clockのオブジェクトは、システム全体のリアルタイムクロックからの実時間を表します。

したがって、system_clockは、スレッドがスリープしているときに進行を停止しません。これは、時計が期待どおりに動作する場合でも、is_steadyhigh_resolution_clockに対してfalseである可能性があるというNicol Bolasの指摘を裏付けています(つまり、関連付けられたスレッドの状態に関係なく進みます)。

これに基づいて、ほとんどの主流の実装がhigh_resolution_clockにある種のリアルタイム(つまり同期された)クロックを使用することを期待することは合理的であるように思われます。結局のところ、実装は便利になるように設計されており、時計は、リアルタイムでない場合、特に上記の「便利な機能」に関する注記のようにタイマーとともに使用される場合、一般的にあまり役に立ちません。

ただし、guaranteedではないため、使用する各実装の動作やドキュメントを確認する必要があります。

編集:私はこの問題について ISO C++標準グループに関する議論 を開始しました。これはバグであることを示唆しています。標準。ハワード・ヒナントからの最初の返信は、それを標準にしたことで評価されていますin標準ですが、引用する価値があります。

私はhigh_resolution_clockの非推奨に反対するつもりはなく、適切な非推奨期間の後にそれを削除するつもりです。現実には、それは常にsteady_clockまたはsystem_clockのいずれかのtypedefであり、プログラマーはhigh_resolution_clockを選択していくつかを取得するよりも、これら2つのうちの1つを選択して何が得られるかを知っている方がよいでしょう。サイコロを振って他の時計。

...したがって、Hinnantによると、道徳はhigh_resolution_clockを使用しないでください。

編集2:

Hinnantによるとhigh_resolution_clockの問題は、HRCの問題に遭遇する可能性が高いほどではありません(ただし、上記の引数のように、準拠するコンパイラでもis可能です) )、ただし、通常、他の2つのクロックのいずれかよりも実際には低い解像度が得られないため(ただし、「最大解像度」を取得するには、type-aliasまたはtypedefでそれらの解像度を手動で比較する必要があります) 「眠っていない時計)、具体的なメリットはありません。したがって、適合した実装でスレッドが永久にスリープするリスクと、名前high_resolution_clockのセマンティックな利点、および独自のtypedefまたはtype-aliasを作成しないことによる単純/簡潔な利点を比較検討する必要があります。

さまざまなアプローチの実際のコードを次に示します。

  • static_assertを使用してcheckhigh_resolution_clockが実際に実際のクロックにエイリアスされているかどうか。これはおそらく決して起動しません。つまり、独自のtypedefをいじることなく、最高解像度の「リアルタイム」クロックを自動的に取得します。

     static_assert(
          std::is_same<high_resolution_clock, steady_clock>::value
       || std::is_same<high_resolution_clock, system_clock>::value,
       "high_resolution_clock IS NOT aliased to one of the other standard clocks!");
    
  • high_resolution_clock::is_steadyがtrueの場合は、HRCを使用します。それ以外の場合は、system_clocksteady_clockの間の高解像度クロックを優先します。 [〜#〜]注[〜#〜]high_resolution_clock::is_steadyがfalseの場合、これはおそらく HRCがsystem_clockにエイリアスされていること。この場合、最終的には新しいタイプエイリアスになります。これは実際にはhigh_resolution_clockと同じタイプです。ただし、独自のタイプエイリアスを作成すると、これが明示的になり、悪意のあるが準拠している実装でも上記の問題が発生しないことが保証されます。

    using maxres_sys_or_steady =
        std::conditional<
            system_clock::period::den <= steady_clock::period::den,
            system_clock, steady_clock
          >::type;
    using maxres_nonsleeping_clock =
        std::conditional<
            high_resolution_clock::is_steady,
            high_resolution_clock, maxres_sys_or_steady
          >::type;
    
20
Kyle Strand

標準では、この動作をクロックから指定していません。ではない正確に。

時計にはis_steady静的プロパティがあり、これを確認できます。 is_steadyがtrueを返すクロックcannotは、スレッドをスリープ状態にしたという理由だけで実行を停止する種類のクロックです。ただし、その値がfalseであるクロックは、さまざまな理由で非定常である可能性があります。システム時刻が変わると変わる掛け時計なので、安定しない場合があります。または、ティック間の期間が正確な数ではなく平均であるためです。

したがって、is_steadyは実際にはあなたの質問に答えません。

この規格ではhigh_resolution_clock::is_steadyは指定されていませんが、その質問に答えるために実装が必要です。安定している場合は、スレッドをスリープしてもクロックが停止しないことが保証されます。しかし、それが安定していない場合...あなたはまったく保証を得ることができません。

9
Nicol Bolas