Pergunta

Como você pode medir quanto tempo uma função levará para ser executada?

Esta é uma função relativamente curta e o tempo de execução provavelmente estaria na faixa de milissegundos.

Esta questão específica refere-se a um sistema embarcado, programado em C ou C++.

Foi útil?

Solução

A melhor maneira de fazer isso em um sistema embarcado é definir um pino de hardware externo ao entrar na função e limpá-lo ao sair da função.Isso é feito preferencialmente com um pouco de instrução de montagem para não distorcer muito os resultados.

Editar:Um dos benefícios é que você pode fazer isso em seu aplicativo real e não precisa de nenhum código de teste especial.Pinos de depuração externos como esse são (deveriam ser!) Prática padrão para todos os sistemas embarcados.

Outras dicas

Existem três soluções potenciais:

Solução de Hardware:

Use um pino de saída livre no processador e conecte um osciloscópio ou analisador lógico ao pino.Inicialize o pino para um estado baixo, pouco antes de chamar a função que deseja medir, coloque o pino em um estado alto e logo após retornar da função, desative o pino.


    *io_pin = 1;
    myfunc();
    *io_pin = 0;

Solução para leitor ávido:

Se a função for bastante pequena e você puder gerenciar o código desmontado, poderá abrir o databook da arquitetura do processador e contar os ciclos que o processador levará para executar todas as instruções.Isso lhe dará o número de ciclos necessários.
Tempo = # ciclos * Taxa de clock do processador / tiques do relógio por instruções

Isso é mais fácil de fazer para funções menores ou código escrito em assembler (para um microcontrolador PIC, por exemplo)

Solução de contador de carimbo de data/hora:

Alguns processadores possuem um contador de carimbo de data/hora que aumenta rapidamente (a cada poucos tiques do clock do processador).Basta ler o carimbo de data/hora antes e depois da função.Isso lhe dará o tempo decorrido, mas cuidado, pois você poderá ter que lidar com a rolagem do contador.

Invoque-o em um loop com uma tonelada de invocações e depois divida pelo número de invocações para obter o tempo médio.

então:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.

se estiver usando Linux, você pode cronometrar o tempo de execução de um programa digitando na linha de comando:

time [funtion_name]

se você executar apenas a função em main() (assumindo C++), o resto do tempo do aplicativo deverá ser insignificante.

Repito a chamada de função muitas vezes (milhões), mas também emprego o seguinte método para descontar a sobrecarga do loop:

start = getTicks();

repeat n times {
    myFunction();
    myFunction();
}

lap = getTicks();

repeat n times {
    myFunction();
}

finish = getTicks();

// overhead + function + function
elapsed1 = lap - start;

// overhead + function
elapsed2 = finish - lap;

// overhead + function + function - overhead - function = function
ntimes = elapsed1 - elapsed2;

once = ntimes / n; // Average time it took for one function call, sans loop overhead

Em vez de chamar function() duas vezes no primeiro loop e uma vez no segundo loop, você poderia chamá-lo apenas uma vez no primeiro loop e não chamá-lo (ou seja,loop vazio) no segundo, porém o loop vazio pode ser otimizado pelo compilador, fornecendo resultados de tempo negativos :)

start_time = timer
function()
exec_time = timer - start_time

Windows XP/NT incorporado ou Windows CE/Mobile

Você pode usar QueryPerformanceCounter() para obter o valor de um contador MUITO RÁPIDO antes e depois de sua função.Então você subtrai esses valores de 64 bits e obtém "tiques" delta.Usando QueryPerformanceCounterFrequency() você pode converter os "delta ticks" em uma unidade de tempo real.Você pode consultar a documentação do MSDN sobre essas chamadas WIN32.

Outros sistemas embarcados

Sem sistemas operacionais ou apenas com sistemas operacionais básicos você terá que:

  • programe um dos temporizadores internos da CPU para rodar e contar livremente.
  • configure-o para gerar uma interrupção quando o timer estourar, e nesta rotina de interrupção incremente uma variável "carry" (isso é para que você possa realmente medir o tempo maior que a resolução do timer escolhido).
  • antes de sua função, você salva AMBOS o valor de "transporte" e o valor do registro da CPU que contém os ticks em execução para o temporizador de contagem que você configurou.
  • o mesmo depois da sua função
  • subtraia-os para obter um contador delta.
  • a partir daí, é apenas uma questão de saber quanto tempo significa um tick em sua CPU/Hardware, considerando o relógio externo e a desmultiplicação que você configurou ao configurar seu temporizador.Você multiplica esse “comprimento do tick” pelos “tiques delta” que acabou de obter.

