2203 votes

Que signifient 'real', 'user' et 'sys' dans la sortie de time(1) ?

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

Lequel de ces trois éléments est significatif pour l'évaluation de mon application ?

2 votes

Comment puis-je accéder à un seul d'entre eux ? par exemple en temps réel ?

4 votes

19 votes

Si votre programme se termine aussi rapidement, aucune de ces mesures n'est significative, il s'agit simplement de frais de démarrage. Si vous voulez mesurer l'ensemble du programme avec time pour qu'il fasse quelque chose qui prendra au moins une seconde.

2591voto

Statistiques sur le temps de traitement réel, utilisateur et système

L'une de ces choses n'est pas comme l'autre. Real se réfère au temps réel écoulé ; User et Sys se réfèrent au temps CPU utilisé. uniquement par le processus.

  • Real est le temps de 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 la fin des E/S).

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

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

User+Sys vous dira combien de temps réel de CPU votre processus a utilisé. Notez que ce temps est calculé sur tous les processeurs, donc si le processus a plusieurs threads, il peut potentiellement dépasser le temps d'horloge indiqué par la fonction Real . Notez que dans la sortie, ces chiffres incluent le User et Sys de tous les processus enfants également, bien que les appels système sous-jacents renvoient les statistiques pour le processus et ses enfants séparément.

Origines des statistiques rapportées par time (1)

Les statistiques rapportées par time sont recueillies à partir de divers appels système. User " et " Sys " proviennent de wait (2) ou times (2) selon le système en question. La valeur "réelle" est calculée à partir d'une heure de début et d'une heure de fin obtenues à partir de l'écran d'affichage. gettimeofday (2) appel. En fonction de la version du système, d'autres statistiques telles que le nombre de changements de contexte peuvent également être collectées par time .

Sur une machine multiprocesseur, un processus multithread ou un processus bifurquant des enfants pourrait avoir un temps écoulé inférieur au temps total du CPU - car différents threads ou processus peuvent fonctionner en parallèle. En outre, les statistiques de temps rapportées proviennent de différentes origines, de sorte que les temps enregistrés pour des tâches de très courte durée peuvent être sujets à des erreurs d'arrondi, comme le montre l'exemple donné par le posteur original.

Une brève introduction au mode Kernel vs. mode Utilisateur

Sur Unix, ou tout autre système d'exploitation à mémoire protégée, Kernel" ou "Superviseur". fait référence à un mode privilégié dans laquelle l'unité centrale peut fonctionner. Certaines actions privilégiées susceptibles d'affecter la sécurité ou la stabilité ne peuvent être effectuées que lorsque l'UC fonctionne dans ce mode ; ces actions ne sont pas accessibles au code d'application. Un exemple d'une telle action pourrait être de manipuler la fonction MMU pour accéder à l'espace d'adressage d'un autre processus. Normalement, mode utilisateur ne peut pas le faire (avec de bonnes raisons), bien qu'il puisse demander Mémoire partagée du noyau, 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 par un mécanisme sécurisé et les deux processus doivent explicitement s'y attacher pour pouvoir l'utiliser.

Le mode privilégié est généralement appelé mode "noyau" parce que le noyau est exécuté par l'unité centrale de traitement qui fonctionne dans ce mode. Afin de passer en mode noyau, vous devez émettre une instruction spécifique (souvent appelée piège ) qui fait passer l'unité centrale en mode noyau. et exécute le code à partir d'un emplacement spécifique contenu dans une table de saut. Pour des raisons de sécurité, vous ne pouvez pas passer en mode noyau et exécuter du code arbitraire - les pièges sont gérés par une table d'adresses dans laquelle il est impossible d'écrire, sauf si l'unité centrale fonctionne en mode superviseur. Les pièges sont gérés par une table d'adresses dans laquelle il est impossible d'écrire, à moins que le CPU ne fonctionne en mode superviseur. Vous utilisez un numéro de piège 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 (notamment ceux décrits dans la section 2 des pages de manuel) ont une composante en mode utilisateur, qui correspond à ce que vous appelez réellement depuis votre programme C. En coulisse, 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 toujours du code qui s'exécute en mode utilisateur. Il est également tout à fait possible d'émettre directement un piège vers le mode noyau à partir de n'importe quel code de l'espace utilisateur si vous le souhaitez, bien que vous deviez écrire un bout de langage d'assemblage pour configurer les registres correctement pour l'appel. Une page décrivant les appels système fournis par le noyau Linux et les conventions de configuration des registres peut être consultée à l'adresse suivante ici.

