Час виконання програми С


209

У мене є програма C, яка має на меті працювати паралельно на декількох процесорах. Мені потрібно мати можливість записувати час виконання (який може бути від 1 секунди до декількох хвилин). Я шукав відповіді, але всі вони, здається, пропонують використовувати clock()функцію, яка потім включає обчислення кількості годин, яку програма взяла, поділену на Clocks_per_secondзначення.

Я не впевнений, як Clocks_per_secondрозраховується значення?

У Java я просто приймаю поточний час у мілісекундах до та після виконання.

Чи є в С подібне? Я придивився, але я не можу знайти спосіб отримати щось краще, ніж друга резолюція.

Я також знаю, що профілер може бути варіантом, але я прагну сам реалізувати таймер.

Дякую


3
які рамки ОС / API ви використовуєте / доступні? Просто звичайний C?
typo.pl

4
Це досить невелика програма, просто звичайний C
Роджер

Я детально писав про реалізацію портативного рішення у цій відповіді: stackoverflow.com/questions/361363/…
Олександр Саприкін

Час , необхідний для виконання повної функції stackoverflow.com/a/40380118/6180077
Абдулла Farweez

Відповіді:


344

CLOCKS_PER_SEC- константа, яка оголошена в <time.h>. Щоб отримати час процесора, який використовується завданням у програмі C, використовуйте:

clock_t begin = clock();

/* here, do your time-consuming job */

clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

Зауважте, що це повертає час як тип з плаваючою точкою. Це може бути точніше секунди (наприклад, ви вимірюєте 4,52 секунди). Точність залежить від архітектури; в сучасних системах ви легко отримуєте 10 мс і менше, але на старих машинах Windows (з епохи Win98) він був ближче до 60мс.

clock()є стандартним С; вона працює «скрізь». Існують специфічні для системи функції, такі як getrusage()у Unix-подібних системах.

Java System.currentTimeMillis()не вимірює те саме. Це "настінні годинники": він може допомогти вам виміряти, скільки часу знадобилося програмі для виконання, але він не говорить вам, скільки часу було використано процесора. У системах багатозадачності (тобто всі) вони можуть бути різними.


1
Це дає мені дуже випадковий результат - я отримую суміш великого / малого / негативного числа над тим самим фрагментом коду. GCC 4.7 Linux 3.2 AMD64

3
Так: clock()повертає час у деякій внутрішній шкалі, що називається "годинник", і CLOCKS_PER_SECце кількість годин за секунду, тому ділення на CLOCKS_PER_SECвихід дає час на секунди. У наведеному вище коді значення - це doubleтак, що ви можете його масштабувати за бажанням.
Томас Порнін

18
Велике попередження: clock () повертає кількість часу, витраченого ОС на ваш процес, а не фактичну кількість часу, що минув. Однак це добре для тимчасового блоку коду, але не для вимірювання часу, що минає в реальному світі.

2
Він сказав, що хоче виміряти багатопотокову програму. Я не впевнений, що годинник () підходить для цього, оскільки він підсумовує час роботи всіх потоків, тому результат буде схожий на те, якби код виконувався послідовно. Для таких речей я використовую omp_get_wtime (), але, звичайно, мені потрібно переконатися, що система не зайнята іншими процесами.
Youda008

1
Я мушу зазначити деякі речі, хоча ця нитка була більш актуальною рік тому: CLOCKS_PER_SECце long intзначення зі значенням 1000000, що дає час у мікросекундах, коли не ділиться; не тактові цикли процесора. Тому не потрібно враховувати динамічну частоту, оскільки тактовий час тут знаходиться в мікросекундах (можливо, тактові цикли для процесора 1 МГц?) Я зробив коротку програму для друку цього значення, і це було 1000000 на моєму ноутбуці i7-2640M, з динамічною частотою, що дозволяє від 800 МГц до 2,8 ГГц, навіть використовуючи Turbo Boost, щоб вийти до 3,5 ГГц.
DDPWNAGE

111

Якщо для запуску використовується оболонка Unix, ви можете використовувати команду time.

