Co oznaczają „rzeczywisty”, „użytkownik” i „sys” w wyniku czasu (1)?


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

Co oznaczają „rzeczywisty”, „użytkownik” i „sys” w wyniku czasu?

Który z nich ma znaczenie podczas testowania mojej aplikacji?


2
jak mogę uzyskać dostęp tylko do jednego z nich? na przykład w czasie rzeczywistym?
Mojtaba Ahmadi

1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi


7
Jeśli twój program kończy się tak szybko, żaden z nich nie ma znaczenia, to wszystko po prostu narzut związany z uruchomieniem. Jeśli chcesz zmierzyć cały program time, zrób to, co zajmie co najmniej sekundę.
Peter Cordes,

5
Bardzo ważne jest, aby pamiętać, że timejest to słowo kluczowe bash. Więc wpisywanie man timesię nie daje stronie man bash time, a to daje na stronie man /usr/bin/time. To mnie potknęło.
irritable_phd_syndrom

Odpowiedzi:


2059

Statystyki czasu rzeczywistego, użytkownika i Sys

Jedna z tych rzeczy nie jest taka jak druga. Rzeczywisty odnosi się do rzeczywistego czasu, który upłynął; Użytkownik i Sys odnoszą się do czasu procesora wykorzystywanego tylko przez proces.

  • Rzeczywisty jest czas naścienny - czas od początku do końca połączenia. Jest to cały czas, który upłynął, w tym przedziały czasowe używane przez inne procesy oraz czas, jaki proces spędza zablokowany (na przykład, jeśli czeka na zakończenie operacji we / wy).

  • Użytkownik to ilość czasu procesora spędzona na kodzie trybu użytkownika (poza jądrem) w procesie. Jest to tylko rzeczywisty czas pracy procesora wykorzystywany do wykonania procesu. Inne procesy i czas zablokowany przez proces nie liczą się do tej liczby.

  • Sys to ilość czasu procesora spędzonego w jądrze w procesie. Oznacza to wykonywanie czasu procesora spędzonego na wywołaniach systemowych w jądrze, w przeciwieństwie do kodu biblioteki, który wciąż działa w przestrzeni użytkownika. Podobnie jak „użytkownik”, proces ten zajmuje tylko czas procesora. Poniżej znajduje się krótki opis trybu jądra (znanego również jako tryb „superwizora”) i mechanizmu wywołania systemowego.

User+Syspokaże, ile rzeczywistego czasu procesora wykorzystał proces. Zauważ, że dotyczy to wszystkich procesorów, więc jeśli proces ma wiele wątków (a ten proces działa na komputerze z więcej niż jednym procesorem), może potencjalnie przekroczyć zgłaszany czas zegara ściennego Real(co zwykle ma miejsce). Zauważ, że w danych wyjściowych liczby te obejmują Useri Sysczas wszystkich procesów potomnych (i ich potomków), a także kiedy mogły zostać zebrane, np. Przez wait(2)lub waitpid(2), chociaż wywołania systemowe leżące u podstaw zwracają statystyki dla procesu i jego potomków osobno.

Pochodzenie statystyk zgłoszonych przez time (1)

Statystyki zgłaszane przez timesą gromadzone z różnych wywołań systemowych. „Użytkownik” i „Sys” pochodzą od wait (2)( POSIX ) lub times (2)( POSIX ), w zależności od konkretnego systemu. „Rzeczywisty” jest obliczany na podstawie czasu rozpoczęcia i zakończenia zebranego z gettimeofday (2)połączenia. W zależności od wersji systemu mogą być gromadzone różne inne statystyki, takie jak liczba przełączników kontekstu time.

Na maszynie wieloprocesorowej proces wielowątkowy lub proces rozwidlania dzieci może mieć mniejszy czas niż całkowity czas procesora - ponieważ różne wątki lub procesy mogą przebiegać równolegle. Ponadto raportowane statystyki czasowe pochodzą z różnych źródeł, więc czasy zarejestrowane dla bardzo krótkotrwałych zadań mogą być obarczone błędami zaokrąglenia, jak pokazuje przykład z oryginalnego plakatu.

Krótki podkład w trybie jądra vs. użytkownika

