Tempo de execução do programa C


209

Eu tenho um programa C que visa ser executado em paralelo em vários processadores. Preciso gravar o tempo de execução (que pode variar de 1 segundo a vários minutos). Procurei respostas, mas todas parecem sugerir o uso da clock()função, que envolve o cálculo do número de relógios que o programa levou dividido pelo Clocks_per_secondvalor.

Não tenho certeza de como o Clocks_per_secondvalor é calculado?

Em Java, apenas dedico o tempo atual em milissegundos antes e após a execução.

Existe algo semelhante em C? Eu dei uma olhada, mas não consigo encontrar uma maneira de obter algo melhor do que uma segunda resolução.

Também sei que um criador de perfil seria uma opção, mas estou procurando implementar um cronômetro.

obrigado


3
quais estruturas de OS / API você está usando / disponível? Simplesmente C?
typo.pl

4
É um pequeno programa, simplesmente C
Roger

Eu escrevi em detalhes sobre a implementação de uma solução portátil nesta resposta: stackoverflow.com/questions/361363/...
Alexander Saprykin

tempo necessário para executar uma função completa stackoverflow.com/a/40380118/6180077
Abdullah Farweez 17/17

Respostas:


344

CLOCKS_PER_SECé uma constante que é declarada em <time.h>. Para obter o tempo de CPU usado por uma tarefa em um aplicativo C, use:

clock_t begin = clock();

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

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

Observe que isso retorna a hora como um tipo de ponto flutuante. Isso pode ser mais preciso do que um segundo (por exemplo, você mede 4,52 segundos). A precisão depende da arquitetura; em sistemas modernos, você obtém facilmente 10ms ou menos, mas em máquinas Windows mais antigas (da era Win98), era mais próximo de 60ms.

clock()é padrão C; funciona "em qualquer lugar". Existem funções específicas do sistema, como getrusage()em sistemas similares ao Unix.

Java System.currentTimeMillis()não mede a mesma coisa. É um "relógio de parede": pode ajudá-lo a medir quanto tempo levou para a execução do programa, mas não informa quanto tempo de CPU foi usado. Em sistemas multitarefa (ou seja, todos eles), estes podem ser amplamente diferentes.


1
Isso me dá um resultado muito aleatório - recebo uma mistura de número grande / pequeno / negativo no mesmo trecho de código. GCC 4.7 Linux 3.2 AMD64

3
Sim: clock()retorna um tempo em alguma escala interna chamada "relógios" e CLOCKS_PER_SECé o número de relógios por segundo, dividindo-o por CLOCKS_PER_SECum tempo em segundos. No código acima, o valor é a doublepara que você possa escalá-lo à vontade.
9788 Thomas Thomasin

18
Grande aviso: clock () retorna a quantidade de tempo que o SO passou executando o processo, e não a quantidade real de tempo decorrido. No entanto, isso é bom para cronometrar um bloco de código, mas não para medir o tempo decorrido no mundo real.

2
Ele disse que quer medir um programa multiencadeado. Não tenho certeza se um clock () é adequado para isso, porque resume os tempos de execução de todos os threads, portanto o resultado será semelhante se o código for executado seqüencialmente. Para essas coisas, uso omp_get_wtime (), mas é claro que preciso ter certeza de que o sistema não está ocupado com outros processos.
Youda008

1
Devo mencionar algumas coisas, embora esse segmento tenha sido mais relevante há um ano: CLOCKS_PER_SECé um long intcom o valor 1000000, dando tempo em microssegundos quando não dividido; não ciclos de clock da CPU. Portanto, ele não precisa levar em consideração a frequência dinâmica, pois o relógio aqui está em microssegundos (talvez ciclos de clock para uma CPU de 1 MHz?) Eu fiz um pequeno programa em C imprimindo esse valor e era 1000000 no meu laptop i7-2640M, com frequência dinâmica, permitindo 800 MHz a 2,8 GHz, mesmo usando o Turbo Boost para atingir 3,5 GHz.
DDPWNAGE 17/08/19

111

Se você estiver usando o shell Unix para execução, poderá usar o comando time.

fazendo

$ time ./a.out

assumindo a.out como o executável lhe dará o tempo necessário para executar este


3
@acgtyrant, mas apenas para programas simples, porque levará todo o tempo do programa, incluindo entrada, saída etc.
phuclv

1
Se você está no Linux e reduziu seu (micro) benchmark para um programa com sobrecarga insignificante de inicialização, por exemplo, um executável estático que executa seu hot loop por alguns segundos, você pode usar perf stat ./a.outpara obter contadores de desempenho de HW para falhas de cache e desvios de agência e IPC.
Peter Cordes