робити

$ time ./a.out

припустимо, що a.out як виконуваний файл дасть у час, необхідний для цього


3
@acgtyrant, але лише для простих програм, тому що це займе весь програмний час, включаючи введення, виведення тощо
phuclv

1
Якщо ви працюєте в Linux, і ви зменшили свій (мікро) орієнтир до програми з незначними накладними накладними витратами, наприклад, статичного виконуваного файлу, який запускає гарячу петлю протягом декількох секунд, ви можете використовувати perf stat ./a.outдля отримання лічильників продуктивності HW для пропусків кешу і галузеві непередбачення, і IPC.
Пітер Кордес

61

У звичайній ванілі С:

#include <time.h>
#include <stdio.h>

int main()
{
    clock_t tic = clock();

    my_expensive_function_which_can_spawn_threads();

    clock_t toc = clock();

    printf("Elapsed: %f seconds\n", (double)(toc - tic) / CLOCKS_PER_SEC);

    return 0;
}

6
Кращі імена змінних, які я бачив за деякий час. tic = "час у такт", toc = "тактовий час". Але також tic-toc = "тик-так". Ось як я маркую часові захоплення тут.
Логан Шелі

60

Ви функціонально бажаєте цього:

#include <sys/time.h>

struct timeval  tv1, tv2;
gettimeofday(&tv1, NULL);
/* stuff to do! */
gettimeofday(&tv2, NULL);

printf ("Total time = %f seconds\n",
         (double) (tv2.tv_usec - tv1.tv_usec) / 1000000 +
         (double) (tv2.tv_sec - tv1.tv_sec));

Зауважте, що це вимірюється в мікросекундах, а не лише секундах.


2
Компілятор MinGW заснований на GCC. Так воно і попрацює. Але якщо ви використовуєте візуальний компілятор C, то ви отримаєте помилку.
користувач2550754

11
Так, він буде працювати у Windows з бібліотекою змінного струму, яка підтримує дзвінок gettimeofday. Насправді не має значення, що таке компілятор, просто потрібно пов’язати його з гідною бібліотекою libc. Що у випадку з mingw - це не вікно за замовчуванням.
Уес Хардакер

1
Це працює для мене в Windows XP з cygwin gcc та Linux Ubuntu. Це саме те, що я хотів.
Любов і спокій - Джо Кодесвелл

gettimeofdayзастарілий і не рекомендується використовувати новий код. Навчальна його сторінка POSIX рекомендує замість clock_gettime clock_gettime , що дозволяє вам запитати, на CLOCK_MONOTONICщо не впливають зміни системного годинника, і, отже, краще як інтервал часу. (Дивіться відповідь ДжонаСлла ). Наприклад, у сучасних системах Linux, gettimeofday - це в основному обгортка для clock_gettime, яка перетворює наносекунди в мікросекунди.
Пітер Кордес

12

Більшість простих програм мають час обчислення в мілісекундах. Отже, я гадаю, вам це стане в нагоді.

#include <time.h>
#include <stdio.h>

int main(){
    clock_t start = clock();
    // Execuatable code
    clock_t stop = clock();
    double elapsed = (double)(stop - start) * 1000.0 / CLOCKS_PER_SEC;
    printf("Time elapsed in ms: %f", elapsed);
}

Якщо ви хочете обчислити час виконання всієї програми, і ви перебуваєте в системі Unix, запустіть свою програму, використовуючи команду time, як цеtime ./a.out


У Windows принаймні коефіцієнт принаймні 100, але не 1000, і не точно
боктул

6
Ця відповідь нічого , що не було в не додав Alexandre C «s відповіді від два роки раніше.
Джонатан Леффлер

3
@boctulus: 1s - це завжди 1000 мс, також у Windows.
алк

9

Дуже багато відповідей clock() а потім CLOCKS_PER_SECвід time.h. Це, мабуть, погана ідея, тому що так /bits/time.hговорить мій файл:

