Que signifient «réel», «utilisateur» et «sys» dans la sortie du temps (1)?

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

Que signifient «réel», «utilisateur» et «sys» dans la sortie du temps?

Lequel est significatif lors de l'analyse comparative de mon application?

rayryeng
la source
2
comment puis-je accéder à l'un d'entre eux seulement? par exemple juste en temps réel?
Mojtaba Ahmadi
1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi
2
@Casillass Real - stackoverflow.com/questions/2408981/…
ConcernedOfTunbridgeWells
7
Si votre programme se termine aussi rapidement, aucun d'entre eux n'a de sens, c'est juste une surcharge de démarrage. Si vous souhaitez mesurer l'ensemble du programme avec time, faites-le faire quelque chose qui prendra au moins une seconde.
Peter Cordes
5
Il est vraiment important de noter qu'il times'agit d'un mot-clé bash. Donc , frappe man timeest pas vous donner une page de manuel pour le bash time, plutôt il donne la page de manuel /usr/bin/time. Cela m'a fait trébucher.
irritable_phd_syndrom

Réponses:

2066

Statistiques de temps de traitement réel, utilisateur et Sys

Une de ces choses n'est pas comme les autres. Réel fait référence au temps réel écoulé; L'utilisateur et Sys font référence au temps CPU utilisé uniquement par le processus.

  • Le temps réel est l'horloge murale - le temps du début à la fin de l'appel. Il s'agit de tout le temps écoulé, y compris les tranches de temps utilisées par d'autres processus et le temps que le processus passe bloqué (par exemple, s'il attend que les E / S se terminent).

  • Utilisateur est la quantité de temps CPU passé en code en mode utilisateur (en dehors du noyau) dans le processus. Il s'agit uniquement du temps CPU réel utilisé lors de l'exécution du processus. Les autres processus et le temps que le processus passe bloqué ne comptent pas dans ce chiffre.

  • Sys est la quantité de temps CPU passé dans le noyau au sein du processus. Cela signifie exécuter le temps CPU consacré aux appels système dans le noyau, par opposition au code de bibliothèque, qui s'exécute toujours dans l'espace utilisateur. Comme «utilisateur», il s'agit uniquement du temps CPU utilisé par le processus. Voir ci-dessous pour une brève description du mode noyau (également appelé mode «superviseur») et du mécanisme d'appel système.

User+Sysvous dira combien de temps CPU réel votre processus a utilisé. Notez que cela concerne tous les processeurs, donc si le processus a plusieurs threads (et que ce processus s'exécute sur un ordinateur avec plus d'un processeur), il peut potentiellement dépasser le temps d'horloge murale signalé par Real(ce qui se produit généralement). Notez que dans la sortie, ces chiffres incluent leUserSys temps et l' heure de tous les processus enfants (et leurs descendants) ainsi que le moment où ils auraient pu être collectés, par exemple par wait(2)ou waitpid(2), bien que les appels système sous-jacents renvoient séparément les statistiques du processus et de ses enfants.

Origine des statistiques rapportées par time (1)

Les statistiques signalées par timesont recueillies à partir de divers appels système. «Utilisateur» et «Sys» proviennent de wait (2)( POSIX ) ou times (2)( POSIX ), selon le système particulier. «Réel» est calculé à partir des heures de début et de fin recueillies lors de l' gettimeofday (2)appel. Selon la version du système, diverses autres statistiques telles que le nombre de changements de contexte peuvent également être collectées par time.

Sur une machine multiprocesseur, un processus multi-thread ou un processus forking enfants peut avoir un temps écoulé inférieur au temps CPU total - car différents threads ou processus peuvent s'exécuter en parallèle. De plus, les statistiques de temps rapportées proviennent de différentes origines, de sorte que les temps enregistrés pour des tâches très courtes peuvent être sujets à des erreurs d'arrondi, comme le montre l'exemple de l'affiche originale.

Une brève introduction sur le mode noyau vs utilisateur

Sous Unix, ou tout système d'exploitation à mémoire protégée, le mode «noyau» ou «superviseur» fait référence à un mode privilégié dans lequel le processeur peut fonctionner. Certaines actions privilégiées qui pourraient affecter la sécurité ou la stabilité ne peuvent être effectuées que lorsque le processeur fonctionne en ce mode; ces actions ne sont pas disponibles pour le code d'application. Un exemple d'une telle action pourrait être la manipulation de la MMU pour accéder à l'espace d'adressage d'un autre processus. Normalement, le code en mode utilisateur ne peut pas faire cela (pour une bonne raison), bien qu'il puisse demander de la mémoire partagée au noyau, ce qui pourraitêtre lu ou écrit par plus d'un processus. Dans ce cas, la mémoire partagée est explicitement demandée au noyau via un mécanisme sécurisé et les deux processus doivent s'y attacher explicitement afin de l'utiliser.

Le mode privilégié est généralement appelé mode «noyau» car le noyau est exécuté par le processeur fonctionnant dans ce mode. Pour passer en mode noyau , vous devez donner une instruction spécifique (souvent appelé un piège ) qui passe la CPU à l' exécution en mode noyau et exécute le code à partir d' un emplacement spécifique tenu dans une table de saut. Pour des raisons de sécurité, vous ne pouvez pas basculer en mode noyau et exécuter du code arbitraire - les interruptions sont gérées via une table d'adresses qui ne peuvent pas être écrites à moins que le CPU ne s'exécute en mode superviseur. Vous interceptez avec un numéro d'interruption explicite et l'adresse est recherchée dans la table de saut; le noyau a un nombre fini de points d'entrée contrôlés.

Les appels «système» de la bibliothèque C (en particulier ceux décrits dans la section 2 des pages de manuel) ont un composant en mode utilisateur, qui est ce que vous appelez réellement à partir de votre programme C. Dans les coulisses, ils peuvent émettre un ou plusieurs appels système vers le noyau pour effectuer des services spécifiques tels que les E / S, mais ils ont également du code exécuté en mode utilisateur. Il est également tout à fait possible d'émettre directement une interruption en mode noyau à partir de n'importe quel code d'espace utilisateur si vous le souhaitez, bien que vous deviez peut-être écrire un extrait de langage assembleur pour configurer correctement les registres pour l'appel.

En savoir plus sur 'sys'

Il y a des choses que votre code ne peut pas faire à partir du mode utilisateur - des choses comme l'allocation de mémoire ou l'accès au matériel (disque dur, réseau, etc.). Ceux-ci sont sous la supervision du noyau, et lui seul peut les faire. Certaines opérations comme mallocou fread/fwrite invoqueront ces fonctions du noyau et compteront alors comme du temps «sys». Malheureusement, ce n'est pas aussi simple que "chaque appel à malloc sera compté en temps" sys ". L'appel à mallocfera un traitement de son propre chef (toujours compté dans le temps «utilisateur»), puis quelque part en cours, il peut appeler la fonction dans le noyau (compté dans le temps «sys»). Après être revenu de l'appel du noyau, il y aura un peu plus de temps dans «utilisateur», puismallocreviendra à votre code. Quant au moment où le changement se produit, et combien il est dépensé en mode noyau ... vous ne pouvez pas dire. Cela dépend de l'implémentation de la bibliothèque. En outre, d'autres fonctions apparemment innocentes pourraient également utiliser mallocet similaires en arrière-plan, qui auront alors encore du temps dans «sys».

ConcernedOfTunbridgeWells
la source
15
Le temps passé par les processus enfants compte-t-il dans real / sys?
ron
1
@ron - Selon la page de manuel Linux, il agrège les temps «c» avec les temps de processus, donc je pense que oui. Cependant, les heures des parents et des heures des enfants sont disponibles séparément des heures (2). Je suppose que la version Solaris / SysV de time (1) fait quelque chose de similaire.
ConcernedOfTunbridgeWells
3
User + Sys vous permet de mesurer l'utilisation du processeur d'un processus. Vous pouvez l'utiliser pour comparer les performances. Ceci est particulièrement utile pour le code multithread où plusieurs cœurs de processeur peuvent travailler sur un calcul.
ConcernedOfTunbridgeWells
1
Pas précisément sur le sujet, néanmoins: L'exécution de "\ time <cmd>" est intéressante - elle fournit plus de détails: (pardonnez le mauvais formatage dans le commentaire): $ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps réel 0m0.025s utilisateur 0m0.004s sys 0m0.018s $ \ temps ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 heure 11462 pts / 19 00:00:00 ps 0,00utilisateur 0,01 système 0: 00,02 CPU 95% écoulé (0avgtext + 0avgdata 2160maxresident) k 0inputs + 0outputs (0major + 103minor) pagefaults 0swaps $
kaiwan
1
(A manqué de caractères dans le commentaire précédent donc): Plus de détails? Utilisez perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
kaiwan
286

Pour développer la réponse acceptée , je voulais juste fournir une autre raison pour laquelle realuser+ sys.

Gardez à l'esprit que cela realreprésente le temps réel écoulé, tandis userque les sysvaleurs et représentent le temps d'exécution du processeur. Par conséquent, sur un système multicœur, l' heure useret / ou le systemps (ainsi que leur somme) peuvent réellement dépasser le temps réel. Par exemple, sur une application Java que je lance pour la classe, j'obtiens cet ensemble de valeurs:

real    1m47.363s
user    2m41.318s
sys     0m4.013s
lensovet
la source
11
Je m'étais toujours posé des questions à ce sujet. Étant donné que je sais que mes programmes sont à thread unique, la différence entre l'utilisateur et le temps réel doit être une surcharge de VM, n'est-ce pas?
Quantum7
9
pas nécessairement; la JVM Sun sur les machines Solaris ainsi que la JVM d'Apple sur Mac OS X parviennent à utiliser plusieurs cœurs, même dans les applications à thread unique. Si vous faites un exemple d'un processus java, vous verrez que des choses comme la récupération de place s'exécutent sur des threads séparés (et quelques autres choses dont je ne me souviens pas du haut de ma tête). Je ne sais pas si vous voulez vraiment qualifier cette «surcharge de VM».
lensovet
4
Je suppose que le nombre de votes positifs vous a donné suffisamment de réputation maintenant: D. Alors, que pensez-vous du realdépassement useret du systotal? Les frais généraux du système d'exploitation tels que le changement de contexte de thread peuvent être?
Muhammad Gelbana
19
Un autre problème potentiel pourrait être les E / S: si votre application passe beaucoup de temps à attendre pour recevoir un fichier ou un flux, il est évident que le temps réel dépasserait largement le temps utilisateur / système car aucun temps CPU n'est utilisé pendant l'attente pour obtenir l'accès à un fichier ou quelque chose de similaire.
lensovet
1
@MuhammadGelbana - cela peut se produire si l'application est bloquée pour l'exécution pour une raison quelconque. Par exemple, s'il attend des connexions d'E / S, d'IPC ou de socket, il restera inactif, n'accumulant pas de temps CPU jusqu'à ce que l'appel de blocage revienne.
ConcernedOfTunbridgeWells
41

réel : le temps réel passé à exécuter le processus du début à la fin, comme s'il était mesuré par un humain avec un chronomètre

utilisateur : le temps cumulé passé par tous les CPU pendant le calcul

sys : temps cumulé passé par tous les CPU pendant les tâches liées au système telles que l'allocation de mémoire.

Notez que parfois user + sys peut être plus grand que réel, car plusieurs processeurs peuvent fonctionner en parallèle.

varun
la source
sysle temps CPU est-il consacré aux appels système (et aux gestionnaires de défauts de page?)
Peter Cordes
1
realest souvent décrit comme le temps "horloge murale".
Peter Cordes
30

Exemples POSIX C exécutables au minimum

Pour rendre les choses plus concrètes, je veux illustrer quelques cas extrêmes de timequelques programmes de test C minimum.

Tous les programmes peuvent être compilés et exécutés avec:

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

et ont été testés dans Ubuntu 18.10, GCC 8.2.0, glibc 2.28, noyau Linux 4.18, ordinateur portable ThinkPad P51, CPU Intel Core i7-7820HQ (4 cœurs / 8 threads), 2x RAM Samsung M471A2K43BB1-CRC (2x 16GiB).

dormir

Sommeil non occupé ne compte pas dans les deux userou sysseulement real.

Par exemple, un programme qui dort une seconde:

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

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

GitHub en amont .

génère quelque chose comme:

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

Il en va de même pour les programmes bloqués lorsque les entrées-sorties deviennent disponibles.

Par exemple, le programme suivant attend que l'utilisateur entre un caractère et appuyez sur Entrée:

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

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

GitHub en amont .

Et si vous attendez environ une seconde, il produit exactement comme l'exemple de sommeil quelque chose comme:

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

Pour cette raison, vous timepouvez faire la distinction entre les programmes liés au processeur et aux E / S: que signifient les termes "lié au processeur" et "lié aux E / S"?

Plusieurs threads

L'exemple suivant effectue des nitersitérations de travaux inutiles purement liés au processeur sur les 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 en amont + code de tracé .

Ensuite, nous traçons wall, user et sys en fonction du nombre de threads pour une itération fixe de 10 ^ 10 sur mon 8 CPU hyperthread:

entrez la description de l'image ici

Tracer des données .

Du graphique, nous voyons que:

  • pour une application monocœur gourmande en CPU, le mur et l'utilisateur sont à peu près les mêmes

  • pour 2 cœurs, l'utilisateur est environ 2x mur, ce qui signifie que le temps de l'utilisateur est compté sur tous les threads.

    l'utilisateur a pratiquement doublé, et le mur est resté le même.

  • cela continue jusqu'à 8 threads, ce qui correspond à mon nombre d'hyperthreads dans mon ordinateur.

    Après 8, le mur commence également à augmenter, car nous n'avons pas de CPU supplémentaires pour mettre plus de travail dans un laps de temps donné!

    Le ratio plateaux à ce stade.

Notez que ce graphique n'est clair et simple que parce que le travail est purement lié au processeur: s'il était lié à la mémoire, nous obtiendrions une baisse des performances beaucoup plus tôt avec moins de cœurs car les accès à la mémoire seraient un goulot d'étranglement, comme indiqué dans What signifient les termes «lié au processeur» et «lié aux E / S»?

Sys travail lourd avec sendfile

La charge de travail sys la plus lourde que j'ai pu trouver était d'utiliser le sendfile, qui effectue une opération de copie de fichiers sur l'espace du noyau: Copiez un fichier de manière saine, sûre et efficace

J'ai donc imaginé que ce noyau memcpyserait une opération gourmande en CPU.

D'abord, j'initialise un grand fichier aléatoire de 10 Go avec:

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

Exécutez ensuite le code:

#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 en amont .

ce qui donne essentiellement du temps système comme prévu:

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

J'étais également curieux de voir si cela timeferait la distinction entre les appels système de différents processus, alors j'ai essayé:

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

Et le résultat a été:

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

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

Le temps sys est à peu près le même pour les deux que pour un seul processus, mais le temps du mur est plus grand car les processus sont probablement en concurrence pour l'accès en lecture sur disque.

Il semble donc que cela explique en fait quel processus a démarré un travail de noyau donné.

Code source Bash

Lorsque vous ne faites que time <cmd>sur Ubuntu, il utilise le mot-clé Bash comme on peut le voir sur:

type time

qui génère:

time is a shell keyword

Nous avons donc grep source dans le code source Bash 4.19 pour la chaîne de sortie:

git grep '"user\b'

ce qui nous amène à la fonction execute_cmd.ctime_command , qui utilise:

  • gettimeofday()et getrusage()si les deux sont disponibles
  • times() autrement

qui sont tous des appels système Linux et des fonctions POSIX .

Code source de GNU Coreutils

Si nous l'appelons comme:

/usr/bin/time

puis il utilise l'implémentation GNU Coreutils.

Celui-ci est un peu plus complexe, mais la source pertinente semble être à resuse.c et il le fait:

  • un wait3appel BSD non POSIX s'il est disponible
  • timeset gettimeofdaysinon
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
14

Réel montre le temps de rotation total pour un processus; tandis que l'utilisateur affiche le temps d'exécution pour les instructions définies par l'utilisateur et Sys est pour le temps d'exécution des appels système!

Le temps réel comprend également le temps d'attente (le temps d'attente pour les E / S, etc.)

susenj
la source