2009-09-28 2 views
66

Я ищу реализовать простой механизм таймера в C++. Код должен работать в Windows и Linux. Разрешение должно быть максимально точным (точность не менее миллисекунды). Это будет использоваться для простого отслеживания времени, а не для реализации какого-либо проекта, управляемого событиями. Каков наилучший инструмент для этого?C++ Кросс-платформенный таймер с высоким разрешением

+3

Будьте более конкретным. Вы выбираете вызов функции или хотите получить какой-то сигнал после определенного периода времени. Это «простые» приложения таймера, но они реализованы по-разному. Обратите внимание: использование «простых» в кавычках: синхронизация в компьютерах общего назначения никогда не бывает «простой». – jmucchiello

+0

C версия http://stackoverflow.com/questions/361363/how-to-measure-time-in-milliseconds-using-ansi-c –

ответ

37

Для C++ 03:

Boost.Timer может работать, но зависит от функции C clock и поэтому может не иметь достаточно хорошего разрешения для вас.

Boost.Date_Time включает в себя ptime class, который ранее был рекомендован для переполнения стека. См. Его документы на microsec_clock::local_time и microsec_clock::universal_time, но обратите внимание на его предупреждение о том, что «системы Win32 часто не достигают разрешения микросекунд через этот API».

STLsoft предлагает, среди прочего, тонкие кросс-платформенные (Windows и Linux/Unix) C++-оболочки вокруг API-интерфейсов, специфичных для ОС. Его performance library имеет несколько классов, которые будут делать то, что вам нужно. (Чтобы сделать это кросс-платформенный, выбрать класс, как performance_counter, который существует в обоих winstl и unixstl пространств имен, а затем использовать в зависимости от того пространства имен соответствует вашей платформы.)

Для C++ 11 и выше:

В библиотеке std::chrono встроена эта функциональность. Подробнее см. this answer от @HowardHinnant.

+5

Поскольку это известный вопрос/ответ, обновление может быть отличным. В частности, это может быть достигнуто стандартным и переносимым образом с использованием современных возможностей C++, таких как '' и' '? Если возможно, как? – Manu343726

2

Первый ответ на вопросы библиотеки C++, как правило, BOOST: http://www.boost.org/doc/libs/1_40_0/libs/timer/timer.htm. Делает ли это то, что вы хотите? Наверное, но это не начало.

Проблема в том, что вы хотите, чтобы портативные и таймерные функции не были универсальными в ОС.

4

StlSoft библиотека с открытым исходным кодом обеспечивает довольно good timer как на окнах, так и на платформах Linux. Если вы хотите, чтобы он реализовывался самостоятельно, просто посмотрите на их источники.

2

Я видел это реализовано несколько раз с закрытыми исходными растворами в доме .... которые все прибегали к #ifdef решений вокруг родных для Windows высоком разрешении таймеры, с одной стороны, и таймеры ядра Linux, использующие struct timeval (см. man timeradd), с другой стороны.

Вы можете абстрагироваться от этого, и несколько проектов с открытым исходным кодом сделали это - последний, на который я смотрел, был CoinOR class CoinTimer, но, несомненно, их больше.

+0

Я решил пойти по этому маршруту. Ваша ссылка была мертвой, поэтому я прокомментировал тот, который все еще работает: http://www.songho.ca/misc/timer/timer.html – Patrick

+0

