¿Qué significan 'real', 'user' y 'sys' en la salida del tiempo (1)?

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

¿Qué significan 'real', 'usuario' y 'sys' en la salida del tiempo?

¿Cuál es significativo al comparar mi aplicación?

rayryeng
fuente
2
¿Cómo puedo acceder solo a uno de ellos? por ejemplo solo en tiempo real?
Mojtaba Ahmadi
1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi
2
@Casillass Real - stackoverflow.com/questions/2408981/…
ConcernedOfTunbridgeWells
77
Si su programa sale tan rápido, ninguno de ellos es significativo, todo es solo una sobrecarga de inicio. Si desea medir todo el programa time, haga que haga algo que tome al menos un segundo.
Peter Cordes
55
Es realmente importante tener en cuenta que timees una palabra clave bash. Por lo que escribir man timees que no le da una página del manual para la fiesta time, más bien se está dando a la página del manual /usr/bin/time. Esto me ha hecho tropezar.
irritable_phd_syndrom

Respuestas:

2065

Estadísticas de tiempo de proceso real, usuario y sistema

Una de estas cosas no es como la otra. Real se refiere al tiempo transcurrido real; Usuario y Sys se refieren al tiempo de CPU utilizado solo por el proceso.

  • Real es la hora del reloj de pared: tiempo desde el inicio hasta el final de la llamada. Este es todo el tiempo transcurrido, incluidos los segmentos de tiempo utilizados por otros procesos y el tiempo que el proceso pasa bloqueado (por ejemplo, si está esperando que se complete la E / S).

  • Usuario es la cantidad de tiempo de CPU gastado en código de modo de usuario (fuera del núcleo) dentro del proceso. Este es solo el tiempo real de CPU utilizado en la ejecución del proceso. Otros procesos y el tiempo que el proceso pasa bloqueado no cuentan para esta cifra.

  • Sys es la cantidad de tiempo de CPU que se pasa en el núcleo dentro del proceso. Esto significa ejecutar el tiempo de CPU gastado en las llamadas del sistema dentro del núcleo, a diferencia del código de la biblioteca, que todavía se ejecuta en el espacio del usuario. Al igual que 'usuario', este es solo el tiempo de CPU utilizado por el proceso. Consulte a continuación una breve descripción del modo kernel (también conocido como modo 'supervisor') y el mecanismo de llamada del sistema.

User+Sysle dirá cuánto tiempo real de CPU usó su proceso. Tenga en cuenta que esto Realocurre en todas las CPU, por lo que si el proceso tiene varios subprocesos (y este proceso se ejecuta en una computadora con más de un procesador), podría exceder el tiempo de reloj de pared informado (que generalmente ocurre). Tenga en cuenta que en el resultado estas cifras incluyen el tiempo Usery el Systiempo de todos los procesos secundarios (y sus descendientes), así como cuándo podrían haberse recopilado, por ejemplo, mediante wait(2)o waitpid(2), aunque las llamadas al sistema subyacentes devuelven las estadísticas del proceso y sus elementos secundarios por separado.

Orígenes de las estadísticas reportadas por time (1)

Las estadísticas informadas por timese recopilan de varias llamadas al sistema. 'Usuario' y 'Sys' provienen de wait (2)( POSIX ) o times (2)( POSIX ), dependiendo del sistema en particular. 'Real' se calcula a partir de una hora de inicio y finalización recopilada de la gettimeofday (2)llamada. Dependiendo de la versión del sistema, también se pueden recopilar otras estadísticas como el número de cambios de contexto time.

En una máquina multiprocesador, un proceso multiproceso o un proceso que bifurca a los niños podría tener un tiempo transcurrido menor que el tiempo total de la CPU, ya que diferentes subprocesos o procesos pueden ejecutarse en paralelo. Además, las estadísticas de tiempo informadas provienen de diferentes orígenes, por lo que los tiempos registrados para tareas de ejecución muy cortas pueden estar sujetos a errores de redondeo, como muestra el ejemplo dado por el póster original.

