2009-12-01 5 views
30

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

+0

Ни в коем случае вы не можете получить точное время в наносекундах с операционной системой общего доступа времени в пользовательском пространстве. Не слушайте тех, кто дает вам функцию, возвращающую наносекунды - данные бесполезны. Модули ядра могут это сделать, и если у вас есть временные метки оборудования, то вы настроены. Вопрос нуждается в более подробной информации. –

ответ

33

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

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

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

+11

+1 для указания на малоизвестные подводные камни QueryPerformanceCounter. Высокопроизводительные таймеры в Windows просто не надежно существуют, что является гигантским PITA для некоторых систем. Сравните и сравните с системами на базе Unix, которые делают это с легкостью ... –

+10

Это все еще верно в 2013 году? http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx говорит * В общем, результаты счетчиков производительности согласованы во всех процессорах в многоядерных и многопроцессорных системах, даже если они измеряются в разных потоках или процессах. * –

+1

@MartinThompson Я не знаю, на самом деле, это было более четырех лет с тех пор, как я написал этот ответ, все могло измениться за это время. –

14

Windows имеет номер high-performance counter 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 должно быть через несколько секунд.

+1

Следует помнить, что технология SpeedStep от Intel может изменить PerformanceFrequency без уведомления вашего кода ... – xtofl

+0

@xtofl: правда, значения QPF, вероятно, не так уж точны (было хорошее описание риски где-то). Но повторные меры, как правило, усредняют это относительно хорошо. –

+1

QueryPerformanceFrequency/Coutner не обязательно использует системные часы именно по этой причине. Однако это означает, что он использует другой таймер с более низкой частотой. Таймер PIC, таймер ACPI или HPET на очень новых системах. См. Http://blogs.msdn.com/oldnewthing/archive/2007/07/05/3695356.aspx для некоторых деталей – shf301

2

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

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

0

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

+0

Не в моем опыте. См. [Rdtsc x86 инструкция] (http://x86.renejeschke.de/html/file_module_x86_id_278.html). Он считывает счетчик, обновляющий каждый такт ЦП. –

+0

@ JonasByström: API-интерфейс счетчика производительности Windows не использует TSC по описанным причинам [здесь] (http://en.wikipedia.org/wiki/Time_Stamp_Counter). TSC намного выше, но менее согласован. Также этот ответ составляет пять лет, и его содержание может или не может быть действительным. – Clifford

+0

@ JonasByström: Также в [этой дискуссии] (http://forums.devshed.com/programming-42/queryperformancefrequency-return-value-488039.html) видно, что результаты на моем ПК сильно отличались от тех вопроса в обсуждении. YMMV. Это полностью зависит от аппаратной реализации. – Clifford

0

rdtsc instruction является наиболее точным.

+0

На английском (хотя и с различным содержанием) http://en.wikipedia.org/wiki/Time_Stamp_Counter – Clifford

+0

Извините. Я не обращал внимания. Исправлено –

0

Вот класс 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_ 
0

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

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

Чтобы улучшить разрешение, вы можете добавить 'timeBeginPeriod (1)' и 'timeEndPeriod (1)' в 'Start()' и 'End()' соответственно. – mskfisher

6

Для справок в будущем, с 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), особенно когда я пытаюсь получить время сотни тысяч раз в секунду.

3

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

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

От Scenario Home Page. Насколько я знаю, он был предоставлен теми же людьми, что и PPL.

Addionaly вы можете прочитать это High Resolution Clocks and Timers for Performance Measurement in Windows.

5

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

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

См Acquiring high-resolution time stamps (MSDN 2014) для деталей.

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

1

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

#include <chrono> 

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

Обратите внимание, что MSVC 2012 version may be only 1ms accurate. Новые версии должны быть точными до микросекунды.

+0

Для лучшей переносимости вы можете вместо этого использовать 'std :: chrono :: high_resolution_clock' (это может быть или не быть таким же, как' stable_clock', но по крайней мере семантически это лучший выбор). – rustyx

2

Я думаю, что микросекунды немного необоснованны (без аппаратной помощи). Миллисекунды выполнимы, но даже тогда не так точно из-за различных неприятных проблем с разрешениями. Несмотря на это, я включил свой собственный класс таймера (на основе станд :: хроно) для рассмотрения:

#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, станд :: хроно :: high_resolution_clock использует QueryPerformanceCounter, так что это точно так же, но портативный.

+1

Вы один из немногих, кто сразу не выпрыгивает из системы '' с '.count()' и возвращает голый интегральный тип. +1 за это! :-) –

+0

Могу ли я использовать ваш код в коммерческом проекте? – Vertexwahn

+0

Вы, безусловно, знаете. – Robinson

Смежные вопросы