Ahh, ничего похожего на комментарий по восьмилетнему вопросу :) У меня была удача тем временем с библиотекой [CCTZ] (https://github.com/google/cctz) из Google, которая основывается на некоторые новые идиомы C++ 11. –

3

Я очень порекомендуйте boost :: posix_time для этого. Он поддерживает таймеры в различных разрешениях до микросекунд. Я считаю, что

2

STLSoft имеют Performance Library, который включает в себя набор классов таймера, некоторые из которых работают как для UNIX, так и для Windows.

5

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

Несколько лет назад существует исчерпывающий article covering it in Dr. Dobb's, хотя он охватывает только те, что описаны в подпроекте WinSTL. STLSoft также предусматривает UNIX таймеров в UNIXSTL подпроекта, и вы можете использовать «PlatformSTL» один, который включает в UNIX или Windows, один, как соответствующий, например:

#include <platformstl/performance/performance_counter.hpp> 
#include <iostream> 

int main() 
{ 
    platformstl::performance_counter c; 

    c.start(); 
    for(int i = 0; i < 1000000000; ++i); 
    c.stop(); 

    std::cout << "time (s): " << c.get_seconds() << std::endl; 
    std::cout << "time (ms): " << c.get_milliseconds() << std::endl; 
    std::cout << "time (us): " << c.get_microseconds() << std::endl; 
} 

HTH

+0

Ссылка мертва кстати. – Tek

133

Обновленный ответ для старого вопроса:

в C++ 11 вы можете переносимо добраться до самого высокого разрешения таймера с:

#include <iostream> 
#include <chrono> 
#include "chrono_io" 

int main() 
{ 
    typedef std::chrono::high_resolution_clock Clock; 
    auto t1 = Clock::now(); 
    auto t2 = Clock::now(); 
    std::cout << t2-t1 << '\n'; 
} 

Пример вывода:

74 nanoseconds 

«chrono_io» - это расширение, облегчающее проблемы ввода-вывода с этими новыми типами и свободно доступное here.

Существует также реализация <chrono>, доступная в boost (все еще может быть на кончике ствола, не уверен, что она была выпущена).

Update

Это в ответ на комментарий Бена ниже, что последующий вызов std::chrono::high_resolution_clock занять несколько миллисекунд в VS11. Ниже приведено <chrono>-совместимое обходное решение. Однако это работает только на оборудовании Intel, вам необходимо погрузить в инлайн сборку (синтаксис, чтобы сделать это зависит от компилятора), и вы должны проводным тактовых машин в часы:

#include <chrono> 

struct clock 
{ 
    typedef unsigned long long     rep; 
    typedef std::ratio<1, 2800000000>   period; // My machine is 2.8 GHz 
    typedef std::chrono::duration<rep, period> duration; 
    typedef std::chrono::time_point<clock>  time_point; 
    static const bool is_steady =    true; 

    static time_point now() noexcept 
    { 
     unsigned lo, hi; 
     asm volatile("rdtsc" : "=a" (lo), "=d" (hi)); 
     return time_point(duration(static_cast<rep>(hi) << 32 | lo)); 
    } 

private: 

    static 
    unsigned 
    get_clock_speed() 
    { 
     int mib[] = {CTL_HW, HW_CPU_FREQ}; 
     const std::size_t namelen = sizeof(mib)/sizeof(mib[0]); 
     unsigned freq; 
     size_t freq_len = sizeof(freq); 
     if (sysctl(mib, namelen, &freq, &freq_len, nullptr, 0) != 0) 
      return 0; 
     return freq; 
    } 

    static 
    bool 
    check_invariants() 
    { 
     static_assert(1 == period::num, "period must be 1/freq"); 
     assert(get_clock_speed() == period::den); 
     static_assert(std::is_same<rep, duration::rep>::value, 
         "rep and duration::rep must be the same type"); 
     static_assert(std::is_same<period, duration::period>::value, 
         "period and duration::period must be the same type"); 
     static_assert(std::is_same<duration, time_point::duration>::value, 
         "duration and time_point::duration must be the same type"); 
     return true; 
    } 

    static const bool invariants; 
}; 

const bool clock::invariants = clock::check_invariants(); 

Так что не портативный. Но если вы хотите поэкспериментировать с часами с высоким разрешением на вашем собственном оборудовании Intel, он не станет более тонким, чем это. Несмотря на это, сегодняшние тактовые частоты могут динамически меняться (они на самом деле не являются постоянной времени компиляции). И с многопроцессорной машиной вы даже можете получать метки времени от разных процессоров. Но все же эксперименты на моем оборудовании работают достаточно хорошо.Если вы застряли с разрешением в миллисекундах, это может быть обходным путем.

Эти часы имеют длительность с точки зрения тактовой частоты вашего процессора (как вы сообщили об этом). То есть для меня эти часы гадают один раз каждые 1/2 800 000 000 секунд. Если вы хотите, вы можете преобразовать это в наносекунды (например) с:

using std::chrono::nanoseconds; 
using std::chrono::duration_cast; 
auto t0 = clock::now(); 
auto t1 = clock::now(); 
nanoseconds ns = duration_cast<nanoseconds>(t1-t0); 

Конверсия усечет фракцию цикла процессора для формирования наносекунды. Другие варианты округления возможны, но это другая тема.

Для меня это вернет длительность до 18 тактов, которая усекает до 6 наносекунд.

Я добавил некоторую «проверку инварианта» к вышеуказанным часам, наиболее важным из которых является проверка того, что clock::period подходит для машины. Опять же, это не переносимый код, но если вы используете эти часы, вы уже это сделали. Частная функция get_clock_speed(), показанная здесь, получает максимальную частоту процессора на OS X, и это должно быть то же число, что и константный знаменатель clock::period.

Это позволит вам немного отладить время, когда вы переносите этот код на новый компьютер и забудьте обновить clock::period до скорости вашей новой машины. Вся проверка выполняется либо во время компиляции, либо во время запуска программы. Таким образом, это не повлияет на производительность clock::now().

+1

В Visual Studio 11, к сожалению, кратчайший ненулевой интервал для 'high_resolution_clock' составляет несколько миллисекунд. – Petter

+3

Для меня потребовалось несколько секунд ... миллионы наносекунд на платформе, где тактовая частота составляет долю наносекунды. Вау!!! Я надеялся увидеть платформы, где измеряются фракции наносекунды. Я думал, что мои результаты в несколько десятков наносекунд не впечатляют. –

+0

Да, я действительно представил это как ошибку для Visual Studio 11. Однако, поскольку это не критично, я не ожидаю, что он будет исправлен в этой версии. – Petter

0

Если в проекте используется каркас Qt, лучшим решением является использование QElapsedTimer.

2

SDL2 имеет превосходный кросс-платформенный таймер с высоким разрешением. Если вам нужна субмиллисекундная точность, я написал очень маленькую кросс-платформенную библиотеку таймеров here. Совместим с C++ 03 и C++ 11/более поздними версиями C++.

0

Поздно к вечеринке здесь, но я работаю в устаревшей кодовой базе, которую нельзя обновить до C++ 11. Никто из нашей команды не имеет особого опыта работы на C++, поэтому добавление библиотеки, такой как STL, оказывается затруднительным (помимо потенциальных проблем, которые другие подняли о проблемах развертывания). Мне действительно нужен был чрезвычайно простой кросс-платформенный таймер, который мог бы жить сам по себе без чего-либо, кроме стандартных системных библиотек с голыми костями. Вот что я нашел:

http://www.songho.ca/misc/timer/timer.html

перепроведении весь источник здесь просто так не заблудиться, если сайт никогда не умирает:

////////////////////////////////////////////////////////////////////////////// 
// Timer.cpp 
// ========= 
// High Resolution Timer. 
// This timer is able to measure the elapsed time with 1 micro-second accuracy 
// in both Windows, Linux and Unix system 
// 
// AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html 
// CREATED: 2003-01-13 
// UPDATED: 2017-03-30 
// 
// Copyright (c) 2003 Song Ho Ahn 
////////////////////////////////////////////////////////////////////////////// 

#include "Timer.h" 
#include <stdlib.h> 

/////////////////////////////////////////////////////////////////////////////// 
// constructor 
/////////////////////////////////////////////////////////////////////////////// 
Timer::Timer() 
{ 
#if defined(WIN32) || defined(_WIN32) 
    QueryPerformanceFrequency(&frequency); 
    startCount.QuadPart = 0; 
    endCount.QuadPart = 0; 
#else 
    startCount.tv_sec = startCount.tv_usec = 0; 
    endCount.tv_sec = endCount.tv_usec = 0; 
#endif 

    stopped = 0; 
    startTimeInMicroSec = 0; 
    endTimeInMicroSec = 0; 
} 



/////////////////////////////////////////////////////////////////////////////// 
// distructor 
/////////////////////////////////////////////////////////////////////////////// 
Timer::~Timer() 
{ 
} 



/////////////////////////////////////////////////////////////////////////////// 
// start timer. 
// startCount will be set at this point. 
/////////////////////////////////////////////////////////////////////////////// 
void Timer::start() 
{ 
    stopped = 0; // reset stop flag 
#if defined(WIN32) || defined(_WIN32) 
    QueryPerformanceCounter(&startCount); 
#else 
    gettimeofday(&startCount, NULL); 
#endif 
} 



/////////////////////////////////////////////////////////////////////////////// 
// stop the timer. 
// endCount will be set at this point. 
/////////////////////////////////////////////////////////////////////////////// 
void Timer::stop() 
{ 
    stopped = 1; // set timer stopped flag 

#if defined(WIN32) || defined(_WIN32) 
    QueryPerformanceCounter(&endCount); 
#else 
    gettimeofday(&endCount, NULL); 
#endif 
} 



/////////////////////////////////////////////////////////////////////////////// 
// compute elapsed time in micro-second resolution. 
// other getElapsedTime will call this first, then convert to correspond resolution. 
/////////////////////////////////////////////////////////////////////////////// 
double Timer::getElapsedTimeInMicroSec() 
{ 
#if defined(WIN32) || defined(_WIN32) 
    if(!stopped) 
     QueryPerformanceCounter(&endCount); 

    startTimeInMicroSec = startCount.QuadPart * (1000000.0/frequency.QuadPart); 
    endTimeInMicroSec = endCount.QuadPart * (1000000.0/frequency.QuadPart); 
#else 
    if(!stopped) 
     gettimeofday(&endCount, NULL); 

    startTimeInMicroSec = (startCount.tv_sec * 1000000.0) + startCount.tv_usec; 
    endTimeInMicroSec = (endCount.tv_sec * 1000000.0) + endCount.tv_usec; 
#endif 

    return endTimeInMicroSec - startTimeInMicroSec; 
} 



/////////////////////////////////////////////////////////////////////////////// 
// divide elapsedTimeInMicroSec by 1000 
/////////////////////////////////////////////////////////////////////////////// 
double Timer::getElapsedTimeInMilliSec() 
{ 
    return this->getElapsedTimeInMicroSec() * 0.001; 
} 



/////////////////////////////////////////////////////////////////////////////// 
// divide elapsedTimeInMicroSec by 1000000 
/////////////////////////////////////////////////////////////////////////////// 
double Timer::getElapsedTimeInSec() 
{ 
    return this->getElapsedTimeInMicroSec() * 0.000001; 
} 



/////////////////////////////////////////////////////////////////////////////// 
// same as getElapsedTimeInSec() 
/////////////////////////////////////////////////////////////////////////////// 
double Timer::getElapsedTime() 
{ 
    return this->getElapsedTimeInSec(); 
} 

и заголовочный файл:

////////////////////////////////////////////////////////////////////////////// 
// Timer.h 
// ======= 
// High Resolution Timer. 
// This timer is able to measure the elapsed time with 1 micro-second accuracy 
// in both Windows, Linux and Unix system 
// 
// AUTHOR: Song Ho Ahn ([email protected]) - http://www.songho.ca/misc/timer/timer.html 
// CREATED: 2003-01-13 
// UPDATED: 2017-03-30 
// 
// Copyright (c) 2003 Song Ho Ahn 
////////////////////////////////////////////////////////////////////////////// 

#ifndef TIMER_H_DEF 
#define TIMER_H_DEF 

#if defined(WIN32) || defined(_WIN32) // Windows system specific 
#include <windows.h> 
#else   // Unix based system specific 
#include <sys/time.h> 
#endif 


class Timer 
{ 
public: 
    Timer();         // default constructor 
    ~Timer();         // default destructor 

    void start();        // start timer 
    void stop();        // stop the timer 
    double getElapsedTime();     // get elapsed time in second 
    double getElapsedTimeInSec();    // get elapsed time in second (same as getElapsedTime) 
    double getElapsedTimeInMilliSec();   // get elapsed time in milli-second 
    double getElapsedTimeInMicroSec();   // get elapsed time in micro-second 


protected: 


private: 
    double startTimeInMicroSec;     // starting time in micro-second 
    double endTimeInMicroSec;     // ending time in micro-second 
    int stopped;        // stop flag 
#if defined(WIN32) || defined(_WIN32) 
    LARGE_INTEGER frequency;     // ticks per second 
    LARGE_INTEGER startCount;     // 
    LARGE_INTEGER endCount;      // 
#else 
    timeval startCount;       // 
    timeval endCount;       // 
#endif 
}; 

#endif // TIMER_H_DEF 
Смежные вопросы