Una breve introducción sobre el modo Kernel vs. Usuario

En Unix, o en cualquier sistema operativo de memoria protegida, el modo 'Kernel' o 'Supervisor' se refiere a un modo privilegiado en el que la CPU puede operar. Ciertas acciones privilegiadas que podrían afectar la seguridad o la estabilidad solo pueden realizarse cuando la CPU está operando en este modo Estas acciones no están disponibles para el código de la aplicación. Un ejemplo de tal acción podría ser la manipulación de la MMU para obtener acceso al espacio de direcciones de otro proceso. Normalmente, el código de modo de usuario no puede hacer esto (con buena razón), aunque puede solicitar memoria compartida del núcleo, lo que podríaser leído o escrito por más de un proceso. En este caso, la memoria compartida se solicita explícitamente desde el núcleo a través de un mecanismo seguro y ambos procesos deben adjuntarse explícitamente para poder usarla.

El modo privilegiado generalmente se conoce como modo 'núcleo' porque el núcleo lo ejecuta la CPU que se ejecuta en este modo. Para cambiar al modo kernel, debe emitir una instrucción específica (a menudo llamada trampa ) que cambia la CPU para que se ejecute en modo kernel y ejecuta código desde una ubicación específica contenida en una tabla de salto. Por razones de seguridad, no puede cambiar al modo kernel y ejecutar código arbitrario: las trampas se administran a través de una tabla de direcciones en las que no se puede escribir a menos que la CPU se ejecute en modo supervisor. Atrapa con un número de trampa explícito y la dirección se busca en la tabla de salto; el núcleo tiene un número finito de puntos de entrada controlados.

Las llamadas al 'sistema' en la biblioteca C (particularmente las descritas en la Sección 2 de las páginas del manual) tienen un componente de modo de usuario, que es lo que realmente llama desde su programa C. Detrás de escena, pueden emitir una o más llamadas del sistema al núcleo para realizar servicios específicos como E / S, pero también tienen código ejecutándose en modo de usuario. También es bastante posible emitir directamente una trampa al modo kernel desde cualquier código de espacio de usuario si lo desea, aunque es posible que deba escribir un fragmento de lenguaje ensamblador para configurar los registros correctamente para la llamada.

Más acerca de 'sys'

Hay cosas que su código no puede hacer desde el modo de usuario, como asignar memoria o acceder al hardware (HDD, red, etc.). Estos están bajo la supervisión del núcleo, y solo ellos pueden hacerlos. Algunas operaciones como malloco fread/ fwriteinvocarán estas funciones del núcleo y luego contarán como tiempo 'sys'. Desafortunadamente, no es tan simple como "cada llamada a malloc se contará en el tiempo 'sys'". La llamada a mallocrealizará un procesamiento propio (todavía contado en el tiempo del 'usuario') y luego en algún punto del camino puede llamar a la función en el núcleo (contado en el tiempo 'sys'). Después de regresar de la llamada del kernel, habrá más tiempo en 'usuario' y luegomallocvolverá a su código En cuanto a cuándo ocurre el cambio, y cuánto se gasta en modo kernel ... no se puede decir. Depende de la implementación de la biblioteca. Además, otras funciones aparentemente inocentes también podrían usar mallocy similares en el fondo, que nuevamente tendrá algo de tiempo en 'sys'.

TunbridgeWells
fuente
15
¿El tiempo dedicado por los procesos secundarios cuenta en real / sys?
ron
1
@ron: según la página de manual de Linux, agrega los tiempos 'c' con los tiempos de proceso, así que creo que sí. Sin embargo, las horas principales y las secundarias están disponibles por separado de la llamada times (2). Supongo que la versión de Solaris / SysV del tiempo (1) hace algo similar.
Preocupado por
3
User + Sys le permite medir el uso de CPU de un proceso. Puede usarlo para comparar el rendimiento. Esto es particularmente útil para el código multiproceso donde más de un núcleo de CPU podría estar trabajando en un cálculo.
Preocupado por
1
Sin embargo, no es precisamente sobre el tema: la ejecución de "\ time <cmd>" es interesante; proporciona más detalles: (perdone el formato deficiente en el comentario): $ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps real 0m0.025s usuario 0m0.004s sys 0m0.018s $ \ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 tiempo 11462 pts / 19 00:00:00 ps 0.00usuario 0.01sistema 0: 00.02elapsado 95% CPU (0avgtext + 0avgdata 2160maxresident) k 0inputs + 0outputs (0major + 103minor) pagefaults 0swaps $
kaiwan
1
(Se quedó sin caracteres en el comentario anterior): ¿Más detalles? Utilice perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
kaiwan
286

