web-dev-qa-db-ja.com

WindowsでのC ++高精度時間測定

WindowsでC++を使用して、ナノ秒までの特定の時点を測定することに興味があります。これは可能ですか?そうでない場合、少なくともマイクロ秒単位で特定の時間を取得することは可能ですか?マネージコードで可能だと思わない限り、どのライブラリでも実行できます。ありがとう

30
Kelly Elton

マルチコアコンピューターでスレッドアプリケーションを実行している場合、QueryPerformanceCounterは、コードが実行されているコアに応じて異なる値を返すことができます(そして返すでしょう)。 this MSDNの記事を参照してください。 (rdtscにも同じ問題があります)

これは単なる理論上の問題ではありません。私たちはアプリケーションでそれに遭遇し、唯一の信頼できるタイムソースはms精度しか持っていないtimeGetTimeであると結論付けなければなりませんでした(幸いなことにこのケースでは十分でした)。また、各スレッドがQueryPerformanceCounterから常に一貫した値を取得できるように、スレッドのスレッドアフィニティを修正しようとしましたが、これは機能しましたが、アプリケーションのパフォーマンスは完全に低下しました。

物事を要約すると、マイクロ秒の精度で物事を計るのに使用できるウィンドウにはreliableタイマーはありません(少なくとも、マルチコアコンピューター)。

34
Andreas Brinck

Windowsには 高性能カウンターAPI があります。

QueryPerformanceCounterからティックを取得し、QueryPerformanceFrequencyで提供されるプロセッサの周波数で除算する必要があります。

LARGE_INTEGER frequency;
if (::QueryPerformanceFrequency(&frequency) == FALSE)
    throw "foo";

LARGE_INTEGER start;
if (::QueryPerformanceCounter(&start) == FALSE)
    throw "foo";

// Calculation.


LARGE_INTEGER end;
if (::QueryPerformanceCounter(&end) == FALSE)
    throw "foo";

double interval = static_cast<double>(end.QuadPart - start.QuadPart) / frequency.QuadPart;

このintervalは秒単位でなければなりません。

14
Konrad Rudolph

将来の参照のために、Windows Vista、2008以降では、Windowsはハードウェアサポート「HPET」を必要とします。これは、CPUとそのクロックおよび周波数に依存せずに動作します。サブマイクロ秒の精度で時間を取得することが可能です。

これを実装するには、QPC/QPFを使用する必要があります。問題は、QPF(頻度)が公称値であるため、生の呼び出しを使用すると、1日あたり数分を超える可能性がある時間ドリフトが発生することです。これに対応するには、実際の周波数を測定し、熱やその他の物理的な動作条件が影響するため、時間の経過に伴うドリフトをチェックする必要があります。

これを説明する記事は、MSDN(2004年頃!)のこのリンクにあります。 http://msdn.Microsoft.com/en-us/magazine/cc163996.aspx

私はこれに似たものを自分で実装しました(そして今日上記のリンクを見つけました!)が、QPC呼び出し自体はGetSystemTimeAsFileTimeなどの他のWindows呼び出しに比べてかなり長く、同期はオーバーヘッドを追加するため、「マイクロ秒」を使用することを好みませんそのため、特に1秒あたり数十万回の時間を取得しようとする場合は、ミリ秒のタイムスタンプを使用することを好みます(QPCを使用するよりも呼び出し時間を約70%短縮します)。

6
Frank LaPiana

最良の選択は、関数QueryPerformanceCounterおよびQueryPerformanceFrequencyです。

マイクロソフトは最近(2014)、QueryPerformanceCounterに関するより詳細な情報をリリースしました。

詳細については、「 高解像度タイムスタンプの取得 (MSDN 2014)」を参照してください。

これは、多くの例と詳細な説明を含む包括的な記事です。 QPCのユーザーには必ずお読みください。

5
Arno

マイクロ秒は少し不合理だと思います(ハードウェアの支援がなければ)。ミリ秒は実行可能ですが、それでもさまざまな邪悪なカウンター解決の問題のためにそれほど正確ではありません。とにかく、私はあなたの考慮のために(std :: chronoに基づく)独自のタイマークラスを含めます:

#include <type_traits>
#include <chrono>


class Stopwatch final
{
public:

    using elapsed_resolution = std::chrono::milliseconds;

    Stopwatch()
    {
        Reset();
    }

    void Reset()
    {
        reset_time = clock.now();
    }

    elapsed_resolution Elapsed()
    {
        return std::chrono::duration_cast<elapsed_resolution>(clock.now() - reset_time);
    }

private:

    std::chrono::high_resolution_clock clock;
    std::chrono::high_resolution_clock::time_point reset_time;
};

Windowsの内部では、std :: chrono :: high_resolution_clockがQueryPerformanceCounterを使用しているため、まったく同じですが移植性があることに注意してください。

4
Robinson