W systemie Unix lub dowolnym systemie operacyjnym z chronioną pamięcią tryb „Kernel” lub „Supervisor” odnosi się do trybu uprzywilejowanego , w którym może działać procesor. Niektóre działania uprzywilejowane, które mogą mieć wpływ na bezpieczeństwo lub stabilność, można wykonać tylko wtedy, gdy procesor działa w trybie ten tryb; te działania nie są dostępne dla kodu aplikacji. Przykładem takiej akcji może być manipulacja MMU w celu uzyskania dostępu do przestrzeni adresowej innego procesu. Normalnie kod trybu użytkownika nie może tego zrobić (z uzasadnionego powodu), chociaż może zażądać pamięci współdzielonej z jądra, co mogłobybyć czytane lub pisane przez więcej niż jeden proces. W takim przypadku pamięć współdzielona jest jawnie wymagana od jądra za pośrednictwem bezpiecznego mechanizmu i oba procesy muszą jawnie się z nim połączyć, aby z niego skorzystać.

Tryb uprzywilejowany jest zwykle nazywany trybem „jądra”, ponieważ jądro jest wykonywane przez procesor działający w tym trybie. Aby przejść do trybu jądra, musisz wydać konkretną instrukcję (często nazywaną pułapką ), która przełącza procesor do pracy w trybie jądra i uruchamia kod z określonej lokalizacji przechowywanej w tabeli skoków. Ze względów bezpieczeństwa nie można przejść do trybu jądra i wykonać dowolny kod - pułapkami zarządza się za pomocą tabeli adresów, których nie można zapisać, chyba że procesor pracuje w trybie nadzorcy. Trapujesz z wyraźnym numerem pułapki, a adres znajduje się w tabeli skoków; jądro ma skończoną liczbę kontrolowanych punktów wejścia.

Wywołania „systemowe” w bibliotece C (szczególnie te opisane w Rozdziale 2 stron podręcznika) mają komponent trybu użytkownika, który tak naprawdę wywołujesz z programu C. Za kulisami mogą wydawać jedno lub więcej wywołań systemowych do jądra w celu wykonania określonych usług, takich jak We / Wy, ale nadal mają kod działający w trybie użytkownika. Jest również całkiem możliwe bezpośrednie uruchomienie pułapki na tryb jądra z dowolnego kodu przestrzeni użytkownika, jeśli zajdzie taka potrzeba, chociaż może być konieczne napisanie fragmentu języka asemblera, aby poprawnie skonfigurować rejestry dla wywołania.

Więcej informacji o „sys”

Są rzeczy, których twój kod nie może zrobić z trybu użytkownika - takie jak przydzielanie pamięci lub uzyskiwanie dostępu do sprzętu (HDD, sieć itp.). Są pod nadzorem jądra i tylko on może to zrobić. Niektóre operacje, takie jak malloclub fread/, fwritebędą wywoływać te funkcje jądra, a następnie będą liczone jako czas „sys”. Niestety nie jest to takie proste, że „każde połączenie do malloc będzie liczone w czasie„ sys ”. Wywołanie to mallocwykona własne przetwarzanie (wciąż liczone w czasie „użytkownika”), a następnie gdzieś po drodze może wywołać funkcję w jądrze (liczone w czasie „sys”). Po powrocie z wywołania jądra będzie więcej czasu w „user”, a potemmallocwróci do twojego kodu. Co do tego, kiedy nastąpi zmiana i ile jest wydane w trybie jądra ... nie można powiedzieć. To zależy od implementacji biblioteki. Również inne pozornie niewinne funkcje mogą również korzystać mallocw tle i tym podobne, które znów będą miały trochę czasu w „sys”.


15
Czy czas spędzony przez procesy potomne liczy się jako real / sys?
ron

1
@ron - Według strony podręcznika systemu Linux agreguje czasy „c” z czasami procesu, więc myślę, że tak. Czasy nadrzędne i podrzędne są dostępne osobno od wywołania times (2). Wydaje mi się, że wersja time (1) Solaris / SysV robi coś podobnego.
ConcernedOfTunbridgeWells

3
Użytkownik + Sys pozwala zmierzyć użycie procesora przez proces. Możesz go użyć do porównania wydajności. Jest to szczególnie przydatne w przypadku kodu wielowątkowego, w którym więcej niż jeden rdzeń procesora może pracować na obliczeniach.
ConcernedOfTunbridgeWells

1
Niemniej jednak nie na temat: Uruchamianie „\ time <cmd>” jest interesujące - zawiera więcej szczegółów: (wybacz złe formatowanie w komentarzu): $ time ps PID TTY TIME CMD 9437 pkt / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps real 0m0.025s użytkownik 0m0.004s sys 0m0.018s $ \ czas ps PID TTY TIME CMD 9437 pkt / 19 00:00:00 bash 11461 pkt / 19 00:00:00 czas 11462 pts / 19 00:00:00 ps 0,00user 0,01system 0: 00.02 upłynął 95% CPU (0avgtext + 0avgdata 2160maxresident) k 0 wejść + 0 wyjść (0major + 103minor) błędy stron 0swaps $
kaiwan

1
(W poprzednim komentarzu zabrakło znaków): Więcej szczegółów? Użyj perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
kaiwan

285

Aby rozwinąć przyjętą odpowiedź , chciałem tylko podać inny powód, dla którego realuser+ sys.

Należy pamiętać, że realreprezentuje rzeczywisty upływ czasu, podczas useri syswartości reprezentują czas realizacji CPU. W rezultacie w systemie wielordzeniowym czas useri / lub sysczas (a także ich suma) mogą faktycznie przekraczać czas rzeczywisty. Na przykład w aplikacji Java, którą uruchamiam dla klasy, otrzymuję ten zestaw wartości:

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

11
Zawsze się nad tym zastanawiałem. Ponieważ wiem, że moje programy są jednowątkowe, różnica między użytkownikiem a czasem rzeczywistym musi być narzutem maszyny wirtualnej, prawda?
Quantum7

9
niekoniecznie; Sun JVM na maszynach Solaris, a także JVM firmy Apple na Mac OS X potrafią używać więcej niż jednego rdzenia nawet w aplikacjach jednowątkowych. Jeśli wykonasz próbkę procesu java, zobaczysz, że rzeczy takie jak wyrzucanie elementów bezużytecznych działają w osobnych wątkach (i kilka innych rzeczy, których nie pamiętam z głowy). Nie wiem jednak, czy naprawdę chcesz nazywać to „narzutem maszyny wirtualnej”.
lensovet

4
Wydaje mi się, że liczba głosów pozytywnych dała ci teraz wystarczającą reputację: D. Co myślisz o realprzekroczeniu useri syssumie? Obciążenie systemu operacyjnego, takie jak przełączanie kontekstu wątku może być?
Muhammad Gelbana

19
Innym potencjalnym problemem może być We / Wy: jeśli aplikacja spędza dużo czasu, czekając na plik lub strumień, to oczywiście czas rzeczywisty znacznie przekroczy czas użytkownika / sys, ponieważ nie jest używany czas procesora podczas oczekiwania na uzyskanie dostępu do pliku lub czegoś podobnego.
lensovet

1
@MuhammadGelbana - może się to zdarzyć, jeśli aplikacja zostanie zablokowana z jakiegokolwiek powodu. Na przykład, jeśli czeka na połączenia I / O, IPC lub gniazda, będzie siedział bezczynnie, nie kumulując czasu procesora, aż do powrotu połączenia blokującego.
ConcernedOfTunbridgeWells

41

rzeczywisty : Rzeczywisty czas spędzony na uruchomieniu procesu od początku do końca, tak jakby był mierzony przez człowieka ze stoperem

użytkownik : Łączny czas spędzony przez wszystkie procesory podczas obliczeń

sys : Łączny czas spędzony przez wszystkie procesory podczas zadań związanych z systemem, takich jak przydzielanie pamięci.

Zauważ, że czasami użytkownik + sys może być większy niż rzeczywisty, ponieważ wiele procesorów może działać równolegle.


sysczas procesora spędzany na wywołaniach systemowych (i obsłudze błędów stron?)
Peter Cordes

1
realjest często określany jako „zegar ścienny”.
Peter Cordes

30

Minimalne uruchamialne przykłady POSIX C.

Żeby było bardziej konkretnie, chcę zilustrować kilka ekstremalnych przypadków timez minimalnymi programami testowymi C.

Wszystkie programy można kompilować i uruchamiać za pomocą:

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

i zostały przetestowane w Ubuntu 18.10, GCC 8.2.0, glibc 2.28, jądro Linux 4.18, laptop ThinkPad P51, procesor Intel Core i7-7820HQ (4 rdzenie / 8 wątków), 2x RAM Samsung M471A2K43BB1-CRC (2x 16GiB).

sen

Non-zajęty sen nie liczyć na jeden userlub systylko real.

Na przykład program, który śpi przez sekundę:

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

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

GitHub w górę .

wyprowadza coś takiego:

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

To samo dotyczy programów zablokowanych przy IO, które stają się dostępne.