Para ampliar la respuesta aceptada , solo quería proporcionar otra razón por la cual realuser+ sys.

Tenga en cuenta que realrepresenta el tiempo transcurrido real, mientras que usery sysvalores representan el tiempo de ejecución de la CPU. Como resultado, en un sistema multinúcleo, el tiempo usery / o systiempo (así como su suma) pueden exceder el tiempo real. Por ejemplo, en una aplicación Java que estoy ejecutando para la clase, obtengo este conjunto de valores:

real    1m47.363s
user    2m41.318s
sys     0m4.013s
lensovet
fuente
11
Siempre me había preguntado sobre esto. Como sé que mis programas tienen un solo subproceso, la diferencia entre el usuario y el tiempo real debe ser la sobrecarga de la máquina virtual, ¿correcto?
Quantum7
99
no necesariamente; Sun JVM en máquinas Solaris, así como Apple JVM en Mac OS X, logra usar más de un núcleo incluso en aplicaciones de subproceso único. Si hace una muestra de un proceso de Java, verá que cosas como la recolección de basura se ejecutan en subprocesos separados (y algunas otras cosas también que no recuerdo fuera de mi cabeza). Sin embargo, no sé si realmente quieres llamar a esa "sobrecarga de VM".
lensovet
44
Supongo que la cantidad de votos positivos le dio suficiente reputación ahora: D. Entonces, ¿qué opinas sobre realexcedente usery systotal? La sobrecarga del sistema operativo, como el cambio de contexto de subproceso puede ser?
Muhammad Gelbana
19
Otro problema potencial podría ser la E / S: si su aplicación pasa mucho tiempo esperando recibir un archivo o una transmisión, entonces obviamente el tiempo real excedería en gran medida el tiempo del usuario / sys porque no se usa tiempo de CPU mientras espera para obtener acceso a un archivo o algo similar.
lensovet
1
@MuhammadGelbana: esto puede suceder si se bloquea la ejecución de la aplicación por algún motivo. Por ejemplo, si está esperando conexiones de E / S, IPC o socket, permanecerá inactivo y no acumulará tiempo de CPU hasta que vuelva la llamada de bloqueo.
Preocupado por
41

real : el tiempo real empleado en ejecutar el proceso de principio a fin, como si lo midiera un humano con un cronómetro

usuario : el tiempo acumulado empleado por todas las CPU durante el cálculo

sys : el tiempo acumulado que pasan todas las CPU durante las tareas relacionadas con el sistema, como la asignación de memoria.

Tenga en cuenta que a veces user + sys puede ser mayor que real, ya que varios procesadores pueden funcionar en paralelo.

varun
fuente
sys¿Se pasa tiempo de CPU en llamadas al sistema (y manejadores de fallas de página?)
Peter Cordes
1
reala menudo se describe como tiempo de "reloj de pared".
Peter Cordes
30

Ejemplos mínimos de POSIX C ejecutables

Para hacer las cosas más concretas, quiero ejemplificar algunos casos extremos timecon algunos programas mínimos de prueba C.

Todos los programas se pueden compilar y ejecutar con:

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

y han sido probados en 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 hilos), 2x Samsung M471A2K43BB1-CRC RAM (2x 16GiB).

dormir

El sueño no ocupado no cuenta en ninguno usero syssolo real.

Por ejemplo, un programa que duerme un segundo:

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

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

GitHub aguas arriba .

produce algo como:

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

