Самая быстрая система разрешения синхронизации

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

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

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

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

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

Править:

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;
}
9
задан Poni 16 July 2010 в 05:36
поделиться

7 ответов

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

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

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

15
ответ дан 4 December 2019 в 07:47
поделиться

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

Я также обнаружил, что cycle.h с сайта fftw.org (www.fftw.org/cycle.h) может оказаться полезным.

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

#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
5
ответ дан 4 December 2019 в 07:47
поделиться

Если вас беспокоит переполнение 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 дней, он обнаруживает переполнение.

3
ответ дан 4 December 2019 в 07:47
поделиться

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


Обновление (20.09.2016):

  • Mac OS X 10.6.4 не поддерживает clock_gettime () , как и никакая другая версия Mac OS X до Mac OS включительно. X 10.11.6 Эль-Капитан). Однако, начиная с 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
ответ дан 4 December 2019 в 07:47
поделиться

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

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

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

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

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

1
ответ дан 4 December 2019 в 07:47
поделиться

В 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's datetime.c (и было отредактировано для краткости).

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

0
ответ дан 4 December 2019 в 07:47
поделиться

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

0
ответ дан 4 December 2019 в 07:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: