Що означають "реальний", "користувач" і "sys" у результаті часу (1)?


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

Що означають "реальний", "користувач" та "систематичний" у результаті часу?

Який із них має сенс під час тестування мого додатка?


2
як я можу отримати доступ лише до одного з них? наприклад, просто в режимі реального часу?
Мойтаба Ахмаді

1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi


7
Якщо ваша програма закінчується так швидко, жоден з них не має сенсу, це все лише запуск накладні. Якщо ви хочете виміряти всю програму time, дозвольте їй зробити щось, що займе хоча б секунду.
Пітер Кордес

5
Дуже важливо відзначити, що timeце ключове слово bash. Так , набравши man timeв НЕ даючи вам довідкову сторінку для Баша time, а це дає довідкову сторінку для /usr/bin/time. Це мене спонукало.
irritable_phd_syndrom

Відповіді:


2059

Статистика часу, реального користувача та системи

Одна з цих речей не схожа на іншу. Реальний стосується фактично минулого часу; Користувач і Sys посилаються на час процесора, який використовується лише процесом.

  • Справжнім є настінний годинник - час від початку до закінчення розмови. Це весь минулий час, включаючи відрізки часу, використовувані іншими процесами, і час, який процес витрачає заблоковано (наприклад, якщо він чекає завершення вводу / виводу).

  • Користувач - це кількість часу процесора, витраченого на код у режимі користувача (за межами ядра) в процесі. Це лише фактичний час процесора, який використовується для виконання процесу. Інші процеси та час, який процес витрачає заблоковано, не враховуються до цієї цифри.

  • Sys - це кількість часу процесора, проведеного в ядрі в процесі. Це означає виконання часу процесора, витраченого на системні виклики в межах ядра, на відміну від коду бібліотеки, який все ще працює в просторі користувача. Як і "користувач", це лише час процесора, який використовується процесом. Нижче див. Короткий опис режиму ядра (також відомий як режим "супервізор") та механізму системного виклику.