Plus d'informations sur 'sys'.

Il y a des choses que votre code ne peut pas faire en mode utilisateur - des choses comme l'allocation de mémoire ou l'accès au matériel (disque dur, réseau, etc.). Ces choses sont sous la supervision du noyau, et lui seul peut les faire. Certaines opérations que vous effectuez (comme malloc ou fread / fwrite ) invoquera ces fonctions du noyau et cela comptera alors comme du temps 'sys'. Malheureusement, ce n'est pas aussi simple que "chaque appel à malloc sera compté dans le temps 'sys'". L'appel à malloc effectuera son propre traitement (toujours comptabilisé dans le temps "utilisateur") et, quelque part, appellera la fonction dans le noyau (comptabilisé dans le temps "sys"). Après le retour de l'appel au noyau, il y aura encore un peu de temps en 'user', puis en 'sys'. malloc retournera à votre code. Quant à savoir quand la commutation se produit, et combien de temps elle passe en mode noyau... vous ne pouvez pas le dire. Cela dépend de l'implémentation de la bibliothèque. De plus, d'autres fonctions apparemment innocentes peuvent également utiliser malloc et autres en arrière-plan, qui auront à nouveau du temps dans 'sys'.

20 votes

Le temps passé par les processus enfants compte-t-il dans le temps réel/sys ?

1 votes

@ron - Selon la page de manuel de Linux, il agrège les temps 'c' avec les temps de processus, donc je pense qu'il le fait. Les temps des parents et des enfants sont disponibles séparément de l'appel times(2), cependant. Je suppose que la version Solaris/SysV de time(1) fait quelque chose de similaire.

0 votes

@ron Je viens de modifier la réponse : Le temps passé par les processus enfants et leurs descendants comptent seulement alors que le temps aurait pu être collecté par wait(2) ou waitpid(2), et récursivement avec les descendants. Cela implique que les processus fils doivent s'être terminés. Par exemple, on peut comparer time sh -c 'foo & sleep 1' y time sh -c 'foo & sleep 2'foo est une commande qui prend du temps CPU entre 1 et 2 secondes. Le premier produit quelque chose autour de 0.

362voto

lensovet Points 873

Puisque je n'ai pas assez de représentants pour commenter la réponse la plus élevée, je voulais juste fournir une autre raison pour laquelle realuser + sys .

Gardez à l'esprit que real représente le temps réel écoulé, tandis que user et sys représentent le temps d'exécution du CPU. Par conséquent, sur un système multicœur, la valeur de user et/ou sys temps (ainsi que leur somme) peuvent en fait dépasser le temps réel. Par exemple, sur une application Java que j'exécute pour la classe, j'obtiens cet ensemble de valeurs :

real    1m47.363s
user    2m41.318s
sys     0m4.013s

17 votes

Je me suis toujours posé cette question. Puisque je sais que mes programmes sont mono-threading, la différence entre le temps utilisateur et le temps réel doit être une surcharge de la VM, n'est-ce pas ?

19 votes

