O que significam 'real', 'user' e 'sys' na saída do tempo (1)?

1749
$ time foo
real        0m0.003s
user        0m0.000s
sys         0m0.004s
$

O que significam 'real', 'user' e 'sys' na saída do tempo?

Qual deles é significativo ao comparar meu aplicativo?

rayryeng
fonte
2
como posso acessar apenas um deles? por exemplo, apenas em tempo real?
Mojtaba Ahmadi
1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi
2
@Casillass Real - stackoverflow.com/questions/2408981/…
ConcernedOfTunbridgeWells
7
Se o seu programa sair tão rápido, nenhum deles é significativo, é apenas uma sobrecarga de inicialização. Se você deseja medir todo o programa time, faça algo que levará pelo menos um segundo.
Peter Cordes
5
É realmente importante observar que timeé uma palavra-chave bash. Então digitação man timeé não dar-lhe uma página de homem para a festança time, ao contrário, está dando a página man /usr/bin/time. Isso me tropeçou.
irritable_phd_syndrom

Respostas:

2063

Estatísticas de tempo de processo real, de usuário e de sistema

Uma dessas coisas não é igual as outras. Real refere-se ao tempo decorrido real; Usuário e sistema referem-se ao tempo da CPU usado apenas pelo processo.

  • Real é a hora do relógio de parede - hora do início ao fim da chamada. Todo esse tempo decorrido, incluindo intervalos de tempo usados ​​por outros processos e o tempo que o processo passa bloqueado (por exemplo, se estiver aguardando a conclusão da E / S).

  • Usuário é a quantidade de tempo de CPU gasto no código do modo de usuário (fora do kernel) dentro do processo. Este é apenas o tempo real da CPU usado na execução do processo. Outros processos e tempo que o processo gasta bloqueado não contam para esse valor.

  • Sys é a quantidade de tempo de CPU gasto no kernel dentro do processo. Isso significa executar o tempo de CPU gasto em chamadas do sistema no kernel, em oposição ao código da biblioteca, que ainda está sendo executado no espaço do usuário. Como 'usuário', esse é apenas o tempo da CPU usado pelo processo. Veja abaixo uma breve descrição do modo kernel (também conhecido como modo 'supervisor') e o mecanismo de chamada do sistema.

User+Sysinformará quanto tempo de CPU real seu processo usou. Observe que isso ocorre em todas as CPUs; portanto, se o processo tiver vários encadeamentos (e esse processo estiver sendo executado em um computador com mais de um processador), poderá potencialmente exceder o tempo do relógio de parede relatado por Real(o que geralmente ocorre). Observe que, na saída, esses números incluem o tempo Usere o Systempo de todos os processos filhos (e seus descendentes), bem como quando eles poderiam ter sido coletados, por exemplo, por wait(2)ou waitpid(2), embora as chamadas do sistema subjacente retornem as estatísticas para o processo e seus filhos separadamente.

Origens das estatísticas relatadas por time (1)

As estatísticas relatadas por timesão coletadas de várias chamadas do sistema. 'Usuário' e 'Sys' vêm de wait (2)( POSIX ) ou times (2)( POSIX ), dependendo do sistema específico. 'Real' é calculado a partir das horas de início e término coletadas na gettimeofday (2)chamada. Dependendo da versão do sistema, várias outras estatísticas, como o número de alternâncias de contexto, também podem ser coletadas time.

Em uma máquina com vários processadores, um processo com vários threads ou um processo de bifurcação de crianças pode ter um tempo decorrido menor que o tempo total da CPU - pois diferentes threads ou processos podem ser executados em paralelo. Além disso, as estatísticas de tempo relatadas são de origens diferentes; portanto, os tempos registrados para tarefas de execução muito curtas podem estar sujeitos a erros de arredondamento, como mostra o exemplo dado pelo pôster original.

Uma breve cartilha no modo Kernel vs. Usuário

No Unix, ou em qualquer sistema operacional com memória protegida, o modo 'Kernel' ou 'Supervisor' refere-se a um modo privilegiado no qual a CPU pode operar. Certas ações privilegiadas que podem afetar a segurança ou a estabilidade só podem ser executadas quando a CPU está operando em este modo; essas ações não estão disponíveis para o código do aplicativo. Um exemplo dessa ação pode ser a manipulação da MMU para obter acesso ao espaço de endereço de outro processo. Normalmente, o código do modo de usuário não pode fazer isso (por um bom motivo), embora possa solicitar memória compartilhada do kernel, o que poderiaser lido ou escrito por mais de um processo. Nesse caso, a memória compartilhada é solicitada explicitamente ao kernel através de um mecanismo seguro e os dois processos precisam se conectar explicitamente a ele para usá-lo.

