Легко измерять прошедшее время
Я пытаюсь использовать time() для измерения различных точек моей программы.
Что я не понимаю, почему значения в до и после одинаковы? Я понимаю, что это не лучший способ профилировать мою программу, я просто хочу посмотреть, сколько времени займет что-то.
printf("**MyProgram::before time= %ld\n", time(NULL));
doSomthing();
doSomthingLong();
printf("**MyProgram::after time= %ld\n", time(NULL));
Я пробовал:
struct timeval diff, startTV, endTV;
gettimeofday(&startTV, NULL);
doSomething();
doSomethingLong();
gettimeofday(&endTV, NULL);
timersub(&endTV, &startTV, &diff);
printf("**time taken = %ld %ld\n", diff.tv_sec, diff.tv_usec);
Как я могу прочитать результат **time taken = 0 26339
? Означает ли это 26,339 наносекунд = 26,3 мсек?
Как насчет **time taken = 4 45025
, означает ли это 4 секунды и 25 мсек?
Ответы
Ответ 1
#include <ctime>
void f() {
using namespace std;
clock_t begin = clock();
code_to_time();
clock_t end = clock();
double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
}
Функция time()
работает только с точностью до секунды, но есть CLOCKS_PER_SEC
"часы" в течение секунды. Это простое переносное измерение, хотя оно упрощено.
Ответ 2
Вы можете абстрагировать механизм измерения времени, и каждый вызываемый период выполнения измеряется с помощью минимального дополнительного кода, просто путем вызова структуры таймера. Кроме того, во время компиляции вы можете параметризовать тип синхронизации (миллисекунды, наносекунды и т.д.).
Благодаря обзору Loki Astari и предложению использовать вариативные шаблоны.Это, поэтому вызов переадресованной функции.
#include <iostream>
#include <chrono>
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
template<typename F, typename ...Args>
static typename TimeT::rep execution(F&& func, Args&&... args)
{
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast< TimeT>
(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
int main() {
std::cout << measure<>::execution(functor(dummy)) << std::endl;
}
Demo
В соответствии с комментарием Howard Hinnant лучше не выходить из системы хронографа, пока нам не придется. Таким образом, вышеуказанный класс может дать пользователю возможность вызвать count
вручную, предоставив дополнительный статический метод (показан на С++ 14)
template<typename F, typename ...Args>
static auto duration(F&& func, Args&&... args)
{
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
return std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now()-start);
}
// call .count() manually later when needed (eg IO)
auto avg = (measure<>::duration(func) + measure<>::duration(func)) / 2.0;
и быть наиболее полезным для клиентов, которые
"хотите выполнить пост-обработку нескольких длительностей до ввода-вывода (например, среднего значения)"
Полный код можно найти здесь. Моя попытка создать инструмент сравнения производительности на основе хронографа записывается здесь.
Если доступно С++ 17 std::invoke
, вызов вызываемого в execution
может быть выполнен следующим образом:
invoke(forward<decltype(func)>(func), forward<Args>(args)...);
чтобы указать вызывающие элементы, которые являются указателями на функции-члены.
Ответ 3
//***C++11 Style:***
#include <chrono>
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::nanoseconds> (end - begin).count() << "[ns]" << std::endl;
Ответ 4
Как я вижу из вашего вопроса, похоже, что вы хотите узнать прошедшее время после выполнения какой-либо части кода. Думаю, вам будет удобно видеть результаты в секундах. Если да, попробуйте использовать функцию difftime()
, как показано ниже. Надеюсь, это решает вашу проблему.
#include <time.h>
#include <stdio.h>
time_t start,end;
time (&start);
.
.
.
<your code>
.
.
.
time (&end);
double dif = difftime (end,start);
printf ("Elasped time is %.2lf seconds.", dif );
Ответ 5
Только для Windows: (тег Linux был добавлен после отправки ответа)
Вы можете использовать GetTickCount(), чтобы получить количество миллисекунд, прошедших с момента запуска системы.
long int before = GetTickCount();
// Perform time-consuming operation
long int after = GetTickCount();
Ответ 6
функция времени (NULL) вернет количество секунд, прошедших с 01.01.1970 в 00:00. И поскольку эта функция вызывается в разное время в вашей программе, она всегда будет отличаться
Время в С++
Ответ 7
time(NULL)
возвращает количество секунд, прошедших с 01.01.1970 в 00:00 (Эпоха). Таким образом, разница между этими двумя значениями - это количество секунд, которое потребовала ваша обработка.
int t0 = time(NULL);
doSomthing();
doSomthingLong();
int t1 = time(NULL);
printf ("time = %d secs\n", t1 - t0);
Вы можете получить более точные результаты с помощью getttimeofday()
, которые возвращают текущее время в секундах, как time()
, а также в микросекундах.
Ответ 8
struct profiler
{
std::string name;
std::chrono::high_resolution_clock::time_point p;
profiler(std::string const &n) :
name(n), p(std::chrono::high_resolution_clock::now()) { }
~profiler()
{
using dura = std::chrono::duration<double>;
auto d = std::chrono::high_resolution_clock::now() - p;
std::cout << name << ": "
<< std::chrono::duration_cast<dura>(d).count()
<< std::endl;
}
};
#define PROFILE_BLOCK(pbn) profiler _pfinstance(pbn)
Использование ниже:
{
PROFILE_BLOCK("Some time");
// your code or function
}
Это похоже на RAII в области
ПРИМЕЧАНИЕ, это не мое, но я подумал, что это актуально здесь.
Ответ 9
#include<time.h> // for clock
#include<math.h> // for fmod
#include<cstdlib> //for system
#include <stdio.h> //for delay
using namespace std;
int main()
{
clock_t t1,t2;
t1=clock(); // first time capture
// Now your time spanning loop or code goes here
// i am first trying to display time elapsed every time loop runs
int ddays=0; // d prefix is just to say that this variable will be used for display
int dhh=0;
int dmm=0;
int dss=0;
int loopcount = 1000 ; // just for demo your loop will be different of course
for(float count=1;count<loopcount;count++)
{
t2=clock(); // we get the time now
float difference= (((float)t2)-((float)t1)); // gives the time elapsed since t1 in milliseconds
// now get the time elapsed in seconds
float seconds = difference/1000; // float value of seconds
if (seconds<(60*60*24)) // a day is not over
{
dss = fmod(seconds,60); // the remainder is seconds to be displayed
float minutes= seconds/60; // the total minutes in float
dmm= fmod(minutes,60); // the remainder are minutes to be displayed
float hours= minutes/60; // the total hours in float
dhh= hours; // the hours to be displayed
ddays=0;
}
else // we have reached the counting of days
{
float days = seconds/(24*60*60);
ddays = (int)(days);
float minutes= seconds/60; // the total minutes in float
dmm= fmod(minutes,60); // the rmainder are minutes to be displayed
float hours= minutes/60; // the total hours in float
dhh= fmod (hours,24); // the hours to be displayed
}
cout<<"Count Is : "<<count<<"Time Elapsed : "<<ddays<<" Days "<<dhh<<" hrs "<<dmm<<" mins "<<dss<<" secs";
// the actual working code here,I have just put a delay function
delay(1000);
system("cls");
} // end for loop
}// end of main
Ответ 10
Значения, напечатанные вашей второй программой, - это секунды и микросекунды.
0 26339 = 0.026'339 s = 26339 µs
4 45025 = 4.045'025 s = 4045025 µs
Ответ 11
#include <ctime>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <sys/time.h>
using namespace std;
using namespace std::chrono;
void f1()
{
high_resolution_clock::time_point t1 = high_resolution_clock::now();
high_resolution_clock::time_point t2 = high_resolution_clock::now();
double dif = duration_cast<nanoseconds>( t2 - t1 ).count();
printf ("Elasped time is %lf nanoseconds.\n", dif );
}
void f2()
{
timespec ts1,ts2;
clock_gettime(CLOCK_REALTIME, &ts1);
clock_gettime(CLOCK_REALTIME, &ts2);
double dif = double( ts2.tv_nsec - ts1.tv_nsec );
printf ("Elasped time is %lf nanoseconds.\n", dif );
}
void f3()
{
struct timeval t1,t0;
gettimeofday(&t0, 0);
gettimeofday(&t1, 0);
double dif = double( (t1.tv_usec-t0.tv_usec)*1000);
printf ("Elasped time is %lf nanoseconds.\n", dif );
}
void f4()
{
high_resolution_clock::time_point t1 , t2;
double diff = 0;
t1 = high_resolution_clock::now() ;
for(int i = 1; i <= 10 ; i++)
{
t2 = high_resolution_clock::now() ;
diff+= duration_cast<nanoseconds>( t2 - t1 ).count();
t1 = t2;
}
printf ("high_resolution_clock:: Elasped time is %lf nanoseconds.\n", diff/10 );
}
void f5()
{
timespec ts1,ts2;
double diff = 0;
clock_gettime(CLOCK_REALTIME, &ts1);
for(int i = 1; i <= 10 ; i++)
{
clock_gettime(CLOCK_REALTIME, &ts2);
diff+= double( ts2.tv_nsec - ts1.tv_nsec );
ts1 = ts2;
}
printf ("clock_gettime:: Elasped time is %lf nanoseconds.\n", diff/10 );
}
void f6()
{
struct timeval t1,t2;
double diff = 0;
gettimeofday(&t1, 0);
for(int i = 1; i <= 10 ; i++)
{
gettimeofday(&t2, 0);
diff+= double( (t2.tv_usec-t1.tv_usec)*1000);
t1 = t2;
}
printf ("gettimeofday:: Elasped time is %lf nanoseconds.\n", diff/10 );
}
int main()
{
// f1();
// f2();
// f3();
f6();
f4();
f5();
return 0;
}
Ответ 12
C++ std :: chrono имеет явное преимущество кроссплатформенности. Однако это также приводит к значительным накладным расходам по сравнению с POSIX clock_gettime(). На моем Linux-компьютере все std::chrono::xxx_clock::now()
работают примерно одинаково:
std::chrono::system_clock::now()
std::chrono::steady_clock::now()
std::chrono::high_resolution_clock::now()
Хотя POSIX clock_gettime(CLOCK_MONOTONIC, &time)
должен быть таким же, как steady_clock::now()
но это более чем в 3 раза быстрее!
Вот мой тест, для полноты.
#include <stdio.h>
#include <chrono>
#include <ctime>
void print_timediff(const char* prefix, const struct timespec& start, const
struct timespec& end)
{
double milliseconds = end.tv_nsec >= start.tv_nsec
? (end.tv_nsec - start.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec) * 1e3
: (start.tv_nsec - end.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec - 1) * 1e3;
printf("%s: %lf milliseconds\n", prefix, milliseconds);
}
int main()
{
int i, n = 1000000;
struct timespec start, end;
// Test stopwatch
clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < n; ++i) {
struct timespec dummy;
clock_gettime(CLOCK_MONOTONIC, &dummy);
}
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("clock_gettime", start, end);
// Test chrono system_clock
clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < n; ++i)
auto dummy = std::chrono::system_clock::now();
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("chrono::system_clock::now", start, end);
// Test chrono steady_clock
clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < n; ++i)
auto dummy = std::chrono::steady_clock::now();
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("chrono::steady_clock::now", start, end);
// Test chrono high_resolution_clock
clock_gettime(CLOCK_MONOTONIC, &start);
for (i = 0; i < n; ++i)
auto dummy = std::chrono::high_resolution_clock::now();
clock_gettime(CLOCK_MONOTONIC, &end);
print_timediff("chrono::high_resolution_clock::now", start, end);
return 0;
}
И это вывод, который я получаю при компиляции с gcc7.2 -O3:
clock_gettime: 24.484926 milliseconds
chrono::system_clock::now: 85.142108 milliseconds
chrono::steady_clock::now: 87.295347 milliseconds
chrono::high_resolution_clock::now: 84.437838 milliseconds
Ответ 13
Вызов функции time(NULL)
возвращает число секунд, прошедших с момента epoc: 1 января 1970 года. Возможно, вы хотите сделать разницу между двумя отметками времени:
size_t start = time(NULL);
doSomthing();
doSomthingLong();
printf ("**MyProgram::time elapsed= %lds\n", time(NULL) - start);
Ответ 14
Внутри функция получает доступ к системным часам, поэтому при каждом вызове она возвращает разные значения. В общем случае с нефункциональными языками в функциях может быть много побочных эффектов и скрытое состояние, которые вы не можете видеть, просто глядя на имя функции и аргументы.
Ответ 15
Из того, что видно, tv_sec хранит прошедшие секунды, в то время как tv_usec хранит микросекунды, прошедшие раздельно. И они не являются конверсиями друг друга. Следовательно, они должны быть изменены на правильную единицу и добавлены для достижения общего времени.
struct timeval startTV, endTV;
gettimeofday(&startTV, NULL);
doSomething();
doSomethingLong();
gettimeofday(&endTV, NULL);
printf("**time taken in microseconds = %ld\n",
(endTV.tv_sec * 1e6 + endTV.tv_usec - (startTV.tv_sec * 1e6 + startTV.tv_usec))
);
Ответ 16
В linux clock_gettime() является одним из хороших вариантов.
Вы должны связать библиотеку реального времени (-lrt).
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#define BILLION 1000000000L;
int main( int argc, char **argv )
{
struct timespec start, stop;
double accum;
if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) {
perror( "clock gettime" );
exit( EXIT_FAILURE );
}
system( argv[1] );
if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) {
perror( "clock gettime" );
exit( EXIT_FAILURE );
}
accum = ( stop.tv_sec - start.tv_sec )
+ ( stop.tv_nsec - start.tv_nsec )
/ BILLION;
printf( "%lf\n", accum );
return( EXIT_SUCCESS );
}
Ответ 17
Как уже отмечали другие, функция time() в стандартной библиотеке C не имеет разрешения лучше одной секунды. Похоже, единственной полностью переносимой функцией C, которая может обеспечить лучшее разрешение, является clock(), но она измеряет время процессора, а не время настенных часов. Если вы хотите ограничиться платформой POSIX (например, Linux), то функция clock_gettime() - хороший выбор.
Начиная с C++ 11, доступны гораздо лучшие средства синхронизации, которые предлагают лучшее разрешение в форме, которая должна быть очень переносимой для разных компиляторов и операционных систем. Точно так же библиотека boost :: datetime предоставляет хорошие классы синхронизации высокого разрешения, которые должны быть легко переносимыми.
Одной из проблем при использовании любого из этих средств является задержка, вызванная запросом системных часов. После экспериментов с clock_gettime(), boost :: datetime и std :: chrono эта задержка может легко составлять несколько микросекунд. Таким образом, при измерении длительности любой части вашего кода вы должны учитывать наличие ошибки измерения около этого размера или пытаться каким-то образом исправить эту нулевую ошибку. В идеале может потребоваться собрать несколько измерений времени, затраченного вашей функцией, и вычислить среднее или максимальное/минимальное время, затраченное на несколько прогонов.
Чтобы справиться со всеми этими проблемами переносимости и сбора статистики, я разрабатывал библиотеку cxx-rtimers, доступную на Github, которая пытается предоставить простой API для блоков синхронизации кода C++, вычисления нулевых ошибок и представления статистики из нескольких таймеры встроены в ваш код. Если у вас есть компилятор C++ 11, вы просто #include <rtimers/cxx11.hpp>
и используете что-то вроде:
void expensiveFunction() {
static rtimers::cxx11::DefaultTimer timer("expensiveFunc");
auto scopedStartStop = timer.scopedStart();
// Do something costly...
}
При выходе из программы вы получите сводную статистику по времени, записанную в std :: cerr, такую как:
Timer(expensiveFunc): <t> = 6.65289us, std = 3.91685us, 3.842us <= t <= 63.257us (n=731)
который показывает среднее время, его стандартное отклонение, верхний и нижний пределы и количество раз, когда эта функция была вызвана.
Если вы хотите использовать специфичные для Linux функции синхронизации, вы можете #include <rtimers/posix.hpp>
или, если у вас есть библиотеки Boost, но более старый компилятор C++, вы можете #include <rtimers/boost.hpp>
. Существуют также версии этих классов таймеров, которые могут собирать статистическую информацию о времени из разных потоков. Существуют также методы, позволяющие оценить нулевую ошибку, связанную с двумя непосредственно последовательными запросами системных часов.
Ответ 18
Мне нужно было измерить время выполнения отдельных функций в библиотеке. Я не хотел оборачивать каждый вызов каждой функции функцией измерения времени, потому что это уродливо и углубляет стек вызовов. Я также не хотел помещать код таймера в верхнюю и нижнюю часть каждой функции, потому что это создает беспорядок, когда функция может выйти рано или выдать, например, исключения. В итоге я создал таймер, который использует свое время жизни для измерения времени.
Таким образом, я могу измерить время простоя блока кода, просто создав экземпляр одного из этих объектов в начале рассматриваемого блока кода (функция или область действия), а затем позволяя деструктору экземпляров измерить время, прошедшее с момента строительство, когда экземпляр выходит за рамки. Вы можете найти полный пример здесь, но структура чрезвычайно проста:
template <typename clock_t = std::chrono::steady_clock>
struct scoped_timer {
using duration_t = typename clock_t::duration;
const std::function<void(const duration_t&)> callback;
const std::chrono::time_point<clock_t> start;
scoped_timer(const std::function<void(const duration_t&)>& finished_callback) :
callback(finished_callback), start(clock_t::now()) { }
scoped_timer(std::function<void(const duration_t&)>&& finished_callback) :
callback(finished_callback), start(clock_t::now()) { }
~scoped_timer() { callback(clock_t::now() - start); }
};
Структура перезвонит вам по предоставленному функтору, когда он выйдет из области видимости, чтобы вы могли что-то сделать с информацией о времени (распечатать или сохранить ее или что-то еще). Если вам нужно сделать что-то еще более сложное, вы можете даже использовать std::bind
с std::placeholders
для функций обратного вызова с большим количеством аргументов.
Вот краткий пример его использования:
void test(bool should_throw) {
scoped_timer<> t([](const scoped_timer<>::duration_t& elapsed) {
auto e = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(elapsed).count();
std::cout << "took " << e << "ms" << std::endl;
});
std::this_thread::sleep_for(std::chrono::seconds(1));
if (should_throw)
throw nullptr;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
Если вы хотите быть более взвешенным, вы также можете использовать new
и delete
чтобы явно запускать и останавливать таймер, не полагаясь на то, что он сделает это за вас.
Ответ 19
Они такие же, потому что ваша функция doSomething происходит быстрее, чем гранулярность таймера. Попробуйте:
printf ("**MyProgram::before time= %ld\n", time(NULL));
for(i = 0; i < 1000; ++i) {
doSomthing();
doSomthingLong();
}
printf ("**MyProgram::after time= %ld\n", time(NULL));
Ответ 20
Причина, по которой оба значения одинаковы, заключается в том, что ваша длительная процедура не занимает много времени, менее одной секунды. Вы можете попробовать просто добавить длинный цикл (для (int я = 0; я < 100000000; я ++);) в конце функции, чтобы убедиться, что это проблема, тогда мы можем перейти оттуда...
В случае, если вышеизложенное окажется правдой, вам нужно будет найти другую системную функцию (я понимаю, что вы работаете с linux, поэтому я не могу помочь вам с именем функции) для более точного измерения времени. Я уверен, что есть функция, аналогичная функции GetTickCount() в Linux, вам просто нужно ее найти.
Ответ 21
Обычно я использую следующее:
#include <chrono>
#include <type_traits>
using perf_clock = std::conditional<
std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock
>::type;
using floating_seconds = std::chrono::duration<double>;
template<class F, class... Args>
floating_seconds run_test(Func&& func, Args&&... args)
{
const auto t0 = perf_clock::now();
std::forward<Func>(func)(std::forward<Args>(args)...);
return floating_seconds(perf_clock::now() - t0);
}
Это то же самое, что и @nikos-athanasiou, за исключением того, что я избегаю использования нестационарных часов и использую плавающее число секунд в качестве продолжительности.
Ответ 22
В ответ на OP три конкретных вопроса.
"Я не понимаю, почему значения в до и после одинаковы?"
первый вопрос и пример кода показывают, что time()
имеет разрешение 1 секунду, поэтому ответ должен состоять в том, что две функции выполняются менее чем за 1 секунду. Но иногда он (по-видимому, нелогично) сообщает 1 секунду, если две метки таймера оседают на одну секунду.
В следующем примере используется gettimeofday()
, который заполняет эту структуру
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
и второй вопрос спрашивает: "Как я могу прочитать результат **time taken = 0 26339
? Означает ли это 26,339 наносекунд = 26,3 мсек?"
Мой второй ответ - это время, равное 0 секундам и 26339 микросекундам, то есть 0,026339 секунд, в котором первый пример выполняется менее чем за 1 секунду.
третий вопрос спрашивает: "Что насчет **time taken = 4 45025
, значит ли это 4 секунды и 25 мсек?"
Мой третий ответ - это время 4 секунды и 45025 микросекунд, то есть 4.045025 секунд, что показывает, что ОП изменил задачи, выполняемые двумя функциями, которые он ранее выполнял.
Ответ 23
Вы можете использовать SFML-библиотеку, которая является простой и быстрой мультимедийной библиотекой.
Он включает в себя множество полезных и четко определенных классов, таких как Clock, Socket, Sound, Graphics и т.д.
Он настолько прост в использовании и настоятельно рекомендуется.
Это пример этого вопроса.
sf::Clock clock;
...
Time time1 = clock.getElapsedTime();
...
Time time2 = clock.restart();