[〜#〜] msdn [〜#〜] と主張する-

シナリオオブジェクトは、ETWイベント(Windowsのイベントトレース)を開始および停止したときに記録する非常に正確なタイマーです。パフォーマンスの計測とベンチマークに使用するように設計されており、C#とC++の両方のバージョンがあります。 ...現代のハードウェアの経験則として、Begin()またはEnd()の呼び出しは1マイクロ秒程度かかり、結果のタイムスタンプは100ns(0.1マイクロ秒)の精度です。 ...バージョンは、.NET 3.5(C#で記述)とネイティブC++の両方で利用可能であり、x86およびx64プラットフォームの両方で実行できます。 ScenarioクラスはもともとVisual Studio 2008を使用して開発されましたが、現在はVisual Studio 2010を使用する開発者を対象としています。]

シナリオのホームページ から。私の知る限り、それはPPLと同じ人々によって提供されました。

さらに、これを読むことができます Windowsでのパフォーマンス測定用の高解像度クロックおよびタイマー

3
SChepurin

新しいWindowsバージョンでは おそらくGetSystemTimePreciseAsFileTime です。 高解像度タイムスタンプの取得 を参照してください。

この多くは、ハードウェアとOSのバージョンに基づいてかなり残念な量に変化します。

2
Craig Ringer

Visual Studioコンパイラ2012以降を使用できる場合は、 std :: chrono 標準ライブラリを使用できます。

#include <chrono>

::std::chrono::steady_clock::time_point time = std::chrono::steady_clock::now();

MSVC 2012バージョンは1ミリ秒の精度しかありません であることに注意してください。新しいバージョンは、マイクロ秒まで正確でなければなりません。

1
SpamBot

can Konrad Rudolfが提案したようにパフォーマンスカウンターAPIを使用しますが、CPU周波数に基づいていることに注意してください。この周波数は、例えば省電力モードが有効になっています。このAPIを使用する場合は、CPUが一定の周波数であることを確認してください。

それ以外の場合は、CPUティックをPC BIOSクロックに相関させる、ある種の「統計」システムを作成できます。後者は、精度はずっと低いですが、一定です。

1
xtofl

Konrad Rudolphの回答に関して、私の経験では、パフォーマンスカウンターの周波数は約3.7MHzなので、サブマイクロ秒ですが、確かにナノ秒の精度ではないことに注意してください。実際の周波数はハードウェア(および省電力モード)に依存します。割り込みレイテンシとプロセス/スレッドコンテキストの切り替え時間はそれよりもはるかに長いため、ナノ秒の精度はどのような場合でも不合理です。これは個々のマシン命令の大きさのオーダーでもあります。

0
Clifford

rdtsc instruction が最も正確です。

0
Alexey Malistov

入力に感謝します...私はニースだったナノまたはマイクロ秒の解像度を得ることができませんでしたが、しかし、私はこれを思い付くことができました...多分他の誰かがそれを役に立つと思うでしょう。

    class N_Script_Timer
{
    public:
        N_Script_Timer()
        {
            running = false;
            milliseconds = 0;
            seconds = 0;
            start_t = 0;
            end_t = 0;
        }
        void Start()
        {
            if(running)return;
            running = true;
            start_t = timeGetTime();
        }
        void End()
        {
            if(!running)return;
            running = false;
            end_t = timeGetTime();
            milliseconds = end_t - start_t;
            seconds = milliseconds / (float)1000;
        }
        float milliseconds;
        float seconds;

    private:
        unsigned long start_t;
        unsigned long end_t;
        bool running;
};
0
Kelly Elton

QueryPerformanceCounter (Windowsの場合)を使用する

0
aJ.

WindowsとLinuxの両方で機能するTimerクラスを次に示します。

#ifndef INCLUDE_CTIMER_HPP_
#define INCLUDE_CTIMER_HPP_

#if defined(_MSC_VER)
#  define NOMINMAX // workaround a bug in windows.h
#  include <windows.h>
#else
#  include <sys/time.h>
#endif

namespace Utils
{
   class CTimer
   {
   private:
#     if defined(_MSC_VER)
         LARGE_INTEGER m_depart;
#     else
         timeval m_depart;
#     endif

   public:
      inline void start()
      {
#        if defined(_MSC_VER)
            QueryPerformanceCounter(&m_depart);
#        else
            gettimeofday(&m_depart, 0);
#        endif
      };

      inline float GetSecondes() const
      {
#        if defined(_MSC_VER)
            LARGE_INTEGER now;
            LARGE_INTEGER freq;

            QueryPerformanceCounter(&now);
            QueryPerformanceFrequency(&freq);

            return (now.QuadPart - m_depart.QuadPart) / static_cast<float>(freq.QuadPart);
#        else
            timeval now;
            gettimeofday(&now, 0);

            return now.tv_sec - m_depart.tv_sec + (now.tv_usec - m_depart.tv_usec) / 1000000.0f;
#        endif
      };
   };
}
#endif // INCLUDE_CTIMER_HPP_
0
Mathieu Pagé