За съжаление, няма универсално решение. Тук ще намерите някои от наличните опции.

Измерването на времето за изпълнение на C/C++ програма или на части от нея понякога е по-трудно, отколкото би трябвало, тъй като много методи често не са преносими към други платформи. Изборът на правилния метод ще зависи до голяма степен от вашата операционна система, версията на вашия компилатор, а също и от това какво имате предвид под „време“.

Тази статия включва изчерпателен списък с някои от най-добрите налични опции, заедно с ограниченията на всяка от тях. Надяваме се, че тук ще намерите една или повече опции, които можете да използвате за вашата програма.

Времето на стената срещу времето на процесора

Първо, важно е да се дефинират и разграничат тези два термина, които често се използват при измерване на времето за изпълнение.

  1. Време на стената (известно още като време на часовникаили време на стена) е просто общото време, изминало по време на измерването. Това е времето, което можете да измерите с хронометър, ако приемете, че можете да го стартирате и спрете точно в точките на изпълнение, които искате.
  2. CPU време, от друга страна, се отнася до времето, през което процесорът е бил зает с обработката на инструкциите на програмата. Времето, прекарано в изчакване за завършване на други неща (като I/O операции), не се включва във времето на процесора.

Коя от тези дефиниции трябва да използвате зависи от това защо измервате времето за изпълнение на вашата програма на първо място. В списъка по-долу повечето методи изчисляват само един от тези видове време, докато само няколко са в състояние да изчислят и двете. Също така важно е, че някои са налични както за потребители на Linux, така и за Windows, но други са ограничени до конкретна операционна система. За да улесня читателите, изрично изброих в началото на всеки раздел какъв вид време измерва всеки метод и на кои системи е наличен.

Бележки към примерите за код

Примерите за код, включени по-долу, са базирани на програми, които изчисляват приближение за безкрайната сума 1/2⁰ + 1/2¹ + 1/2² + 1/2³ + … = 2. Въпреки че 100 итерации на цикъла са достатъчно, за да стигнете до точната сума (поне на моята машина — резултатите може да варират на други платформи), тези програми изпълняват 1 милиард итерации, за да имат значително време за измерване.

В тези програми процесорът е зает почти 100% от времето, така че едва ли ще има разлика между времето на стената и времето на процесора. В случай, че искате да накарате процесора да не работи известно време по време на експериментите, можете лесно да го направите с функцията sleep() (достъпна в <unistd.h>).

Сега нека започнем с нашия списък.

1. Използване на командата „време“ на Linux

Работи на: Само Linux. (Това всъщност може да се използва за всяка програма, която можете да изпълните от терминала.)
Мерки: Както времето на стената, така и времето на процесора.

Добре, това всъщност не е C/C++ код. Но тъй като вероятно ще е достатъчно за много хора, работещи с Linux, реших да включа тази опция преди по-сложните. Ако просто искате да измерите процесора и/или времето на стената за вашата цяла програма, всъщност не е необходимо да променяте кода си за това. Просто напишете time преди това, което обикновено бихте написали, за да стартирате програмата си от командния ред на терминала. След това, когато вашата програма приключи с изпълнението, измерените времена ще се покажат на екрана. Като този:

$ time ./MyProgram
Result: 2.00000000000000000000 
 
real 0m5.931s 
user 0m5.926s 
sys 0m0.005s

В изхода „реално“ означава време на стената, а „потребител“ означава време на процесора, така че имате и двете измервания за цялата ви програма, без да променяте нито един ред код. Въпреки това, ако искате да измерите времето, необходимо на изолираните части на вашата програма, тогава ще ви трябва една от другите опции по-долу.

Забележка: Преди да напиша това, винаги съм предполагал, че Windows има своя собствена версия на командата time за своя команден ред, така че всъщност бях изненадан, когато разбрах, че не е така. Ако се интересувате, можете да намерите няколко алтернативи онлайн, но предполагам, че вграждането на измерванията на времето директно във вашия C/C++код трябва да бъде по-преносимо.

2. Използване на ‹chrono›

Работи на: Linux и Windows, но изисква C++11 или по-нова версия.
Измерва: Време на стена.

Това е може би най-добрият и преносим начин за измерване на времето на стената в днешно време, но е наличен само на C++11 и по-нови версии. Ако вашият проект/компилатор не поддържа C++11, ще ви трябва една от другите опции, изброени в тази статия.

Библиотеката <chrono> има достъп до няколко различни часовника във вашата машина, всеки от тях с различни цели и характеристики. Ако желаете, можете да получите повече подробности за всеки тип часовник тук. Но освен ако наистина не се нуждаете от различен часовник, бих препоръчал просто да използвате high_resolution_clock. Този използва часовника с най-високата налична резолюция, така че вероятно е подходящ за повечето хора. Ето как да го използвате:

Както можете да видите в ред 19, избрахме да преведем измереното време в наносекунди (въпреки че по-късно го преобразуваме в секунди). Ако предпочитате, можете да промените кода, за да използвате друга единица по ваш избор, като използвате chrono::hours, chrono::minutes, chrono::seconds, chrono::milliseconds или chrono::microseconds.

Забележка: Виждал съм хора да споменават, че измерването на времето за изпълнение с тази библиотека може да добави значително натоварване в сравнение с други C/C++ методи, особено когато се използва многократно в цикъл. Честно казано, не съм тествал или изпитвал това сам, така че не мога да кажа много за това. Ако смятате, че това е проблем за вас, може би трябва да помислите за някоя от другите опции по-долу.

3. С ‹sys/time.h› и gettimeofday()

Работи на: Linux и Windows.
Мерки: Време на стената.

