Самая быстрая система временного разрешения - PullRequest
9 голосов
/ 02 июля 2010

Какую самую быструю систему синхронизации может использовать программист C / C ++?

Например:
time () даст секунды с 01 января 1970 00:00.
GetTickCount () в Windows будет указывать время в миллисекундах с момента запуска системы, но ограничено 49,7 днями (после этого оно просто обнуляется).

Я хочу получить текущее время или отметки времени запуска системы / приложения в миллисекундах.

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

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

Edit:

unsigned __int64 GetTickCountEx()
{
    static DWORD dwWraps = 0;
    static DWORD dwLast = 0;

    DWORD dwCurrent = 0;

    timeMutex.lock();

    dwCurrent = GetTickCount();
    if(dwLast > dwCurrent)
        dwWraps++;

    dwLast = dwCurrent;

    unsigned __int64 timeResult = ((unsigned __int64)0xFFFFFFFF * dwWraps) + dwCurrent;

    timeMutex.unlock();

    return timeResult;
}

Ответы [ 10 ]

15 голосов
/ 02 июля 2010

Для синхронизации текущая рекомендация Microsoft должна использовать QueryPerformanceCounter & QueryPerformanceFrequency.

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

Вот небольшая статья Microsoft с примерами того, почему вы должны ее использовать :)

5 голосов
/ 02 июля 2010

У меня недавно был этот вопрос и я провел небольшое исследование.Хорошая новость заключается в том, что все три основные операционные системы имеют своего рода таймер высокого разрешения.Плохая новость заключается в том, что это разные вызовы API в каждой системе.Для операционных систем POSIX вы хотите использовать clock_gettime ().Однако, если вы работаете в Mac OS X, это не поддерживается, вы должны использовать mach_get_time ().Для окон используйте QueryPerformanceCounter.В качестве альтернативы, с компиляторами, которые поддерживают OpenMP, вы можете использовать omp_get_wtime (), но он может не обеспечивать разрешение, которое вы ищете.

Я также нашел цикл.h из fftw.org (www.fftw.org/cycle.h) будет полезен.

Вот код, который вызывает таймер в каждой ОС, используя некоторые уродливые операторы #ifdef.Использование очень просто: Таймер t;t.tic ();SomeOperation ();t.toc ( "Message");И он распечатает прошедшее время в секундах.

#ifndef TIMER_H
#define TIMER_H

#include <iostream>
#include <string>
#include <vector>

# if  (defined(__MACH__) && defined(__APPLE__))
#   define _MAC
# elif (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(_WIN64))
#   define _WINDOWS
#   ifndef WIN32_LEAN_AND_MEAN
#     define WIN32_LEAN_AND_MEAN
#   endif
#endif

# if defined(_MAC)
#    include <mach/mach_time.h>
# elif defined(_WINDOWS)
#    include <windows.h>
# else
#    include <time.h>
# endif


#if defined(_MAC)
  typedef uint64_t timer_t;
  typedef double   timer_c;

#elif defined(_WINDOWS)
  typedef LONGLONG      timer_t;
  typedef LARGE_INTEGER timer_c;

#else
  typedef double   timer_t;
  typedef timespec timer_c;