61

Em baunilha C simples:

#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
Os melhores nomes de variáveis ​​que já vi há algum tempo. tic = "time in clock", toc = "time out clock". Mas também tic-toc = "tick-tock". É assim que estou rotulando o tempo daqui para a frente.
Logan Schelly

60

Você deseja funcionalmente isso:

#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));

Observe que isso mede em microssegundos, não apenas em segundos.


2
O compilador MinGW é baseado no GCC. Então, vai funcionar nisso. Mas se você usar o compilador visual C, receberá um erro.
precisa saber é o seguinte

11
Sim, ele funcionará no Windows com uma biblioteca AC que suporta a chamada gettimeofday. Na verdade, não importa qual seja o compilador, você apenas precisa vinculá-lo a uma biblioteca libc decente. Que, no caso de mingw, não é o padrão do Windows.
Wes Hardaker

1
Isso funciona para mim no Windows XP com cygwin gcc e Linux Ubuntu. Isto é exatamente o que eu queria.
Amor e paz - Joe Codeswell

gettimeofdayestá obsoleto e não é recomendado para novo código. Sua página do manual POSIX recomenda o clock_gettime , que permite solicitar CLOCK_MONOTONICque não seja afetado pelas alterações no relógio do sistema e, portanto, é melhor como um intervalo de tempo. (Veja a resposta de JohnSll ). Nos sistemas Linux modernos, por exemplo, gettimeofday é basicamente um invólucro para clock_gettime que converte nanossegundos em microssegundos.
Peter Cordes

12

A maioria dos programas simples possui tempo de computação em milissegundos. Então, suponho, você achará isso útil.

#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);
}

Se você deseja calcular o tempo de execução de todo o programa e você estiver em um sistema Unix, execute o programa usando o comando time como estetime ./a.out


No Windows, pelo menos, o fator é de pelo menos 100, mas não 1000 e não é exata
boctulus

6
Essa resposta não adiciona qualquer coisa que não estava em Alexandre C 's resposta a partir de dois anos antes.
Jonathan Leffler

3
@boctulus: 1s é sempre 1000ms, também no Windows.
alk

9

Muitas respostas foram sugeridas clock()e depois a CLOCKS_PER_SECpartir de time.h. Provavelmente, essa é uma péssima idéia, porque é isso que meu /bits/time.harquivo diz:

/* 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

Portanto, CLOCKS_PER_SECpode ser definido como 1000000, dependendo de quais opções você usa para compilar e, portanto, não parece ser uma boa solução.


1
Obrigado pela informação, mas existe alguma alternativa melhor ainda?
Ozanmuyes

4
Este não é um problema prático: sim, os sistemas Posix sempre têm CLOCK_PER_SEC==1000000, mas, ao mesmo tempo, todos usam precisão de 1 µs para a implementação do clock (); a propósito, ele tem a propriedade de reduzir os problemas de compartilhamento. Se você deseja medir eventos potencialmente muito rápidos, digamos abaixo de 1 ms, primeiro você deve se preocupar com a precisão (ou resolução) da função clock (), que é necessariamente mais grossa do que 1 µs no Posix, mas geralmente é muito mais grossa; a solução usual é executar o teste várias vezes; a pergunta feita não parecia exigir, no entanto.
AntoineL

Por que não seria uma boa solução? Você obtém algum valor clock(), se você dividir esse valor com CLOCK_PER_SECgarantia de tempo em segundos que a CPU levou. A responsabilidade de medir a velocidade real do relógio é responsabilidade da clock()função, não sua.
Zaffy 28/08/19

9

A resposta de Thomas Pornin como macros:

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

Use-o assim:

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

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

Resultado:

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

4

Você deve levar em consideração que medir o tempo que levou a execução de um programa depende muito da carga que a máquina possui naquele momento específico.

Sabendo que, a maneira de obter o tempo atual em C pode ser alcançada de diferentes maneiras, é mais fácil:

#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));
}

Espero que ajude.

Saudações!


4

(Todas as respostas aqui estão ausentes, se o administrador do sistema alterar a hora do sistema ou se o fuso horário tiver horários diferentes de inverno e de verão. Portanto ...)

No uso do linux: clock_gettime(CLOCK_MONOTONIC_RAW, &time_variable); não será afetado se o administrador do sistema alterar a hora ou se você mora em um país com horário de inverno diferente do horário de verão etc.

#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 afirma:

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.

Você pode explicar o cálculo usado para obter o número de segundos? Não é óbvio o que está acontecendo.
Colin Keenan

1
Esse (end.tv_nsec - begin.tv_nsec) / 1000000000.0resultado não seria 0sempre?
alk

@alk: não, dividir por um doubleliteral dispara int ou longà doubleconversão antes da divisão. É claro que você pode usar o número inteiro e imprimir a tv_secpeça e, em seguida, a parte fracionária com zero como %ld.%09ld, mas converter para o dobro é fácil e 53 bits de precisão geralmente são suficientes para tempos de referência.
Peter Cordes

1
(Ops, a subtração da parte dos nanossegundos pode precisar ser carregada na parte dos segundos, portanto, usar double e deixar negativo evita esse problema. Para usar uma string de formato inteiro puro, você precisa do timespec_subtracttipo timeval_subtractsugerido no manual da glibc : gnu.org/software/libc/manual/html_node/Elapsed-Time.html )
Peter Cordes

3

O ANSI C especifica apenas as funções de segunda hora de precisão. No entanto, se você estiver executando em um ambiente POSIX, poderá usar a função gettimeofday () que fornece microssegundos de resolução de tempo passado desde a época do UNIX.

Como uma observação lateral, eu não recomendaria o uso de clock (), pois ele é mal implementado em muitos sistemas (se não todos?) E não é preciso, além do fato de que se refere apenas ao tempo que o seu programa passou na CPU e não o tempo de vida total do programa, que, de acordo com sua pergunta, é o que suponho que você gostaria de medir.


O padrão ISO C (assumindo que é isso que significa ANSI C ) propositalmente não especifica a precisão das funções de tempo . Então, especificamente em uma implementação POSIX, ou no Windows, a precisão das funções de relógio de parede (consulte a resposta de Thomas) está em segundos. Mas a precisão do relógio () 's é geralmente maior, e sempre 1μs no POSIX (independentemente da precisão.)
AntoineL

2

Todas as soluções não estão funcionando no meu sistema.

Eu posso usar

#include <time.h>

double difftime(time_t time1, time_t time0);

2
Isso indica a diferença entre dois time_tvalores como um dobro. Como os time_tvalores são precisos apenas por um segundo, é de valor limitado a impressão do tempo gasto pelos programas de execução curta, embora possa ser útil para programas executados por longos períodos.
Jonathan Leffler

Por alguma razão, passar um par de clock_ts para difftimeparece funcionar para mim com a precisão de um centésimo de segundo. Isso está no linux x86. Também não consigo subtrair stope starttrabalhar.
ragerdl

@ragerdl: Você precisa passar para difftime() clock() / CLOCKS_PER_SEC, pois espera segundos.
alk

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);
}

Este programa funcionará como charme.


2

Descobri que o relógio usual (), todo mundo recomenda aqui, por algum motivo se desvia muito de uma corrida para outra, mesmo para código estático sem efeitos colaterais, como desenhar na tela ou ler arquivos. Pode ser porque a CPU altera os modos de consumo de energia, OS dando prioridades diferentes, etc ...

Portanto, a única maneira de obter o mesmo resultado sempre de forma confiável com clock () é executar o código medido em um loop várias vezes (por vários minutos), tomando precauções para impedir que o compilador o otimize: os compiladores modernos podem pré-calcular o código sem efeitos colaterais sendo executados em um loop e mova-o para fora do loop., como, por exemplo, usar entrada aleatória para cada iteração.

Depois que amostras suficientes são coletadas em uma matriz, uma classifica essa matriz e pega o elemento do meio, chamado mediana. A mediana é melhor que a média, porque elimina desvios extremos, como, por exemplo, antivírus que ocupam toda a CPU ou OS fazendo alguma atualização.

Aqui está um utilitário simples para medir o desempenho de execução do código C / C ++, com a média dos valores próximos da mediana: https://github.com/saniv/gauge

Ainda estou procurando uma maneira mais rápida e robusta de medir o código. Provavelmente, pode-se tentar executar o código em condições controladas no bare metal sem nenhum sistema operacional, mas isso dará um resultado irreal, porque, na realidade, o sistema operacional se envolve.

O x86 possui esses contadores de desempenho de hardware, que incluem o número real de instruções executadas, mas são difíceis de acessar sem a ajuda do SO, difíceis de interpretar e têm seus próprios problemas ( http://archive.gamedev.net/archive/reference/articles /article213.html ). Ainda assim, eles podem ser úteis para investigar a natureza do gargalo da garrafa (acesso a dados ou cálculos reais nesses dados).


Sim, as modernas CPUs x86 ficam ociosas muito mais lentamente que o max turbo. Dependendo das configurações do "regulador", a velocidade máxima do clock pode demorar um milissegundo (Skylake com gerenciamento de estado P de hardware, especialmente com energy_performance_preference definido como performance) ou muitas dezenas de milissegundos. en.wikipedia.org/wiki/Dynamic_frequency_scaling . E sim, o desempenho médio geralmente é uma boa escolha; o high-end geralmente tem alguns picos de interferência.
Peter Cordes

Muitas vezes, sua melhor aposta para evitar que o trabalho seja otimizado é uma entrada da linha de comando e retornar o resultado. Ou escreva uma função em um arquivo separado do mainque recebe um argumento e retorna um resultado e não use a otimização do tempo do link. Em seguida, o compilador não pode incorporá-lo no chamador. Só funciona se a função já incluir algum tipo de loop, caso contrário, a sobrecarga de chamada / retenção é muito alta.
Peter Cordes

O compilador ainda pode otimizar a entrada de linha de comando única fora do loop, se você a processar com código estático sem efeitos colaterais. Portanto, é melhor gerar uma entrada aleatória para cada iteração. Obviamente, rand () deve ser chamado fora do código medido, antes do primeiro relógio (), porque rand () também pode resultar em uma chamada do sistema, amostrando algum gerador de entropia de hardware (que em sistemas mais antigos era o movimento do mouse). Apenas não esqueça de imprimir todos os bits da saída, caso contrário, o compilador pode decidir que você não precisa de toda a saída como um todo ou parte dela. Isso pode ser feito com o CRC32, por exemplo.
SmugLispWeenie

Se o código em teste estiver em um arquivo separado e você não usar a otimização do tempo do link, não há como o compilador fazer o CSE para otimizar entre as chamadas. O chamador não pode assumir nada sobre o receptor não ter efeitos colaterais visíveis. Isso permite que você coloque algo relativamente curto dentro de um loop de repetição para torná-lo tempo suficiente para o tempo, com apenas sobrecarga de chamada / reter. Se você o deixar em linha, deverá verificar o asm gerado para garantir que ele não elimine um cálculo de um loop, como você diz.
Peter Cordes

A maneira específica do compilador é usar (por exemplo) o GNU C inline asm para forçar um compilador a materializar um resultado em um registro e / ou esquecer o que ele sabe sobre o valor de uma variável, sem realmente introduzir instruções extras. O equivalente a "Escape" e "Clobber" no MSVC vincula a um vídeo sobre criação de perfil e microbenchmarking (conversa do CppCon 2015 do desenvolvedor de clang Chandler Carruth) Não há equivalente ao MSVC, mas a pergunta em si mostra as funções do GNU C e como usá-las.
Peter Cordes

0

Alguns podem achar útil um tipo diferente de entrada: recebi esse método de medir o tempo como parte de um curso universitário de programação GPGPU com a NVidia CUDA ( descrição do curso ). Ele combina os métodos vistos nas postagens anteriores, e eu simplesmente a publico porque os requisitos dão credibilidade:

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);

Suponho que você possa se multiplicar, por exemplo, 1.0 / 1000.0para obter a unidade de medida que atenda às suas necessidades.


1
gettimeofday é obsoleto e não é recomendado. Sua página de manual do POSIX recomenda clock_gettime, em vez disso, que permite que você CLOCK_MONOTONICnão seja afetado por alterações no relógio do sistema e, portanto, é melhor como um temporizador de intervalo. Nos sistemas Linux modernos, por exemplo, gettimeofdayé basicamente um invólucro clock_gettimeque converte nanossegundos em microssegundos. (Veja a resposta de JohnSll).
Peter Cordes

Este método foi adicionado pelo @Wes Hardaker, a principal diferença está usando timeval_subtract.
precisa saber é o seguinte

Ok, então a única parte útil da sua resposta é o nome de uma função que você não define e que não está na biblioteca padrão. (Apenas no manual da glibc: gnu.org/software/libc/manual/html_node/Elapsed-Time.html ).
Peter Cordes

-2

Comparação do tempo de execução do tipo de bolha e do tipo de seleção Eu tenho um programa que compara o tempo de execução do tipo de bolha e do tipo de seleção. Para descobrir o tempo de execução de um bloco de código, calcule o tempo antes e depois do bloco,

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

Código de exemplo:

#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
Isso realmente não adiciona nada de novo em comparação com a resposta do adimoh , exceto que ele preenche o bloco 'o código executável' (ou dois deles) com algum código real. E essa resposta não adiciona qualquer coisa que não estava em Alexandre C 's resposta a partir de dois anos antes.
Jonathan Leffler
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.