Pas nécessairement ; la JVM de Sun sur les machines Solaris ainsi que la JVM d'Apple sur Mac OS X parviennent à utiliser plus d'un cœur même dans les applications à un seul fil. Si vous faites un échantillon d'un processus Java, vous verrez que des choses comme la collecte des déchets s'exécutent sur des threads séparés (et d'autres choses aussi dont je ne me souviens plus). Je ne sais pas si vous voulez vraiment appeler cela "VM overhead".

0 votes

@Quantum7 - non, pas nécessairement. Voir mon message ci-dessus. Real est le temps écoulé, user et sys sont des statistiques de tranches de temps accumulées à partir du temps CPU que le processus utilise réellement.

80voto

varun Points 1446

- real : 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 CPUs pendant le calcul.

- sys : Le temps cumulé passé par toutes les unités centrales 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, comme plusieurs processeurs peuvent travailler en parallèle.

0 votes

sys est le temps CPU passé dans les appels système (et les gestionnaires de fautes de page ?)

4 votes

real est souvent décrite comme une heure "d'horloge murale".

62voto

Ciro Santilli Points 3341

Exemples POSIX C minimaux exécutables

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

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 Samsung M471A2K43BB1-CRC RAM (2x 16GiB).

sleep syscall

Le sommeil non-occupé tel que fait par le sleep syscall ne compte que dans real mais pas pour user o sys .

Par exemple, un programme qui dort pendant une seconde :

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

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

GitHub en amont .

sort quelque chose comme :

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

Il en va de même pour les programmes bloqués sur IO qui deviennent disponibles.

Par exemple, le programme suivant attend que l'utilisateur saisisse un caractère et appuie sur la touche 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 sort comme l'exemple de sommeil quelque chose comme :

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

C'est pourquoi time peut vous aider à distinguer les programmes liés au processeur et ceux liés aux entrées-sorties : Que signifient les termes "lié au processeur" et "lié aux E/S" ?

Fils multiples

L'exemple suivant fait niters itérations de travail inutile purement lié au CPU sur nthreads fils :

#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 amont + code de la parcelle .

Ensuite, nous traçons wall, user et sys en fonction du nombre de threads pour un nombre fixe de 10^10 itérations sur mon CPU à 8 hyperthreads :

enter image description here

Données de traçage .

D'après le graphique, nous voyons que :

  • pour une application mono-coeur intensive en CPU, le mur et l'utilisateur sont à peu près identiques

  • pour 2 cœurs, l'utilisateur est environ 2x le 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 au nombre d'hyperthreads de mon ordinateur.

    Après 8, le mur commence à augmenter aussi, parce que nous n'avons pas de CPUs supplémentaires pour mettre plus de travail dans une quantité donnée de temps !

    Le rapport se stabilise à ce stade.

Notez que ce graphique n'est aussi clair et simple que parce que le travail est purement lié à l'unité centrale : s'il était lié à la mémoire, nous aurions une baisse de performance beaucoup plus tôt avec moins de cœurs, car les accès à la mémoire seraient un goulot d'étranglement, comme le montre l'illustration ci-dessous. Que signifient les termes "lié au processeur" et "lié aux E/S" ?

Vérifier rapidement ce mur < utilisateur est un moyen simple de déterminer qu'un programme est multithreadé, et plus ce ratio est proche du nombre de cœurs, plus la parallélisation est efficace, par ex :

Sys heavy work with sendfile

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

J'ai donc imaginé que ce noyau interne memcpy sera une opération gourmande en ressources CPU.

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

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

Ensuite, exécutez 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 aussi curieux de voir si time distinguerait les syscalls 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 wall est plus important parce que les processus sont en compétition pour l'accès à la lecture du disque.

Il semble donc qu'il tienne compte du processus qui a lancé un travail donné du noyau.

Code source de Bash

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

type time

qui sort :

time is a shell keyword

Donc, nous grep source dans le code source de Bash 4.19 pour la chaîne de sortie :

git grep '"user\b'

ce qui nous amène à execute_cmd.c fonction time_command qui utilise :

  • gettimeofday() y getrusage() si les deux sont disponibles
  • times() sinon

qui sont tous Appels système Linux y Fonctions POSIX .

Code source de GNU Coreutils

Si nous l'appelons ainsi :

/usr/bin/time

alors il utilise l'implémentation de GNU Coreutils.

Celle-ci est un peu plus complexe, mais la source pertinente semble être à l'adresse suivante resuse.c et c'est le cas :

Pour rendre les choses plus concrètes, je veux exemplifier quelques cas extrêmes de time avec quelques programmes de test C minimaux.

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 Samsung M471A2K43BB1-CRC RAM (2x 16GiB).

dormir

Le sommeil non occupé ne compte pas dans les deux cas user o sys seulement real .

Par exemple, un programme qui dort pendant une seconde :

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

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

GitHub en amont .

sort quelque chose comme :

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

Il en va de même pour les programmes bloqués sur IO qui deviennent disponibles.

Par exemple, le programme suivant attend que l'utilisateur saisisse un caractère et appuie sur la touche 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 sort comme l'exemple de sommeil quelque chose comme :

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

C'est pourquoi time peut vous aider à distinguer les programmes liés au processeur et ceux liés aux entrées-sorties : Que signifient les termes "lié au processeur" et "lié aux E/S" ?

Fils multiples

L'exemple suivant fait niters itérations de travail inutile purement lié au CPU sur nthreads fils :

#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 amont + code de la parcelle .

Ensuite, nous traçons wall, user et sys en fonction du nombre de threads pour un nombre fixe de 10^10 itérations sur mon CPU à 8 hyperthreads :

enter image description here

Données de traçage .

D'après le graphique, nous voyons que :

  • pour une application mono-coeur intensive en CPU, le mur et l'utilisateur sont à peu près identiques

  • pour 2 cœurs, l'utilisateur est environ 2x le 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 au nombre d'hyperthreads de mon ordinateur.

    Après 8, le mur commence à augmenter aussi, parce que nous n'avons pas de CPUs supplémentaires pour mettre plus de travail dans une quantité donnée de temps !

    Le rapport se stabilise à ce stade.

Notez que ce graphique n'est aussi clair et simple que parce que le travail est purement lié à l'unité centrale : s'il était lié à la mémoire, nous aurions une baisse de performance beaucoup plus tôt avec moins de cœurs, car les accès à la mémoire seraient un goulot d'étranglement, comme le montre l'illustration ci-dessous. Que signifient les termes "lié au processeur" et "lié aux E/S" ?

Vérifier rapidement ce mur < utilisateur est un moyen simple de déterminer qu'un programme est multithreadé, et plus ce ratio est proche du nombre de cœurs, plus la parallélisation est efficace, par ex :

Sys heavy work with sendfile

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

Donc j'ai imaginé que ce noyau interne memcpy sera une opération gourmande en ressources CPU.

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

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

Ensuite, exécutez 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 aussi curieux de voir si time distinguerait les syscalls 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 wall est plus important parce que les processus sont en compétition pour l'accès à la lecture du disque.

Il semble donc qu'il tienne compte du processus qui a lancé un travail donné du noyau.

Code source de Bash

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

type time

qui sort :

time is a shell keyword

Donc, nous grep source dans le code source de Bash 4.19 pour la chaîne de sortie :

git grep '"user\b'

ce qui nous amène à execute_cmd.c fonction time_command qui utilise :

  • gettimeofday() y getrusage() si les deux sont disponibles
  • times() sinon

qui sont tous Appels système Linux y Fonctions POSIX .

Code source de GNU Coreutils

Si nous l'appelons ainsi :

/usr/bin/time

alors il utilise l'implémentation de GNU Coreutils.

Celle-ci est un peu plus complexe, mais la source pertinente semble être à l'adresse suivante resuse.c et c'est le cas :

  • un BSD non-POSIX wait3 appeler si cela est disponible
  • times y gettimeofday sinon

1 votes

Réponse beaucoup trop détaillée pour une question simple, voir les réponses ci-dessous

17voto

susenj Points 41

Real indique le temps d'exécution total d'un processus ; tandis que User montre le temps d'exécution des 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.).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X