/* ISO/IEC 9899:1990 7.12.1: <time.h>
The macro `CLOCKS_PER_SEC' is the number per second of the value
returned by the `clock' function. */
/* CAE XSH, Issue 4, Version 2: <time.h>
The value of CLOCKS_PER_SEC is required to be 1 million on all
XSI-conformant systems. */
#  define CLOCKS_PER_SEC  1000000l

#  if !defined __STRICT_ANSI__ && !defined __USE_XOPEN2K
/* Even though CLOCKS_PER_SEC has such a strange value CLK_TCK
presents the real value for clock ticks per second for the system.  */
#   include <bits/types.h>
extern long int __sysconf (int);
#   define CLK_TCK ((__clock_t) __sysconf (2))  /* 2 is _SC_CLK_TCK */
#  endif

Так CLOCKS_PER_SEC може бути визначено як 1000000, залежно від того, які параметри ви використовуєте для компіляції, і, таким чином, це не здається хорошим рішенням.


1
Дякую за інформацію, але чи є ще якась альтернатива?
озанмуї

4
Це не є практичною проблемою: так, у системах Posix завжди є CLOCK_PER_SEC==1000000, але в той же час всі вони використовують 1-µс точність для своєї тактової () реалізації; до речі, вона має приємне властивість зменшувати проблеми спільного використання. Якщо ви хочете виміряти потенційно дуже швидкі події, скажімо, нижче 1 мс, то спочатку слід потурбуватися про точність (або роздільну здатність) функції clock (), яка в Posix обов'язково грубіша за 1 мкс, але також часто набагато грубіша; звичайне рішення - запустити тест багато разів; питання, як було задано, здається, не вимагає цього.
AntoineL

Чому це не було б хорошим рішенням? Ви отримуєте деяку цінність clock(), якщо ділити це значення з CLOCK_PER_SECвами гарантовано отримаєте час у секундах, що займає процесор. Відповідальність за вимірювання фактичної тактової частоти - це відповідальність clock()функції, а не ваша.
Заффі

9

Відповідь Томаса Порніна як макроси:

#define TICK(X) clock_t X = clock()
#define TOCK(X) printf("time %s: %g sec.\n", (#X), (double)(clock() - (X)) / CLOCKS_PER_SEC)

Використовуйте його так:

TICK(TIME_A);
functionA();
TOCK(TIME_A);

TICK(TIME_B);
functionB();
TOCK(TIME_B);

Вихід:

time TIME_A: 0.001652 sec.
time TIME_B: 0.004028 sec.

4

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

Знаючи, що спосіб отримання поточного часу в С може бути досягнутий різними способами, простішим є:

#include <time.h>

#define CPU_TIME (getrusage(RUSAGE_SELF,&ruse), ruse.ru_utime.tv_sec + \
  ruse.ru_stime.tv_sec + 1e-6 * \
  (ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec))

int main(void) {
    time_t start, end;
    double first, second;

    // Save user and CPU start time
    time(&start);
    first = CPU_TIME;

    // Perform operations
    ...

    // Save end time
    time(&end);
    second = CPU_TIME;

    printf("cpu  : %.2f secs\n", second - first); 
    printf("user : %d secs\n", (int)(end - start));
}

Сподіваюся, це допомагає.

З повагою!


4

(Усі відповіді тут відсутні, якщо ваш sysadmin змінює системний час, або ваш часовий пояс відрізняється зимовим та колись часовим. Тому ...)

На використання Linux: clock_gettime(CLOCK_MONOTONIC_RAW, &time_variable); це не впливає, якщо системний адміністратор змінить час, або ви живете в країні, в яку зимовий час відрізняється від літнього часу тощо.

#include <stdio.h>
#include <time.h>

#include <unistd.h> /* for sleep() */

int main() {
    struct timespec begin, end;
    clock_gettime(CLOCK_MONOTONIC_RAW, &begin);

    sleep(1);      // waste some time

    clock_gettime(CLOCK_MONOTONIC_RAW, &end);

    printf ("Total time = %f seconds\n",
            (end.tv_nsec - begin.tv_nsec) / 1000000000.0 +
            (end.tv_sec  - begin.tv_sec));

}

