Подтвердить что ты не робот

Измерение высокой точности С++ в Windows

Мне интересно измерять конкретный момент времени до наносекунды, используя С++ в Windows. Это возможно? Если это не так, возможно ли получить конкретное время в микросекундах как минимум?. Любая библиотека должна делать, если я не предполагаю, что это возможно с управляемым кодом. спасибо

4b9b3361

Ответ 1

Если у вас есть многопоточное приложение, запущенное на многоядерном компьютере, QueryPerformanceCounter может (и будет) возвращать разные значения в зависимости от того, на каком ядре выполняется код. См. эту статью MSDN. (rdtsc имеет ту же проблему)

Это не просто теоретическая проблема; мы столкнулись с этим с нашим приложением и пришли к выводу, что единственным надежным источником времени является timeGetTime, который имеет только точность ms (что, к счастью, было в нашем случае). Мы также попытались установить привязанность потока к нашим потокам, чтобы гарантировать, что каждый поток всегда получает согласованное значение от QueryPerformanceCounter, это сработало, но это абсолютно убило производительность в приложении.

Подводя итог, нет надежного таймера в окнах, который можно использовать во времени с точностью до микросекунды (по крайней мере, не при работе на многоядерном компьютере).

Ответ 2

В 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 должен быть в секундах.

Ответ 3

В будущем, с Windows Vista, 2008 и выше, Windows требует аппаратную поддержку "HPET". Это работает независимо от процессора и его часов и частоты. Можно получить времена с точностью до субмикросекунды.

Чтобы реализовать это, вам нужно использовать QPC/QPF. Проблема в том, что QPF (частота) является значением NOMINAL, поэтому использование необработанных вызовов приведет к временным сдвигам, которые могут превышать минуты в день. Для этого необходимо измерить фактическую частоту и проверить ее дрейф с течением времени, так как это повлияет на тепло и другие физические условия работы.

Статья, описывающая это, можно найти в MSDN (около 2004!) по этой ссылке. http://msdn.microsoft.com/en-us/magazine/cc163996.aspx

Я сам реализовал что-то подобное этому (и только что нашел ссылку выше!), но предпочитаю не использовать "микросекундное время", потому что сам вызов QPC довольно длинный по сравнению с другими вызовами Windows, такими как GetSystemTimeAsFileTime, а синхронизация добавляет больше накладных расходов. Поэтому я предпочитаю использовать миллисекундные временные метки (примерно на 70% меньше времени вызова, чем при использовании QPC), особенно когда я пытаюсь получить время сотни тысяч раз в секунду.

Ответ 4

Наилучшим выбором являются функции QueryPerformanceCounter и QueryPerformanceFrequency.

Недавно Microsoft (2014) выпустила более подробную информацию о QueryPerformanceCounter:

Подробнее см. Приобретение штампов с высоким разрешением (MSDN 2014).

Это всеобъемлющая статья с большим количеством примеров и подробного описания. A должен читать для пользователей QPC.

Ответ 5

MSDN утверждает, что -

Объект сценария - это очень точный таймер, который регистрирует события ETW (Трассировка событий для Windows) при запуске и остановке. Он разработан для использования в контрольно-измерительных приборах и в версиях С# и С++.... Как правило, на современном аппаратное обеспечение, вызов Begin() или End() принимает порядок микросекунды, и результирующие временные метки являются точными до 100 нс (т.е. 0,1 микросекунды).... Версии доступны как для .NET 3.5 (написанные на С#), так и для родного С++ и выполняются как на x86, так и на x64 платформ. Класс Scenario был первоначально разработан с использованием Visual Studio 2008, но теперь он предназначен для разработчиков, использующих Visual Studio 2010].

Из Главная страница сценария. Насколько я знаю, он был предоставлен теми же людьми, что и PPL.

Addionaly вы можете прочитать это часы и таймеры высокого разрешения для измерения производительности в Windows.

Ответ 6

Я думаю, что микросекунды немного необоснованны (без аппаратной поддержки). Миллисекунды выполнимы, но даже тогда не так точно из-за различных неприятных проблем с разрешениями. Несмотря на это, я включаю свой собственный класс таймера (на основе 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, поэтому он такой же, но портативный.

Ответ 7

Вы можете использовать API счетчиков производительности, как предлагал Конрад Рудольф, но следует предупредить, что он основан на частоте процессора. Эта частота нестабильна, когда, например, режим энергосбережения включен. Если вы хотите использовать этот API, убедитесь, что CPU находится на постоянной частоте.

В противном случае вы можете создать какую-то "статистическую" систему, сопоставляющую тики процессора с часами BIOS компьютера. Последнее менее точное, но постоянное.

Ответ 9

Если вы можете использовать компилятор Visual Studio 2012 или выше, вы можете использовать стандартную библиотеку std:: chrono.

#include <chrono>

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

Обратите внимание, что версия MSVC 2012 может быть точна точно в 1 мс. Новые версии должны быть точными до микросекунды.

Ответ 11

Что касается ответа Конрада Рудольфа, обратите внимание, что по моему опыту частота счетчика производительности составляет около 3,7 МГц, поэтому субмикросекунда, но, конечно, не наносекундная точность. Фактическая частота зависит от оборудования (и режима энергосбережения). Точность Nanosecond несколько необоснованна в любом случае, поскольку задержки прерываний и время переключения процесса/потока намного больше, чем это, и это также порядок отдельных команд машины.

Ответ 13

Вот класс Timer, который будет работать как для Windows, так и для Linux:

#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_

Ответ 14

Спасибо за вход... хотя я не мог получить nano или разрешение на микросекунду, что было бы неплохо, но я мог придумать это... может быть, кто-то найдет это полезным.

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