Na przykład następujący program czeka na wprowadzenie znaku przez użytkownika i naciśnięcie enter:

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

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

GitHub w górę .

A jeśli zaczekasz około jednej sekundy, wyświetli się tak jak w przykładzie uśpienia, na przykład:

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

Z tego powodu timemoże pomóc ci rozróżnić programy związane z procesorem i operacjami we / wy : Co oznaczają pojęcia „związane z procesorem” i „związane z operacjami we / wy”?

Wiele wątków

Poniższy przykład wykonuje nitersiteracje bezużytecznych prac związanych wyłącznie z procesorem w nthreadswątkach:

#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 w górę + kod fabuły .

Następnie wykreślamy ścianę, użytkownika i system jako funkcję liczby wątków dla stałej iteracji 10 ^ 10 na moim 8 procesorze hyperthread:

wprowadź opis zdjęcia tutaj

Wykres danych .

Z wykresu widzimy, że:

  • w przypadku aplikacji jednordzeniowych intensywnie wykorzystujących procesor, ściana i użytkownik są mniej więcej takie same

  • dla 2 rdzeni użytkownik ma około 2x ściany, co oznacza, że ​​czas użytkownika jest liczony we wszystkich wątkach.

    użytkownik w zasadzie podwoił się, a ściana pozostała bez zmian.

  • kontynuuje to do 8 wątków, co odpowiada mojej liczbie hiperwątków na moim komputerze.

    Po 8 ściana zaczyna się również zwiększać, ponieważ nie mamy żadnych dodatkowych procesorów, aby włożyć więcej pracy w danym czasie!

    Stosunki plateau w tym punkcie.

Zauważ, że ten wykres jest tylko tak przejrzysty i prosty, ponieważ praca jest ściśle związana z procesorem: gdyby była związana z pamięcią, osiągnęlibyśmy spadek wydajności znacznie wcześniej z mniejszą liczbą rdzeni, ponieważ dostęp do pamięci byłby wąskim gardłem, jak pokazano w What oznaczają terminy „związany z procesorem” i „związany z I / O”?

Sys ciężka praca z sendfile

Najcięższym obciążeniem systemowym, jakie mogłem wymyślić, było użycie narzędzia sendfile, które wykonuje operację kopiowania plików w przestrzeni jądra: Kopiuj plik w rozsądny, bezpieczny i wydajny sposób

Więc wyobrażałem sobie, że to jądro memcpybędzie wymagało dużej mocy obliczeniowej procesora.

Najpierw inicjuję duży losowy plik 10GiB za pomocą:

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

Następnie uruchom kod:

#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 w górę .

co daje w zasadzie głównie czas systemowy zgodnie z oczekiwaniami:

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

Byłem również ciekawy, czy timerozróżniam systemy różnych procesów, więc spróbowałem:

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

Rezultatem było:

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

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

Czas sys jest mniej więcej taki sam dla obu jak dla pojedynczego procesu, ale czas ściany jest dłuższy, ponieważ procesy rywalizują o dostęp do odczytu dysku.

Wygląda więc na to, że faktycznie bierze pod uwagę proces, który rozpoczął dane działanie jądra.

Kod źródłowy Bash

Kiedy robisz to tylko time <cmd>na Ubuntu, użyj słowa kluczowego Bash, jak widać z:

type time

które wyjścia:

time is a shell keyword

Dlatego grepujemy źródło w kodzie źródłowym Bash 4.19 dla ciągu wyjściowego:

git grep '"user\b'

co prowadzi nas do funkcji execute_cmd.ctime_command , która wykorzystuje:

  • gettimeofday()i getrusage()jeśli oba są dostępne
  • times() Inaczej

wszystkie to wywołania systemowe Linux i funkcje POSIX .

Kod źródłowy GNU Coreutils

Jeśli nazwiemy to:

/usr/bin/time

następnie wykorzystuje implementację GNU Coreutils.

Ten jest nieco bardziej złożony, ale wydaje się, że odpowiednim źródłem jest resuse.c i robi:

  • wait3wywołanie BSD inne niż POSIX, jeśli jest dostępne
  • timesi gettimeofdayinaczej

14

Real pokazuje całkowity czas realizacji procesu; podczas gdy użytkownik pokazuje czas wykonania instrukcji zdefiniowanych przez użytkownika, a Sys czas wykonywania wywołań systemowych!

Czas rzeczywisty obejmuje również czas oczekiwania (czas oczekiwania na we / wy itp.)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.