man clock_gettime констатує:

CLOCK_MONOTONIC
              Clock  that  cannot  be set and represents monotonic time since some unspecified starting point.  This clock is not affected by discontinuous jumps in the system time
              (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP.

Чи можете ви пояснити розрахунок, який використовували для отримання кількості секунд? Не очевидно, що відбувається.
Колін Кінан

1
Не завжди це (end.tv_nsec - begin.tv_nsec) / 1000000000.0призведе 0завжди?
алк

НЕ @alk: немає, поділів на doubleбуквальних тригера INT або longдля doubleперетворення до поділу. Звичайно, ви можете просто дотримуватись цілої і надрукувати tv_secчастину, а потім дробову частину з нулем, як %ld.%09ld, але перетворення в подвійне дуже просто, і 53 біти точності, як правило, достатньо для еталонних разів.
Пітер Кордес

1
(На жаль, віднімання наносекундної частини може знадобитися перенести у частину секунд, тому використання подвійного та наведення негативу дозволяє уникнути цієї проблеми. Щоб використовувати чистий рядок у цілому форматі, вам знадобиться timespec_subtractподібна timeval_subtractпропозиція в посібнику glibc : gnu.org/software/libc/manual/html_node/Elapsed-Time.html )
Пітер Кордес

3

ANSI C задає лише функції часу другої точності. Однак, якщо ви працюєте в середовищі POSIX, ви можете використовувати функцію gettimeofday (), яка забезпечує роздільну здатність мікросекунд часу, пройденого з епохи UNIX.

Як зауваження, я б не рекомендував використовувати clock (), оскільки він погано реалізований у багатьох (якщо не всіх?) Системах і не точний, крім того, що він стосується лише того, скільки часу ваша програма витратила на процесор і не загальний термін служби програми, який, відповідно до вашого запитання, - це те, що я вважаю, що ви хотіли б виміряти.


Стандарт ISO C (якщо це означає ANSI C ) навмисно не визначає точність функцій часу . Тоді конкретно щодо реалізації POSIX або для Windows точність функцій настінного годинника (див. Відповідь Томаса) здійснюється за секунди. Але точність тактової частоти () зазвичай більша, і завжди 1 мкс у Posix (незалежно від точності)
AntoineL

2

Кожне рішення не працює в моїй системі.

Я можу використати

#include <time.h>

double difftime(time_t time1, time_t time0);

2
Це дає різницю між двома time_tзначеннями як подвійне. Оскільки time_tзначення є точними лише до секунди, воно має обмежене значення для друку часу, зайнятого короткими запущеними програмами, хоча це може бути корисно для програм, які працюють протягом тривалих періодів.
Джонатан Леффлер

З будь-якої причини передача у пару clock_ts difftimeздається, що працює для мене з точністю до сотої секунди. Це на linux x86. Я також не можу отримати віднімання stopта startпрацювати.
ragerdl

@ragerdl: Вам потрібно перейти difftime() clock() / CLOCKS_PER_SEC, оскільки очікують секунди.
алк

2
    #include<time.h>
    #include<stdio.h>
    int main(){
clock_t begin=clock();

    int i;
for(i=0;i<100000;i++){
printf("%d",i);

}
clock_t end=clock();
printf("Time taken:%lf",(double)(end-begin)/CLOCKS_PER_SEC);
}

Ця програма буде працювати як шарм.


2

Я виявив, що звичайний годинник (), який всі рекомендують тут, чомусь дико відхиляється від запуску до запуску, навіть для статичного коду без будь-яких побічних ефектів, як малюнок на екрані чи читання файлів. Це може бути тому, що процесор змінює режими енергоспоживання, ОС дає різні пріоритети тощо.

Тож єдиний спосіб надійно отримувати один і той же результат кожен раз за допомогою годинника () - запускати вимірюваний код у циклі кілька разів (протягом декількох хвилин), вживаючи запобіжних заходів, щоб запобігти оптимізації компілятора: сучасні компілятори можуть попередньо обчислити код без побічних ефектів, що працюють у циклі, і перемістіть його з циклу, наприклад, використовуючи випадковий вхід для кожної ітерації.

Після того як достатня кількість зразків збирається в масив, один сортує цей масив і бере середній елемент, званий медіаною. Медіана краща за середню, тому що вона викидає екстремальні відхилення, як-от скажімо, антивірус, який піднімає всі процесори або ОС робить деяке оновлення.

Ось проста утиліта для вимірювання ефективності виконання коду C / C ++, усереднення значень біля медіани: https://github.com/saniv/gauge

Я все ще шукаю більш надійний і швидкий спосіб вимірювання коду. Можливо, можна спробувати запустити код у контрольованих умовах на голому металі без будь-якої ОС, але це дасть нереальний результат, адже насправді ОС дійсно задіяна.

x86 має ці апаратні лічильники продуктивності, які включають фактичну кількість виконаних інструкцій, але вони складні для доступу без допомоги ОС, важко інтерпретувати та мають свої проблеми ( http://archive.gamedev.net/archive/reference/articles /article213.html ). Однак вони можуть бути корисними для дослідження характеру горловини пляшки (доступ до даних або фактичні розрахунки за цими даними).


Так, сучасні процесори x86 простоюють набагато повільніше, ніж max turbo. Залежно від налаштувань "губернатора", набіг до максимальної тактової швидкості може зайняти мілісекунд (Skylake з апаратним управлінням P-станом, особливо з встановленою енергією_виконання performance) або багато десятків мілісекунд. en.wikipedia.org/wiki/Dynamic_frequency_scaling . І так, середня продуктивність - це зазвичай хороший вибір; високий кінець зазвичай має деякі шипи від перешкод.
Пітер Кордес

Найчастіше найкраща ставка, щоб уникнути оптимізації роботи, - це введення командного рядка та повернення результату. Або запишіть функцію в окремий файл, mainякий бере аргумент і повертає результат, і не використовуйте оптимізацію часу зв’язку. Тоді компілятор не може встроїти його в абонент. Працює лише в тому випадку, якщо функція вже містить певний цикл, інакше накладні виклики / повтори занадто високі.
Пітер Кордес

Компілятор все ще може оптимізувати вхід одного командного рядка з циклу, якщо обробити його статичним кодом без будь-яких побічних ефектів. Тому найкраще генерувати випадковий вхід для кожної ітерації. Очевидно, rand () слід викликати поза вимірюваним кодом, перш ніж перший годинник (), тому що rand () може також призвести до системного виклику, вибірки деякого апаратного генератора ентропії (який у старих системах був рухом миші). Просто не забудьте надрукувати кожен біт виводу, інакше компілятор може вирішити, що вам не потрібен весь результат у цілому або його частина. Це можна зробити, скажімо, CRC32.
SmugLispWeenie

Якщо ваш тест коду знаходиться в окремому файлі, і ви не використовуєте оптимізацію часу зв’язку, компілятор не може зробити CSE для оптимізації між викликами. Абонент не може припустити нічого про те, що користувач не має видимих ​​побічних ефектів. Це дозволяє помістити щось відносно коротке всередину повторюваного циклу, щоб зробити його достатньо довгим за часом, просто надіславши / повторити накладні витрати. Якщо ви дозволите це вбудованому рядку, вам доведеться перевірити згенерований ASM, щоб переконатися, що він не виводив обчислення з циклу, як ви говорите.
Пітер Кордес

Специфічний для компілятора спосіб - використовувати (наприклад) GNU C inline asm, щоб змусити компілятора матеріалізувати результат у регістрі та / або забути те, що він знає про значення змінної, фактично не вводячи додаткових інструкцій. Еквівалент "Escape" та "Clobber" у посиланнях MSVC на відео про профілювання та мікробенчмаркінг (розмова розробника Clang Чандлера Каррута на CppCon 2015) Існує не еквівалент MSVC, але саме питання показує функції GNU C та способи їх використання.
Пітер Кордес

0

Деяким може бути корисним інший вид введення: мені цей метод вимірювання часу був наданий у рамках університетського курсу з GPGPU-програмування за допомогою NVidia CUDA ( опис курсу ). Він поєднує в собі методи, помічені в попередніх публікаціях, і я просто розміщую його, оскільки вимоги надають йому надійності:

unsigned long int elapsed;
struct timeval t_start, t_end, t_diff;
gettimeofday(&t_start, NULL);

// perform computations ...

gettimeofday(&t_end, NULL);
timeval_subtract(&t_diff, &t_end, &t_start);
elapsed = (t_diff.tv_sec*1e6 + t_diff.tv_usec);
printf("GPU version runs in: %lu microsecs\n", elapsed);

Я припускаю, що ви могли б помножитись, наприклад, 1.0 / 1000.0щоб отримати одиницю вимірювання, яка відповідає вашим потребам.


1
gettimeofday застарілий і не рекомендується. clock_gettimeНатомість його сторінка "POSIX" рекомендує , що дозволяє запитувати про CLOCK_MONOTONICте, що на це не впливають зміни системного годинника, і, отже, це краще як таймер інтервалу. Наприклад, у сучасних системах Linux, gettimeofdayв основному, це обгортка, clock_gettimeяка перетворює наносекунди в мікросекунди. (Дивіться відповідь ДжонаСлла).
Пітер Кордес

Цей метод додав @Wes Hardaker, головна відмінність - використання timeval_subtract.
ワ イ き ん ぐ

Гаразд, тому єдиною корисною частиною вашої відповіді є назва функції, яку ви не визначаєте, і якої немає в стандартній бібліотеці. (Тільки в посібнику з glibc: gnu.org/software/libc/manual/html_node/Elapsed-Time.html ).
Пітер Кордес

-2

Порівняння часу виконання сортування міхура та сортування вибору У мене є програма, яка порівнює час виконання сортування міхура та сортування вибору. Щоб дізнатись час виконання блоку коду, обчисліть час до та після блоку

 clock_t start=clock();
 
 clock_t end=clock();
 CLOCKS_PER_SEC is constant in time.h library

Приклад коду:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
   int a[10000],i,j,min,temp;
   for(i=0;i<10000;i++)
   {
      a[i]=rand()%10000;
   }
   //The bubble Sort
   clock_t start,end;
   start=clock();
   for(i=0;i<10000;i++)
   {
     for(j=i+1;j<10000;j++)
     {
       if(a[i]>a[j])
       {
         int temp=a[i];
         a[i]=a[j];
         a[j]=temp;
       }
     }
   }
   end=clock();
   double extime=(double) (end-start)/CLOCKS_PER_SEC;
   printf("\n\tExecution time for the bubble sort is %f seconds\n ",extime);

   for(i=0;i<10000;i++)
   {
     a[i]=rand()%10000;
   }
   clock_t start1,end1;
   start1=clock();
   // The Selection Sort
   for(i=0;i<10000;i++)
   {
     min=i;
     for(j=i+1;j<10000;j++)
     {
       if(a[min]>a[j])
       {
         min=j;
       }
     }
     temp=a[min];
     a[min]=a[i];
     a[i]=temp;
   }
   end1=clock();
   double extime1=(double) (end1-start1)/CLOCKS_PER_SEC;
   printf("\n");
   printf("\tExecution time for the selection sort is %f seconds\n\n", extime1);
   if(extime1<extime)
     printf("\tSelection sort is faster than Bubble sort by %f seconds\n\n", extime - extime1);
   else if(extime1>extime)
     printf("\tBubble sort is faster than Selection sort by %f seconds\n\n", extime1 - extime);
   else
     printf("\tBoth algorithms have the same execution time\n\n");
}

4
Це на самому ділі не додає нічого нового по порівнянні з adimoh «и відповіді , за винятком того, що він заповнює" виконуваний код "блок (або два з них) з деяким фактичним кодом. І ця відповідь нічого , що не було в не додав Alexandre C «s відповіді від два роки раніше.
Джонатан Леффлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.