Время выполнения C программы


209

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

Я не уверен, как Clocks_per_secondрассчитывается значение?

В Java я просто беру текущее время в миллисекундах до и после выполнения.

Есть ли подобное в C? Я посмотрел, но я не могу найти способ получить что-то лучше, чем второе разрешение.

Я также знаю, что вариант был бы вариант, но я пытаюсь реализовать таймер сам.

Спасибо


3
какие платформы OS / API вы используете / доступны? Просто с C?
typo.pl

4
Это довольно маленькая программа, просто C
Roger

Я подробно описал реализацию переносимого решения в этом ответе: stackoverflow.com/questions/361363/…
Александр Сапрыкин

время, необходимое для выполнения полной функции stackoverflow.com/a/40380118/6180077
Абдулла Фарвиц

Ответы:


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()является стандартным C; это работает "везде". Есть системные функции, такие как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
Он сказал, что хочет измерить многопоточную программу. Я не уверен, что clock () подходит для этого, потому что он суммирует время выполнения всех потоков, поэтому результат будет выглядеть, как если бы код выполнялся последовательно. Для таких вещей я использую omp_get_wtime (), но, конечно, мне нужно убедиться, что система не занята другими процессами.
Youda008

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

111

Если вы используете оболочку Unix для запуска, вы можете использовать команду времени.

дела

$ time ./a.out

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


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

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

61

В простой ванили C:

#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 = "Tick-Tock". Вот так я обозначаю время отсюда.
Логан Шелли

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. Так что это будет работать над этим. Но если вы используете компилятор Visual C, то вы получите ошибку.
user2550754

11
Да, он будет работать в Windows с библиотекой AC, которая поддерживает вызов gettimeofday. На самом деле не имеет значения, что это за компилятор, вам просто нужно связать его с приличной библиотекой libc. Что, в случае с mingw, не является окном по умолчанию.
Уэс Хардакер

1
Это работает для меня на Windows XP с Cygwin GCC и Linux Ubuntu. Это именно то, что я хотел.
Любовь и мир - Джо Кодсвелл

gettimeofdayустарел и не рекомендуется для нового кода. Его справочная страница POSIX рекомендует вместо этого clock_gettime , которая позволяет вам спрашивать CLOCK_MONOTONIC, не влияет ли изменение системных часов, и, следовательно, оно лучше как интервал времени. (См . Ответ JohnSll ). Например, в современных системах 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, и это не точно
boctulus

6
Этот ответ не добавляет ничего такого, чего не было в ответе Александра С , сделанного двумя годами ранее.
Джонатан Леффлер

3
@boctulus: 1 с всегда 1000 мс, также на окнах.
алк

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
Спасибо за информацию, но есть ли лучшая альтернатива?
ozanmuyes

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

Почему это не будет хорошим решением? Вы получаете некоторое значение clock(), если вы делите это значение с CLOCK_PER_SECгарантией получения времени в секундах, затраченного процессором. Ответственность за измерение фактической тактовой частоты лежит на clock()функции, а не на вас.
Zaffy

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

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

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

#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

(Все ответы здесь отсутствуют, если ваш системный администратор меняет системное время или ваш часовой пояс отличается от зимнего и летнего времени. Поэтому ...)

При использовании 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 точность функций настенных часов (см. Ответ Томаса) выражается в секундах. Но точность clock () обычно выше, и в Posix она всегда равна 1 мкс (независимо от точности)
AntoineL

2

Каждое решение не работает в моей системе.

Я могу использовать

#include <time.h>

double difftime(time_t time1, time_t time0);

2
Это дает разницу между двумя time_tзначениями в двойном размере. Поскольку time_tзначения имеют точность только с точностью до секунды, они имеют ограниченную ценность при распечатке времени, затрачиваемого короткими программами, хотя это может быть полезно для программ, работающих в течение длительных периодов времени.
Джонатан Леффлер

По какой-то причине передача пары clock_ts to, 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

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

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

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

Вот простая утилита для измерения производительности выполнения кода C / C ++ с усреднением значений около медианы: https://github.com/saniv/gauge

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

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


Да, современные процессоры x86 бездействуют намного медленнее, чем max turbo. В зависимости от настроек «регулятора», увеличение максимальной тактовой частоты может занять миллисекунду (Skylake с аппаратным управлением P-состоянием, особенно с установленным значением energy_performance_preference performance) или многие десятки миллисекунд. en.wikipedia.org/wiki/Dynamic_frequency_scaling . И да, средняя производительность, как правило, хороший выбор; у высокого конца обычно есть некоторые шипы от вмешательства.
Питер Кордес

Часто лучшим вариантом избежать оптимизации работы является ввод с командной строки и возврат результата. Или напишите функцию в отдельном файле, mainкоторый принимает аргумент и возвращает результат, и не использует оптимизацию во время компоновки. Тогда компилятор не может вставить его в вызывающую программу. Работает только в том случае, если функция уже содержит какой-то цикл, в противном случае издержки call / ret слишком велики.
Питер Кордес

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

Если ваш тестируемый код находится в отдельном файле и вы не используете оптимизацию во время компоновки, компилятор не сможет выполнить CSE для оптимизации между вызовами. Абонент не может предположить, что вызываемый абонент не имеет видимых побочных эффектов. Это позволяет поместить что - то относительно короткий внутри цикла повторения , чтобы сделать его достаточно долго время, только с вызовом / RET над головой. Если вы позволите ему встроиться, то вы должны проверить сгенерированный asm, чтобы убедиться, что он не выводит вычисления из цикла, как вы говорите.
Питер Кордес

Специфичный для компилятора способ заключается в использовании (например) встроенного asm GNU C, чтобы заставить компилятор материализовать результат в регистре и / или забыть то, что он знает о значении переменной, без фактического введения дополнительных инструкций. Эквивалент «Escape» и «Clobber» в MSVC ссылается на видео о профилировании и микробенчмаркинге (выступление CppCon 2015 от Chandler Carruth, разработчик Clang) Эквивалента 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которая преобразует наносекунды в микросекунды. (См. Ответ JohnSll).
Питер Кордес

Этот метод был добавлен @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 «ы ответа , за исключением того, что он заполняет„исполняемый код“блок (или два из них) с некоторым фактическим кодом. И этот ответ не добавляет ничего такого, чего не было в ответе Александра С , сделанного двумя годами ранее.
Джонатан Леффлер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.