Функцията gettimeofday() връща времето, изминало от 00:00:00 UTC на 1 януари 1970 г. (често наричано Епохално време). Трудното е, че функцията връща както броя секунди, така и броя микросекунди в отделни long int променливи, така че за да получите общото време, включително микросекунди, трябва съответно да сумирате и двете. Ето как да го направите:

Бележка 1: Ако не ви интересуват части от секунди, можете директно да получите изминалото време, като изчислите end.tv_sec - begin.tv_sec.
Бележка 2: Вторият аргумент от gettimeofday() се използва за указване на текущата часова зона. Тъй като изчисляваме изминалото време, часовите зони са без значение, при условие че се използва една и съща стойност както за begin, така и за end. Така използвахме нула и за двете повиквания.

4. С ‹time.h› и time()

Работи на: Linux и Windows.
Измерва: Време на стената, но измерва само цели секунди.

Функцията time() е подобна на gettimeofday(), тъй като връща времето, изминало от времето на епохата. Две основни разлики обаче: Първо, не можете да посочите часовата зона, така че винаги е UTC. Второ и най-важно, връща само пълни секунди. Следователно измерването на времето с този метод има смисъл само ако измерените интервали са по-дълги от няколко секунди. Ако мили-, микро- или наносекунди или микросекунди имат значение за вашите измервания, трябва да използвате един от другите методи. Ето как да го използвате:

Забележка: time_t всъщност е същото като long int, така че можете да го отпечатате директно с printf() или cout или лесно да го преобразувате в друг цифров тип по ваш избор.

5. Използване на ‹time.h› и clock()

Работи на: Linux и Windows.
Измерва: CPU време на Linux и време на стената на Windows.

Функцията clock() връща броя на часовниците от началото на изпълнението на програмата. Ако го разделите на константата CLOCKS_PER_SEC, ще получите колко време работи програмата в секунди. Това обаче ще има различни значения в зависимост от операционната система: В Linux ще получите време на процесора, докато в Windows ще получите време на стената. Така че трябва да бъдете много внимателни, когато използвате това. Ето кода:

Забележка: clock_t също е long int, така че трябва да го преобразувате в тип с плаваща запетая, преди да го разделите на CLOCKS_PER_SEC, или в противен случай ще получите целочислено деление.

6. С ‹time.h› и clock_gettime()

Работи на: Само Linux.
Измерва: И времето на стената, и времето на процесора.

Хубавото на този е, че можете да го използвате, за да измервате както времето на стената, така и времето на процесора. Той обаче е наличен само в Unix системи. Примерът по-долу измерва времето на стената, но можете да го промените, за да измерва времето на процесора, просто като замените константата CLOCK_REALTIME с CLOCK_PROCESS_CPUTIME_ID.

Забележка 1: В допълнение към CLOCK_REALTIME и CLOCK_PROCESS_CPUTIME_ID, има и други часовници, които можете да използвате с тази функция. Можете да проверите тази страница за по-пълен списък.
Забележка 2:Структурата timespec, използвана от тази функция, е много подобна на тази, използвана от gettimeofday() (метод #3 по-горе). Той обаче съдържа наносекунди вместо микросекунди, така че бъдете внимателни, когато преобразувате единиците.

7. С ‹sysinfoapi.h› и GetTickCount64()

Работи на: Само Windows.
Измерва: Време на стената.

Функцията GetTickCount64() връща броя милисекунди от стартирането на системата. Има и 32-битова версия (GetTickCount()), но тя е ограничена до 49,71 дни, така че е малко по-безопасно да използвате 64-битовата. Ето как да го използвате:

8. С ‹processthreadsapi.h› и GetProcessTimes()

Работи на: Само Windows.
Измерва: CPU време.

Това е най-сложният метод в списъка, но е единственият в него, който може да се използва за измерване на процесорното време в Windows. Няма да навлизам в подробности как работи, защото е прекалено сложно и никога не съм го използвал сам, но можете да погледнете в официалната документация за повече подробности. Ето кода:

Забележка:Адаптирах този код от „Отговор при препълване на стека“, така че бих искал да отдам всички заслуги на Александър Йи, потребителят, който е публикувал отговора там. Всъщност там той описва хубав преносим начин за изчисляване както на стената, така и на процесорното време на Linux и Windows машини с #ifdef макроси, така че може да искате да проверите пълния отговор и там.

Последни мисли

Е, ето ви го: много начини за измерване на времето за изпълнение на C/C++. Както можете да видите, няма универсално решение: всички методи по-горе имат ограничения и нито един от тях не може да изчисли както времето на стената, така и времето на процесора и е наличен както за Linux, така и за Windows. Въпреки това очаквам поне един от тези методи да работи за вашия код и за това, към което се стремите. Благодаря ви, че прочетохте.

Предложена литература

  • „Езикът за програмиране C++“, от Bjarne Stroustrup.
  • „C++ High Performance: Овладейте изкуството да оптимизирате функционирането на вашия C++ код“, от Björn Andrist и Viktor Sehr.

Още от същия автор







Ресурси

Ако имате нужда от допълнителна информация, по-долу ще намерите по-подробна документация за всеки метод, изброен в тази статия:

  1. Линукс команда ‘Time’
  2. ‹chrono
  3. gettimeofday()
  4. време ()
  5. "часовник()"
  6. clock_gettime()
  7. GetTickCount64()
  8. GetProcessTimes()

Разкриване: Тази публикация съдържа една или повече връзки от програмата на Amazon Services LLC Associates. Като партньор получавам комисионни за покупки, направени чрез тези връзки, без допълнителни разходи за клиента.