#endif

  //==============================================================================
  // Timer
  // A quick class to do benchmarking.
  // Example: Timer t;  t.tic();  SomeSlowOp(); t.toc("Some Message");

  class Timer {
  public:
    Timer();

    inline void tic();
    inline void toc();
    inline void toc(const std::string &msg);

    void print(const std::string &msg);
    void print();
    void reset();
    double getTime();

  private:
    timer_t start;
    double duration;
    timer_c ts;
    double conv_factor;
    double elapsed_time;
  };



  Timer::Timer() {

#if defined(_MAC)
    mach_timebase_info_data_t info;
    mach_timebase_info(&info);

    conv_factor = (static_cast<double>(info.numer))/
                  (static_cast<double>(info.denom));
    conv_factor = conv_factor*1.0e-9;

#elif defined(_WINDOWS)
    timer_c freq;
    QueryPerformanceFrequency(&freq);
    conv_factor = 1.0/(static_cast<double>freq.QuadPart);

#else
    conv_factor = 1.0;
#endif

    reset();
  }

  inline void Timer::tic() {

#if defined(_MAC)
    start = mach_absolute_time();

#elif defined(_WINDOWS)
    QueryPerformanceCounter(&ts);
    start = ts.QuadPart;

#else
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    start = static_cast<double>(ts.tv_sec) + 1.0e-9 *
            static_cast<double>(ts.tv_nsec);

#endif
  }

  inline void Timer::toc() {
#if defined(_MAC)
    duration =  static_cast<double>(mach_absolute_time() - start);

#elif defined(_WINDOWS)
    QueryPerformanceCounter(&qpc_t);
    duration = static_cast<double>(qpc_t.QuadPart - start);

#else
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    duration = (static_cast<double>(ts.tv_sec) + 1.0e-9 *
                static_cast<double>(ts.tv_nsec)) - start;

#endif

    elapsed_time = duration*conv_factor;
  }

  inline void Timer::toc(const std::string &msg) { toc(); print(msg); };

  void Timer::print(const std::string &msg) {
    std::cout << msg << " "; print();
  }

  void Timer::print() {
    if(elapsed_time) {
      std::cout << "elapsed time: " << elapsed_time << " seconds\n";
    }
  }

  void Timer::reset() { start = 0; duration = 0; elapsed_time = 0; }
  double Timer::getTime() { return elapsed_time; }


#if defined(_WINDOWS)
# undef WIN32_LEAN_AND_MEAN
#endif

#endif // TIMER_H
4 голосов
/ 13 июля 2012

GetSystemTimeAsFileTime - самый быстрый ресурс. Его гранулярность может быть получена вызовом GetSystemTimeAdjustment , который заполняет lpTimeIncrement . Системное время как файловое время имеет 100 нс единиц и увеличивается на TimeIncrement . TimeIncrement может варьироваться и зависит от настройки интерфейса мультимедийного таймера.

Вызов timeGetDevCaps раскроет возможности служб времени. Возвращается значение wPeriodMin для минимального поддерживаемого периода прерывания. Вызов timeBeginPeriod с wPeriodMin в качестве аргумента настроит систему для работы на максимально возможной частоте прерывания (обычно ~ 1 мс). Это также приведет к уменьшению приращения системного времени, возвращаемого GetSystemTimeAsFileTime. Его гранулярность будет в диапазоне 1 мс (10000 100 нс единиц).

Для вашей цели я бы предложил пойти на такой подход.

Выбор QueryPerformanceCounter сомнителен, поскольку его частота не Точна двумя способами: во-первых, оно отклоняется от значения, заданного QueryPerformanceFrequency аппаратным смещением. Это смещение может быть легко несколько сотен на миллион, что означает, что преобразование во время будет содержать ошибку в несколько сотен микросекунд в секунду. Во-вторых, у него есть тепловой дрейф. Дрейф таких устройств может легко составлять несколько частей на миллион. Таким образом, другой - тепловой зависимости - ошибка добавлено несколько us / s.

Так что, пока достаточно разрешения ~ 1 мс, а главный вопрос - это накладные расходы, GetSystemTimeAsFileTime - безусловно, лучшее решение.

Когда важны микросекунды, вам придется пойти более длинным путем и увидеть больше деталей. Услуги с точностью до миллисекунды описаны в Проекте отметок времени Windows

3 голосов
/ 02 июля 2010

Если вы просто беспокоитесь о переполнении GetTickCount(), вы можете просто обернуть его так:

DWORDLONG GetLongTickCount(void)
{
    static DWORDLONG last_tick = 0;
    DWORD tick = GetTickCount();

    if (tick < (last_tick & 0xffffffff))
        last_tick += 0x100000000;

    last_tick = (last_tick & 0xffffffff00000000) | tick;
    return last_tick;
}

Если вы хотите вызвать это из нескольких потоков, вам нужно заблокировать доступ кпеременная last_tick.Пока вы звоните GetLongTickCount() хотя бы раз в 49,7 дня, он будет обнаруживать переполнение.

1 голос
/ 02 июля 2010

Я бы рекомендовал использовать API GetSystemTimeAsFileTime , если вы специально ориентированы на Windows.Как правило, он быстрее, чем GetSystemTime и имеет ту же точность (что составляет около 10-15 миллисекунд - не смотрите на разрешение);когда я проводил тестирование несколько лет назад под Windows XP, он был где-то в диапазоне 50-100 раз быстрее.