MUITO IMPORTANTE Não se esqueça de desabilitar antes e restaurar as interrupções depois de obter os valores do temporizador (tanto o valor de transporte quanto o valor do registro), caso contrário você corre o risco de salvar valores incorretos.

NOTAS

  • Isso é muito rápido porque são necessárias apenas algumas instruções de montagem para desabilitar interrupções, salvar dois valores inteiros e reativar interrupções.A subtração e conversão reais para unidades de tempo real ocorrem FORA da zona de medição de tempo, ou seja, DEPOIS da sua função.
  • Você pode querer colocar esse código em uma função para reutilizá-lo por completo, mas isso pode atrasar um pouco as coisas por causa da chamada de função e do envio de todos os registros para a pilha, mais os parâmetros, e então pop-los novamente.Em um sistema embarcado isso pode ser significativo.Pode ser melhor então em C usar MACROS ou escrever sua própria rotina de montagem salvando/restaurando apenas registros relevantes.

Depende da sua plataforma incorporada e do tipo de tempo que você está procurando.Para Linux embarcado, existem várias maneiras de fazer isso.Se desejar medir a quantidade de tempo de CPU usado pela sua função, você pode fazer o seguinte:

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

#define SEC_TO_NSEC(s) ((s) * 1000 * 1000 * 1000)

int work_function(int c) {
    // do some work here
    int i, j;
    int foo = 0;
    for (i = 0; i < 1000; i++) {
        for (j = 0; j < 1000; j++) {
            for ^= i + j;
        }
    }
}

int main(int argc, char *argv[]) {
    struct timespec pre;
    struct timespec post;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &pre);
    work_function(0);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &post);

    printf("time %d\n",
        (SEC_TO_NSEC(post.tv_sec) + post.tv_nsec) -
        (SEC_TO_NSEC(pre.tv_sec) + pre.tv_nsec));
    return 0;
}

Você precisará vincular isso à biblioteca em tempo real, basta usar o seguinte para compilar seu código:

gcc -o test test.c -lrt

Você também pode ler a página de manual em clock_gettime há alguns problemas com a execução deste código em um sistema baseado em SMP que podem invalidar o teste.Você poderia usar algo como sched_setaffinity() ou a linha de comando cpuset para forçar o código em apenas um núcleo.

Se você deseja medir o tempo do usuário e do sistema, você pode usar o times(NULL) que retorna algo como um instante.Ou você pode alterar o parâmetro para clock_gettime() de CLOCK_THREAD_CPUTIME_ID para CLOCK_MONOTONIC... mas tome cuidado para não enrolar CLOCK_MONOTONIC.

Para outras plataformas, você está por conta própria.

Atraiu

Eu sempre implemento uma rotina de ticker orientada por interrupção.Isso atualiza um contador que conta o número de milissegundos desde a inicialização.Este contador é então acessado com uma função GetTickCount().

Exemplo:

#define TICK_INTERVAL 1    // milliseconds between ticker interrupts
static unsigned long tickCounter;

interrupt ticker (void)  
{
    tickCounter += TICK_INTERVAL;
    ...
}

unsigned in GetTickCount(void)
{
    return tickCounter;
}

No seu código você cronometraria o código da seguinte maneira:

int function(void)
{
    unsigned long time = GetTickCount();

    do something ...

    printf("Time is %ld", GetTickCount() - ticks);
}

No terminal OS X (e provavelmente no Unix também), use "time":

time python function.py

Se o código for .Net, use a classe cronômetro (.net 2.0+) NÃO DateTime.Now.DateTime.Now não é atualizado com precisão suficiente e fornecerá resultados malucos

Se você estiver procurando uma resolução abaixo de um milissegundo, tente um destes métodos de temporização.Todos eles fornecerão resolução em pelo menos dezenas ou centenas de microssegundos:

Se for Linux embarcado, veja os temporizadores do Linux:

http://linux.die.net/man/3/clock_gettime

Java incorporado, veja nanoTime (), embora não tenha certeza se isso está na edição incorporada:

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#nanoTime()

Se você quiser acessar os contadores de hardware, tente PAPI:

http://icl.cs.utk.edu/papi/

Caso contrário, você sempre pode ir para a montadora.Você pode consultar a fonte PAPI da sua arquitetura se precisar de ajuda com isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top