User+Sysпідкаже, скільки фактичного часу ЦП використовував ваш процес. Зауважте, що це є у всіх процесорах, тому якщо процес має декілька потоків (і цей процес працює на комп'ютері з більш ніж одним процесором), він може потенційно перевищувати час настінного годинника, про Realякий повідомляється (що зазвичай відбувається). Зауважте, що у вихідних даних ці дані включають час Userта Sysчас усіх дочірніх процесів (та їхніх нащадків), а також коли вони могли бути зібрані, наприклад, wait(2)або waitpid(2), хоча базові системи вимагають повернення статистики процесу та його дітей окремо.

Походження статистичних даних, про які повідомляє time (1)

Статистика, про яку повідомляють time, збирається з різних системних дзвінків. "Користувач" і "Сис" походять від wait (2)( POSIX ) або times (2)( POSIX ), залежно від конкретної системи. "Справжній" обчислюється від часу початку та закінчення, зібраного під час gettimeofday (2)виклику. Залежно від версії системи, також можуть бути зібрані різні інші статистичні дані, такі як кількість контекстних комутаторів time.

На багатопроцесорній машині багатопотоковий процес або процес роздрібнення дітей може мати час, менший за загальний час процесора - оскільки різні потоки або процеси можуть працювати паралельно. Крім того, звіт про статистику часу походить з різних джерел, тому час, записаний для дуже коротких виконання завдань, може зазнавати помилок округлення, як показує приклад, наведений в оригінальному плакаті.

Короткий буквар на режим Kernel vs. User

У Unix або будь-якій операційній системі захищеної пам’яті режим «Kernel» або «Supervisor» посилається на привілейований режим , в якому може працювати ЦП. Деякі привілейовані дії, які можуть вплинути на безпеку або стабільність, можна виконати лише тоді, коли центральний процесор працює в цей режим; ці дії недоступні для коду програми. Прикладом такої дії може бути маніпулювання MMU для отримання доступу до адресного простору іншого процесу. Зазвичай код у режимі користувача не може цього зробити (з поважних причин), хоча він може вимагати спільної пам'яті з ядра, яке могло бчитати чи писати більш ніж одним процесом. У цьому випадку спільна пам'ять явно запитується від ядра через захищений механізм, і обидва процеси повинні явно приєднатися до неї, щоб використовувати його.

Привілейований режим зазвичай називають режимом «ядро», оскільки ядро ​​виконується процесором, що працює в цьому режимі. Для переходу в режим ядра вам слід видати певну інструкцію (часто її називають пасткою ), яка перемикає процесор на режим роботи в режимі ядра і запускає код з певного місця, що знаходиться в таблиці стрибків. З міркувань безпеки ви не можете переключитися в режим ядра та виконувати довільний код - пастками керує таблиця адрес, до якої неможливо записати, якщо ЦП не працює в режимі супервізора. Ви потрапляєте в ловушку з явним номером пастки, і адреса шукається в таблиці стрибків; ядро має кінцеву кількість контрольованих точок входу.

"Системні" дзвінки в бібліотеці С (особливо ті, що описані в розділі 2 підручних сторінок) містять компонент в режимі користувача, який ви дійсно телефонуєте зі своєї програми C. Позаду, вони можуть надсилати одне або більше системних викликів ядра для виконання конкретних служб, таких як введення / виведення, але вони все ще мають код, що працює в режимі користувача. Також, цілком можливо, безпосередньо видати пастку в режим ядра з будь-якого коду простору користувача за бажанням, хоча вам може знадобитися написати фрагмент мови складання, щоб правильно налаштувати регістри для виклику.

Більше про "sys"

Є такі речі, які ваш код не може зробити в режимі користувача - такі, як розподіл пам'яті або доступ до обладнання (жорсткий диск, мережа тощо). Вони знаходяться під наглядом ядра, і тільки їх може це зробити. Деякі операції на зразок mallocабо fread/ fwriteвикликатимуть ці функції ядра, і тоді вони будуть рахуватися як час 'sys'. На жаль, це не так просто, як "кожен дзвінок на malloc буде рахуватися в" sys "час". Заклик до mallocбуде виконувати деяку власну обробку (все ще рахується у "користувальницькому" часі), а потім десь по дорозі може викликати функцію в ядрі (рахується в "sys" час). Після повернення з виклику ядра буде ще деякий час у «користувачеві», а потімmallocповернеться до вашого коду. Що стосується того, коли відбувається перемикання і скільки його витрачається в режимі ядра ... ви не можете сказати. Це залежить від реалізації бібліотеки. Крім того, інші, здавалося б, невинні функції також можуть використовуватись mallocтощо у фоновому режимі, який знову матиме деякий час у «sys».


15
Чи враховується час, витрачений на дочірні процеси, на реальний / сис?
ron

1
@ron - Відповідно до сторінки man Linux, вона об'єднує "c" часи з часом обробки, тому я думаю, що це так. Однак, батьківські та дочірні часи доступні окремо від дзвінка "Час (2)". Я думаю, що версія часу Solaris / SysV (1) робить щось подібне.
СтурбованоOfTunbridgeWells

3
User + Sys дозволяє вимірювати використання процесора процесом. Ви можете використовувати його для тестування продуктивності. Це особливо корисно для багатопотокового коду, де над обчисленням може працювати більше одного ядра процесора.
СтурбованоOfTunbridgeWells

1
Не точно по темі, все ж: Запуск "\ time <cmd>" цікавий - він надає більш детальну інформацію: (пробачте, невдале форматування в коментарі): $ time ps PID TTY TIME CMD 9437 балів / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps реально 0m0.025s користувач 0m0.004s sys 0m0.018s $ \ час ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 час 11462 pts / 19 00:00:00 ps 0,00user 0.01system 0: 00.02elapsed 95% CPU (0avgtext + 0avgdata 2160maxresident) k 0вхідні дані + 0outputs (0major + 103minor) pagefaults 0swaps $
kaiwan

1
(Вибіг з символів у попередньому коментарі так): Детальніше? Використовуйте perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
kaiwan

285

Щоб розширити прийняту відповідь , я просто хотів надати ще одну причину, чому realuser+ sys.

Майте на увазі , що realпредставляє собою фактичний час , що минув, в той час як userі sysзначення представляють час виконання CPU. Як результат, у багатоядерній системі час userта / або sysчас (а також їх сума) можуть фактично перевищувати реальний час. Наприклад, у додатку Java, який я запускаю для класу, я отримую цей набір значень:

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

11
Я завжди цікавився цим. Оскільки я знаю, що мої програми є однопотоковими, різниця між користувачем та реальним часом повинна бути накладеною на VM, правильно?
Quantum7

9
не обов'язково; Sun JVM на машинах Solaris, а також JVM Apple на Mac OS X вдається використовувати більше одного ядра навіть у однопотокових додатках. Якщо ви робите зразок процесу Java, ви побачите, що такі речі, як збирання сміття, працюють окремими нитками (та ще якісь речі, які я не пам’ятаю вгорі голови). Я не знаю, чи дійсно ви хочете назвати цей "ВМ накладними", хоча
лінзовет

4
Я думаю, що кількість голосів дала тобі достатньо репутації: D. То що ви думаєте про realперевищення userта sysзагальне? Можливо, накладні витрати на ОС, такі як контекстна комутація потоку?
Мухаммад Гелбана

19
Іншою потенційною проблемою може бути введення-виведення: якщо ваша програма витрачає багато часу на очікування отримання файлу чи потоку, то, очевидно, реальний час значно перевищить час користувача / системи, тому що час очікування доступу не використовується для процесорного часу у файл чи щось подібне.
лінзовет

1
@MuhammadGelbana - це може статися, якщо програму з будь-якої причини заблоковано виконувати. Наприклад, якщо він чекає на з'єднання вводу / виводу, IPC або socket, він буде простоювати, не накопичуючи часу процесора, поки виклик блокування не повернеться.
ЗанепокоєнийOfTunbridgeWells

41

реальний : фактичний час, витрачений на виконання процесу від початку до кінця, так, як якщо б він вимірювався людиною секундоміром

користувач : сукупний час, витрачений усіма процесорами під час обчислення

sys : сукупний час, витрачений усіма процесорами під час системних завдань, таких як розподіл пам'яті.

Зауважте, що іноді user + sys можуть бути більшими, ніж реальні, оскільки паралельно можуть працювати декілька процесорів.


sysчи витрачається процесорний час на системні дзвінки (і обробку помилок сторінки?)
Пітер Кордес

1
realчасто описується як "настінні годинники".
Пітер Кордес

30

Мінімальні приклади, які можна виконувати POSIX C

Щоб зробити конкретніше, я хочу навести декілька крайніх випадків timeіз деякими мінімальними програмами тестування на С.

Усі програми можна компілювати та запускати за допомогою:

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

і пройшли тестування в Ubuntu 18.10, GCC 8.2.0, glibc 2.28, Linux ядрі 4.18, ноутбуку ThinkPad P51, процесорі Intel Core i7-7820HQ (4 ядра / 8 потоків), 2x оперативної пам'яті Samsung M471A2K43BB1-CRC (2x 16GiB).

спати

Незайнятого сон не враховується ні в одному userабо sysтільки real.

Наприклад, програма, яка спить на секунду:

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

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

GitHub вище за течією .

виводить щось на кшталт:

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

Те саме стосується програм, заблокованих на IO, які стають доступними.

Наприклад, наступна програма чекає, коли користувач введе символ і натисне клавішу Enter:

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

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

GitHub вище за течією .

І якщо ви зачекаєте близько однієї секунди, він виводить так само, як приклад сну, щось подібне:

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

З цієї причини timeви можете допомогти вам розрізнити програми, пов'язані з процесором та введеннями, що означають : що означають терміни "пов'язані з процесором" та "пов'язані введення / виведення"?

Кілька ниток

Наступний приклад робить nitersітерації марної роботи, пов'язаної з процесором, на nthreadsпотоках:

#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 вище за течією + код ділянки .

Тоді ми побудуємо графік wall, user та sys як функція від кількості потоків для фіксованих 10 ^ 10 ітерацій на моєму 8 процесорі гіперпотоків:

введіть тут опис зображення

Дані графіку .

З графіка ми бачимо, що:

  • для інтенсивного однопроцесорного додатка, стіни та користувача приблизно однакові

  • для 2 ядер, користувач становить приблизно 2х стінок, що означає, що час користувача обчислюється в усіх потоках.

    Користувач в основному збільшився вдвічі, і поки стіна залишилася колишньою.

  • це триває до 8 потоків, що відповідає моїй кількості гіперточок у моєму комп’ютері.

    Після 8 стіна також починає збільшуватися, тому що у нас немає додаткових процесорів, щоб помістити більше роботи за певний час!

    Співвідношення плато в цій точці.

Зауважте, що цей графік настільки чіткий і простий, оскільки робота суто пов'язана з процесором: якби це було пов'язано з пам'яттю, ми б отримали падіння продуктивності набагато раніше з меншими ядрами, оскільки доступ до пам'яті був би вузьким місцем, як показано на Що чи означають терміни "пов'язані з процесором" та "пов'язані введення / виведення"?

Сис важку роботу з sendfile

Найбільш важке завантаження системи, яке я міг придумати, - це використовувати функцію sendfile, яка виконує операцію копіювання файлу в просторі ядра: Скопіюйте файл надійним, безпечним та ефективним способом

Тож я уявив, що це в ядрі memcpyбуде інтенсивним процесором.

Спочатку я ініціалізую великий випадковий файл 10 Гбіт із:

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

Потім запустіть код:

#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 вище за течією .

яка в основному дає системний час, як очікувалося:

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

Мені також було цікаво дізнатись, чи timeможна розрізняти системні виклики різних процесів, тому я спробував:

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

І результат:

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

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

Час sys приблизно однаковий для обох процесів, але час стіни більший, оскільки процеси конкурують за ймовірний доступ до читання дисків.

Тож здається, що насправді це враховує, для якого процесу розпочато роботу ядра.

Bash вихідний код

Коли ви просто робите time <cmd>в Ubuntu, він використовує ключове слово Bash, як видно з:

type time

який виводить:

time is a shell keyword

Отже, ми знімаємо джерело у вихідному коді Bash 4.19:

git grep '"user\b'

що призводить нас до функції Execute_cmd.ctime_command , яка використовує:

  • gettimeofday() і getrusage() якщо обидва є
  • times() інакше

все це системні виклики Linux та POSIX .

Вихідний код GNU Coreutils

Якщо ми називаємо це:

/usr/bin/time

тоді він використовує реалізацію GNU Coreutils.

Це трохи складніше, але, здається, відповідне джерело знаходиться у resuse.c, і це:

  • не-POSIX BSD wait3виклик який не є якщо такий доступний
  • timesі в gettimeofdayіншому випадку

14

Real показує загальний час повороту процесу; а користувач показує час виконання інструкцій, визначених користувачем, а Sys - час для виконання системних викликів!

Реальний час включає також час очікування (час очікування вводу / виводу тощо)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.