Lo mismo vale para los programas bloqueados cuando IO está disponible.

Por ejemplo, el siguiente programa espera a que el usuario ingrese un carácter y presione enter:

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

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

GitHub aguas arriba .

Y si espera aproximadamente un segundo, se genera como en el ejemplo de reposo algo así como:

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

Por esta razón, timepuede ayudarlo a distinguir entre los programas vinculados a la CPU y a las E / S: ¿Qué significan los términos "CPU vinculados" y "E / S atados"?

Hilos múltiples

El siguiente ejemplo realiza nitersiteraciones de trabajo inútil puramente vinculado a la CPU en nthreadssubprocesos:

#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 trama .

Luego graficamos wall, user y sys en función del número de subprocesos para un 10 ^ 10 iteraciones fijas en mi 8 CPU hyperthread:

ingrese la descripción de la imagen aquí

Trazar datos .

Del gráfico, vemos que:

  • para una aplicación de núcleo único intensivo de CPU, el muro y el usuario son casi lo mismo

  • para 2 núcleos, el usuario mide aproximadamente 2 veces la pared, lo que significa que el tiempo del usuario se cuenta en todos los subprocesos.

    El usuario básicamente se duplicó, y mientras que la pared se mantuvo igual.

  • esto continúa hasta 8 subprocesos, lo que coincide con mi número de hyperthreads en mi computadora.

    Después de 8, el muro comienza a aumentar también, ¡porque no tenemos CPU adicionales para poner más trabajo en un período de tiempo determinado!

    La relación se estabiliza en este punto.

Tenga en cuenta que este gráfico es tan claro y simple porque el trabajo está puramente vinculado a la CPU: si estuviera vinculado a la memoria, obtendríamos una caída en el rendimiento mucho antes con menos núcleos porque los accesos a la memoria serían un cuello de botella como se muestra en What Qué significan los términos "enlazado a la CPU" y "enlazado de E / S"?

Sys trabajo pesado con sendfile

La carga de trabajo de sys más pesada que se me ocurrió fue usar el sendfile, que realiza una operación de copia de archivos en el espacio del kernel: copie un archivo de una manera sana, segura y eficiente

Así que imaginé que este en el núcleo memcpyserá una operación intensiva de la CPU.

Primero inicializo un gran archivo aleatorio de 10GiB con:

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

Luego ejecuta el 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 aguas arriba .

que proporciona básicamente el tiempo del sistema como se esperaba:

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

También tenía curiosidad por ver si timedistinguiría entre syscalls de diferentes procesos, así que intenté:

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

Y el resultado fue:

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

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

El tiempo del sistema es casi el mismo para ambos que para un solo proceso, pero el tiempo del muro es mayor porque los procesos compiten por un acceso de lectura de disco probable.

Por lo tanto, parece que de hecho explica qué proceso inició un trabajo de núcleo dado.

Código fuente de Bash

Cuando lo hace solo time <cmd>en Ubuntu, usa la palabra clave Bash como se puede ver en:

type time

que salidas:

time is a shell keyword

Entonces grep fuente en el código fuente Bash 4.19 para la cadena de salida:

git grep '"user\b'

lo que nos lleva a la función execute_cmd.ctime_command , que usa:

  • gettimeofday()y getrusage()si ambos están disponibles
  • times() de otra manera

todos los cuales son llamadas al sistema Linux y funciones POSIX .

Código fuente de GNU Coreutils

Si lo llamamos como:

/usr/bin/time

entonces usa la implementación GNU Coreutils.

Este es un poco más complejo, pero la fuente relevante parece estar en resuse.c y lo hace:

  • una wait3llamada BSD no POSIX si está disponible
  • timesy de lo gettimeofdaycontrario
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
14

Real muestra el tiempo total de respuesta para un proceso; mientras que el usuario muestra el tiempo de ejecución de las instrucciones definidas por el usuario y Sys es el tiempo para ejecutar las llamadas al sistema.

El tiempo real también incluye el tiempo de espera (el tiempo de espera para E / S, etc.)

susenj
fuente