Единственным недостатком является то, что вам, возможно, придется преобразовывать возвращаемые структуры FILETIME во время часов, используя, например, FileTimeToSystemTime , если вам нужно получить доступ к возвращенному времени в более удобном для человека формате.С другой стороны, если вам не нужны эти преобразованные времена в режиме реального времени, вы всегда можете делать это в автономном режиме или «ленивым» способом (например, только конвертировать метки времени, которые вам нужно отображать / обрабатывать, итолько когда они вам действительно нужны).

QueryPerformanceCounter может быть хорошим выбором, как уже упоминали другие, но накладные расходы могут быть довольно большими в зависимости от базовой аппаратной поддержки.В моем тесте, о котором я упоминал выше, вызовы QueryPerformanceCounter были в 25-200 раз медленнее, чем вызовы GetSystemTimeAsFileTime.Кроме того, существуют некоторые проблемы с надежностью, как, например, сообщалось здесь .

Итак, в заключение: если вы можете справиться с точностью до 10-15 миллисекунд, я бы порекомендовал вам использовать GetSystemTimeAsFileTime.Если вам нужно что-то лучшее, чем это, я бы пошел на QueryPerformanceCounter.

Небольшой отказ от ответственности: я не проводил никаких тестов в более поздних версиях Windows, чем XP SP3.Я бы порекомендовал вам самостоятельно провести сравнительный анализ.

0 голосов
/ 05 марта 2013

В Mac OS X вы можете просто использовать UInt32 TickCount (void), чтобы получить галочки.

0 голосов
/ 24 января 2011

Вы просматривали код в этой статье MSDN?

http://msdn.microsoft.com/en-us/magazine/cc163996.aspx

У меня есть этот код, компилируемый на 64-битной машине Windows 7 с использованием VC2005 и C ++ Builder XE, но при выполнении он блокирует мою машину; не отлажены достаточно далеко, чтобы понять, почему еще. Это кажется слишком сложным. Шаблоны шаблонов шаблонов УГ ...

0 голосов
/ 15 июля 2010

Если вы нацеливаетесь на достаточно позднюю версию ОС, тогда вы можете использовать GetTickCount64(), у которого точка охвата намного выше, чем GetTickCount(). Вы также можете просто создать версию GetTickCount64() поверх GetTickCount().

0 голосов
/ 02 июля 2010

POSIX поддерживает clock_gettime () , который использует struct timespec, который имеет наносекундное разрешение.Вопрос о том, поддерживает ли ваша система это детальное разрешение, является более спорным, но я считаю, что это стандартный вызов с самым высоким разрешением.Не все системы поддерживают его, и иногда он хорошо скрыт (библиотека '-lposix4' в Solaris, IIRC).


Обновление (2016-09-20):

  • Mac OS X 10.6.4 не поддерживала clock_gettime(), как и любая другая версия Mac OS X, вплоть до Mac OS X 10.11.6 El Capitan).Однако, начиная с macOS Sierra 10.12 (выпущена в сентябре 2016 г.), macOS наконец-то имеет функцию clock_gettime() и справочные страницы для нее.Фактическое разрешение (CLOCK_MONOTONIC) все еще составляет микросекунды;меньшие единицы - все нули.Это подтверждается clock_getres(), который сообщает, что разрешение составляет 1000 наносекунд, то есть 1 мкс.

Страница руководства для clock_gettime() в macOS Sierra упоминает mach_absolute_time() как способ получить высокоевремя разрешения.Для получения дополнительной информации, в частности, см. Технические вопросы и ответы QA1398: Единицы абсолютного времени Маха и (на SO) Что основано на mach_absolute_time() на iPhone?

0 голосов
/ 02 июля 2010

В Linux вы получаете микросекунды:

struct timeval tv;
int res = gettimeofday(&tv, NULL);
double tmp = (double) tv.tv_sec + 1e-6 * (double) tv.tv_usec;

В Windows доступны только миллисекунды:

SYSTEMTIME st;
GetSystemTime(&st);
tmp += 1e-3 * st.wMilliseconds;

return tmp;

Это пришло от R datetime.c(и был сокращен для краткости).

Тогда, конечно, есть Boost's Date_Time , который может иметь наносекундное разрешение в некоторых системах (подробности здесь и здесь).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...