O modo privilegiado é geralmente chamado de modo 'kernel' porque o kernel é executado pela CPU em execução nesse modo. Para alternar para o modo kernel, é necessário emitir uma instrução específica (geralmente chamada armadilha ) que alterna a CPU para execução no modo kernel e executa código de um local específico mantido em uma tabela de salto. Por motivos de segurança, você não pode alternar para o modo kernel e executar código arbitrário - os traps são gerenciados através de uma tabela de endereços que não podem ser gravados, a menos que a CPU esteja executando no modo supervisor. Você trap com um número explícito de trap e o endereço é procurado na tabela de salto; o kernel possui um número finito de pontos de entrada controlados.

As chamadas de 'sistema' na biblioteca C (principalmente as descritas na Seção 2 das páginas de manual) possuem um componente no modo de usuário, que é o que você realmente chama no seu programa C. Nos bastidores, eles podem emitir uma ou mais chamadas de sistema para o kernel para executar serviços específicos, como E / S, mas ainda possuem código em execução no modo de usuário. Também é possível emitir diretamente uma interceptação no modo kernel a partir de qualquer código de espaço do usuário, se desejado, embora seja necessário escrever um trecho da linguagem assembly para configurar os registradores corretamente para a chamada.

Mais sobre 'sys'

Há coisas que seu código não pode fazer no modo usuário - como alocar memória ou acessar hardware (HDD, rede etc.). Eles estão sob a supervisão do kernel, e só eles podem fazê-lo. Algumas operações como mallocou fread/ fwriteinvocam essas funções do kernel e que contam como tempo 'sys'. Infelizmente, não é tão simples como "todas as chamadas para malloc serão contadas no tempo 'sys'". A chamada para mallocfará algum processamento próprio (ainda contado no tempo de 'usuário') e, em algum momento do processo, poderá chamar a função no kernel (contada no tempo de 'sys'). Depois de retornar da chamada do kernel, haverá mais tempo em 'user' e, em seguida,mallocretornará ao seu código. Quanto a quando o switch acontece, e quanto dele é gasto no modo kernel ... você não pode dizer. Depende da implementação da biblioteca. Além disso, outras funções aparentemente inocentes também podem usar malloce afins em segundo plano, que terão novamente algum tempo em 'sys'.

ConcernedOfTunbridgeWells
fonte
15
O tempo gasto pelos processos filhos conta em reais / sys?
ron
1
@ron - De acordo com a página de manual do Linux, ele agrega os tempos 'c' com os tempos do processo, então acho que sim. Os horários dos pais e dos filhos estão disponíveis separadamente das chamadas de horário (2). Eu acho que a versão Solaris / SysV do time (1) faz algo semelhante.
ConcernedOfTunbridgeWells
3
Usuário + Sistema permite medir o uso da CPU de um processo. Você pode usá-lo para comparar o desempenho. Isso é particularmente útil para código multithread em que mais de um núcleo da CPU possa estar trabalhando em um cálculo.
ConcernedOfTunbridgeWells
1
Não exatamente no tópico, no entanto: A execução de "\ time <cmd>" é interessante - fornece mais detalhes: (perdoe a má formatação no comentário): $ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps real 0m0.025s usuário 0m0.004s sys 0m0.018s $ \ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 time 11462 pts / 19 00:00:00 ps 0.00user 0.01system 0: 00.02capsulou 95% da CPU (0avgtext + 0avgdata 2160maxresident) k 0inputs + 0outputs (0major + 103minor) pagefaults 0swaps $
kaiwan
1
(Ficou sem caracteres no comentário anterior): Mais detalhes? Use perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
kaiwan
286

Para expandir a resposta aceita , eu só queria fornecer outro motivo para o realuser+ sys.

Tenha em mente que realrepresenta o tempo decorrido real, enquanto usere sysvalores representam o tempo de execução da CPU. Como resultado, em um sistema multicore, o tempo usere / ou sys(assim como sua soma) pode realmente exceder o tempo real. Por exemplo, em um aplicativo Java que estou executando para a classe, recebo este conjunto de valores:

real    1m47.363s
user    2m41.318s
sys     0m4.013s
lensovet
fonte
11
Eu sempre me perguntei sobre isso. Como eu sei que meus programas são de thread único, a diferença entre usuário e tempo real deve ser sobrecarga da VM, correto?
Quantum7
9
não necessariamente; a Sun JVM nas máquinas Solaris e a JVM da Apple no Mac OS X conseguem usar mais de um núcleo, mesmo em aplicativos de thread único. Se você fizer uma amostra de um processo java, verá que coisas como a coleta de lixo são executadas em threads separados (e outras coisas também que não me lembro de nada). Não sei se você realmente deseja denominar essa "sobrecarga de VM".
Objectivet
4
Eu acho que a quantidade de votos positivos lhe deu reputação suficiente agora: D. Então, o que você acha de realexceder usere systotal? Sobrecarga do sistema operacional, como a alternância de contexto de thread pode ser?
Muhammad Gelbana 26/09/12
19
Outro problema em potencial poderia ser a E / S: se seu aplicativo gasta bastante tempo aguardando para receber um arquivo ou fluxo, obviamente o tempo real excederia bastante o tempo do usuário / sistema, porque nenhum tempo de CPU é usado enquanto aguarda o acesso para um arquivo ou algo semelhante.
Lensovet
1
@ MuhammadGelbana - isso pode acontecer se o aplicativo for impedido de executar por qualquer motivo. Por exemplo, se estiver aguardando conexões de E / S, IPC ou soquete, ele permanecerá ocioso, acumulando nenhum tempo de CPU até que a chamada de bloqueio retorne.
ConcernedOfTunbridgeWells
41

real : o tempo real gasto na execução do processo do início ao fim, como se fosse medido por um ser humano com um cronômetro

usuário : o tempo acumulado gasto por todas as CPUs durante o cálculo

sys : o tempo acumulado gasto por todas as CPUs durante tarefas relacionadas ao sistema, como alocação de memória.

Observe que às vezes user + sys pode ser maior que real, pois vários processadores podem funcionar em paralelo.

varun
fonte
sysé CPU-tempo gasto em chamadas de sistema (e manipuladores de falta de página?)
Peter Cordes
1
realé frequentemente descrito como hora de "relógio de parede".
Peter Cordes
30

Exemplos mínimos de POSIX C executáveis

Para tornar as coisas mais concretas, quero exemplificar alguns casos extremos de timealguns programas de teste C mínimos.

Todos os programas podem ser compilados e executados com:

gcc -ggdb3 -o main.out -pthread -std=c99 -pedantic-errors -Wall -Wextra main.c
time ./main.out

e foram testados no Ubuntu 18.10, GCC 8.2.0, glibc 2.28, Linux kernel 4.18, laptop ThinkPad P51, CPU Intel Core i7-7820HQ (4 núcleos / 8 threads), 2x Samsung M471A2K43BB1-CRC RAM (2x 16GiB).

dormir

O sono não ocupado não conta apenas em um userou em sysapenas um real.

Por exemplo, um programa que dorme por um segundo:

#define _XOPEN_SOURCE 700
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    sleep(1);
    return EXIT_SUCCESS;
}

GitHub upstream .

produz algo como:

real    0m1.003s
user    0m0.001s
sys     0m0.003s

O mesmo vale para os programas bloqueados quando o IO está disponível.

Por exemplo, o seguinte programa aguarda o usuário digitar um caractere e pressione enter:

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

int main(void) {
    printf("%c\n", getchar());
    return EXIT_SUCCESS;
}

GitHub upstream .

E se você esperar cerca de um segundo, o resultado será igual ao exemplo de suspensão, algo como:

real    0m1.003s
user    0m0.001s
sys     0m0.003s

Por esse motivo, timepode ajudá-lo a distinguir entre programas vinculados à CPU e E / S: O que significam os termos "vinculado à CPU" e "vinculado à E / S"?

Vários segmentos

O exemplo a seguir faz nitersiterações de trabalho inútil puramente vinculado à CPU em nthreadsthreads:

#define _XOPEN_SOURCE 700
#include <assert.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

uint64_t niters;

void* my_thread(void *arg) {
    uint64_t *argument, i, result;
    argument = (uint64_t *)arg;
    result = *argument;
    for (i = 0; i < niters; ++i) {
        result = (result * result) - (3 * result) + 1;
    }
    *argument = result;
    return NULL;
}

int main(int argc, char **argv) {
    size_t nthreads;
    pthread_t *threads;
    uint64_t rc, i, *thread_args;

    /* CLI args. */
    if (argc > 1) {
        niters = strtoll(argv[1], NULL, 0);
    } else {
        niters = 1000000000;
    }
    if (argc > 2) {
        nthreads = strtoll(argv[2], NULL, 0);
    } else {
        nthreads = 1;
    }
    threads = malloc(nthreads * sizeof(*threads));
    thread_args = malloc(nthreads * sizeof(*thread_args));

    /* Create all threads */
    for (i = 0; i < nthreads; ++i) {
        thread_args[i] = i;
        rc = pthread_create(
            &threads[i],
            NULL,
            my_thread,
            (void*)&thread_args[i]
        );
        assert(rc == 0);
    }

    /* Wait for all threads to complete */
    for (i = 0; i < nthreads; ++i) {
        rc = pthread_join(threads[i], NULL);
        assert(rc == 0);
        printf("%" PRIu64 " %" PRIu64 "\n", i, thread_args[i]);
    }

    free(threads);
    free(thread_args);
    return EXIT_SUCCESS;
}

GitHub upstream + código de plotagem .

Em seguida, plotamos wall, user e sys em função do número de threads para 10 iterações fixas de 10 ^ 10 na minha CPU hyperthread de 8:

insira a descrição da imagem aqui

Plotar dados .

A partir do gráfico, vemos que:

  • para um aplicativo de núcleo único intensivo de CPU, parede e usuário são praticamente os mesmos

  • para 2 núcleos, o usuário tem cerca de duas paredes, o que significa que o tempo do usuário é contado em todos os segmentos.

    o usuário basicamente dobrou e, enquanto o muro permaneceu o mesmo.

  • isso continua até 8 threads, o que corresponde ao meu número de hyperthreads no meu computador.

    Depois das 8, o muro começa a aumentar também, porque não temos CPUs extras para colocar mais trabalho em um determinado período de tempo!

    A razão de platôs neste momento.

Observe que este gráfico é tão claro e simples porque o trabalho é puramente vinculado à CPU: se fosse vinculado à memória, teríamos uma queda no desempenho muito mais cedo com menos núcleos, porque os acessos à memória seriam um gargalo, como mostrado em O que os termos "CPU bound" e "I / O bound" significam?

Sys trabalho pesado com sendfile

A carga de trabalho mais pesada do sistema que eu pude criar foi usar o sendfile, que executa uma operação de cópia de arquivo no espaço do kernel: Copie um arquivo de maneira sã, segura e eficiente

Então, imaginei que esse kernel memcpyseria uma operação intensiva da CPU.

Primeiro, inicializo um arquivo aleatório grande de 10 GiB com:

dd if=/dev/urandom of=sendfile.in.tmp bs=1K count=10M

Em seguida, execute o código:

#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char *source_path, *dest_path;
    int source, dest;
    struct stat stat_source;
    if (argc > 1) {
        source_path = argv[1];
    } else {
        source_path = "sendfile.in.tmp";
    }
    if (argc > 2) {
        dest_path = argv[2];
    } else {
        dest_path = "sendfile.out.tmp";
    }
    source = open(source_path, O_RDONLY);
    assert(source != -1);
    dest = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    assert(dest != -1);
    assert(fstat(source, &stat_source) != -1);
    assert(sendfile(dest, source, 0, stat_source.st_size) != -1);
    assert(close(source) != -1);
    assert(close(dest) != -1);
    return EXIT_SUCCESS;
}

GitHub upstream .

que fornece basicamente o tempo do sistema conforme o esperado:

real    0m2.175s
user    0m0.001s
sys     0m1.476s

Eu também estava curioso para ver se timedistinguiria os syscalls de diferentes processos, então tentei:

time ./sendfile.out sendfile.in1.tmp sendfile.out1.tmp &
time ./sendfile.out sendfile.in2.tmp sendfile.out2.tmp &

E o resultado foi:

real    0m3.651s
user    0m0.000s
sys     0m1.516s

real    0m4.948s
user    0m0.000s
sys     0m1.562s

O tempo do sistema é praticamente o mesmo para um único processo, mas o tempo da parede é maior porque os processos estão competindo pelo acesso de leitura do disco.

Portanto, parece que de fato explica qual processo iniciou um determinado trabalho do kernel.

Código-fonte Bash

Quando você faz apenas time <cmd>no Ubuntu, ele usa a palavra-chave Bash, como pode ser visto em:

type time

quais saídas:

time is a shell keyword

Então nós grep source no código-fonte do Bash 4.19 para a string de saída:

git grep '"user\b'

o que nos leva à função execute_cmd.ctime_command , que usa:

  • gettimeofday()e getrusage()se ambos estiverem disponíveis
  • times() de outra forma

todas chamadas de sistema Linux e funções POSIX .

Código fonte GNU Coreutils

Se o chamarmos como:

/usr/bin/time

então ele usa a implementação GNU Coreutils.

Este é um pouco mais complexo, mas a fonte relevante parece estar em resuse.c e funciona:

  • uma wait3chamada BSD não POSIX, se disponível
  • timese de gettimeofdayoutra forma
Ciro Santilli adicionou uma nova foto
fonte
14

Real mostra o tempo total de retorno de um processo; enquanto o usuário mostra o tempo de execução das instruções definidas pelo usuário e o Sys é o tempo de execução das chamadas do sistema!

O tempo real também inclui o tempo de espera (o tempo de espera para E / S, etc.)

susenj
fonte