Что означают «реальный», «пользователь» и «sys» в выводе времени (1)?

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

Что означают "реальный", "пользовательский" и "sys" в выводе времени?

Какой смысл имеет смысл при тестировании моего приложения?

  • 1
    как я могу получить доступ только к одному из них? например просто в реальном времени?
  • 1
    @ConcernedOfTunbridgeWells
Показать ещё 2 комментария
Теги:
time
benchmarking

6 ответов

1630
Лучший ответ

Real, User и Sys обрабатывают статистику времени

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

  • Реальное время настенные часы - время от начала до конца разговора. Это все прошедшее время, включая временные интервалы, используемые другими процессами, и время, которое процесс блокирует (например, если он ожидает завершения ввода-вывода).

  • Пользователь - это количество процессорного времени, потраченного на код пользовательского режима (вне ядра) внутри процесса. Это только фактическое время процессора, используемое при выполнении процесса. Другие процессы и время, затраченное процессом заблокировано, не учитываются в этом показателе.

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

User+Sys скажет вам, сколько фактического процессорного времени использовало ваш процесс. Обратите внимание, что это относится ко всем процессорам, поэтому, если процесс имеет несколько потоков (и этот процесс выполняется на компьютере с более чем одним процессором), он может потенциально превысить время настенных часов, сообщаемое Real (что обычно происходит). Обратите внимание, что в выходных данных эти цифры включают в себя время User и Sys всех дочерних процессов (и их потомков), а также когда их можно было собрать, например, с помощью wait(2) или waitpid(2), хотя базовые системные вызовы возвращают статистика по процессу и его потомкам отдельно.

Источники статистики, представленной по time (1)

Статистика, представленная по time, собирается из различных системных вызовов. "Пользователь" и "Sys" происходят из wait (2) (POSIX) или times (2) (POSIX), в зависимости от конкретной системы. "Реальное" рассчитывается по времени начала и окончания, полученному из gettimeofday (2). В зависимости от версии системы, различные другие статистические данные, такие как количество переключений контекста, также могут быть собраны по time.

На многопроцессорном компьютере многопоточный процесс или дочерний процесс может иметь истекшее время, меньшее общего времени ЦП, поскольку различные потоки или процессы могут выполняться параллельно. Кроме того, статистические данные о времени поступают из разных источников, поэтому время, записанное для очень коротких задач, может быть подвержено ошибкам округления, как показывает пример, приведенный в оригинальном постере.

Краткое руководство по режиму "Ядро против пользователя"

В Unix или любой другой операционной системе с защищенной памятью режим "Ядро" или "Супервизор" относится к привилегированному режиму, в котором может работать ЦП. Некоторые привилегированные действия, которые могут повлиять на безопасность или стабильность, могут выполняться только тогда, когда ЦП работает в этот режим; эти действия недоступны для кода приложения. Примером такого действия может быть манипулирование MMU для получения доступа к адресному пространству другого процесса. Обычно код пользовательского режима не может сделать это (по уважительной причине), хотя он может запрашивать общую память у ядра, которая может быть прочитана или записана несколькими процессами. В этом случае общая память запрашивается у ядра через безопасный механизм, и оба процесса должны явно подключаться к ней, чтобы использовать ее.

Привилегированный режим обычно называется режимом "ядро", поскольку ядро выполняется процессором, работающим в этом режиме. Чтобы переключиться в режим ядра, вы должны выполнить специальную инструкцию (часто называемую ловушкой), которая переключает ЦП на работу в режиме ядра и запускает код из определенного места, содержащегося в таблице переходов. По соображениям безопасности вы не можете переключиться в режим ядра и выполнить произвольный код - управление ловушками осуществляется через таблицу адресов, в которую невозможно записать, если процессор не работает в режиме супервизора. Вы ловите с явным номером ловушки, и адрес ищется в таблице переходов; ядро имеет конечное число контролируемых точек входа.

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

Подробнее о 'sys'

Есть вещи, которые ваш код не может сделать из пользовательского режима - например, выделение памяти или доступ к оборудованию (жесткий диск, сеть и т.д.). Они находятся под наблюдением ядра, и только он может сделать это. Некоторые операции, которые вы выполняете (например, malloc или fread/fwrite), будут вызывать эти функции ядра, которые затем будут считаться временем 'sys'. К сожалению, это не так просто, как "каждый вызов malloc будет учитываться во время 'sys'". Вызов malloc будет выполнять некоторую собственную обработку (все еще учитывается во время "пользователя"), а затем где-то по пути он может вызывать функцию в ядре (считая во время "sys"). После возврата из вызова ядра, у пользователя будет еще некоторое время, а затем malloc вернется к вашему коду. Что касается того, когда происходит переключение, и сколько его тратится в режиме ядра... вы не можете сказать. Это зависит от реализации библиотеки. Кроме того, другие, казалось бы, невинные функции могут также использовать malloc и тому подобное в фоновом режиме, который снова будет иметь некоторое время в 'sys'.

  • 10
    Учитывается ли время, проведенное дочерними процессами, в real / sys?
  • 1
    @ron - Согласно справочной странице Linux, она агрегирует времена 'c' с временем процесса, так что я думаю, что это так. Однако родительское и дочернее время доступны отдельно от вызова times (2). Я предполагаю, что версия времени Solaris / SysV (1) делает нечто подобное.
Показать ещё 10 комментариев
226

Чтобы расширить принятый ответ , я просто хотел указать еще одну причину, по которой realuser + sys.

Имейте в виду, что real представляет фактическое прошедшее время, а значения user и sys представляют время выполнения ЦП. В результате в многоядерной системе время user и/или sys (а также их сумма) может фактически превышать реальное время. Например, в приложении Java, которое я запускаю для класса, я получаю этот набор значений:

real    1m47.363s
user    2m41.318s
sys     0m4.013s
  • 9
    Я всегда задавался вопросом об этом. Поскольку я знаю, что мои программы являются однопоточными, разница между пользователем и реальным временем должна быть связана с издержками виртуальной машины, верно?
  • 3
    не обязательно; Sun JVM на машинах Solaris, а также Apple JVM на Mac OS X удается использовать более одного ядра даже в однопоточных приложениях. Если вы сделаете пример java-процесса, вы увидите, что такие вещи, как сборка мусора, выполняются в отдельных потоках (и некоторые другие вещи, которые я не помню, в верхней части моей головы). Я не знаю, действительно ли вы хотите назвать это «издержками виртуальной машины».
Показать ещё 9 комментариев
14

Real показывает общее время поворота для процесса; пока Пользователь показывает время выполнения для пользовательских инструкций и Sys на время для выполнения системных вызовов!

Реальное время также включает время ожидания (время ожидания ввода-вывода и т.д.)

11

real: фактическое время, затрачиваемое на выполнение процесса от начала до конца, как если бы оно было измерено человеком с секундомером

пользователь: совокупное время, затрачиваемое всеми процессорами во время вычисления

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

Обратите внимание, что иногда пользователь + sys может быть больше реального, так как несколько процессоров могут работать параллельно.

  • 0
    sys тратит ли процессорное время на системные вызовы (и обработчики ошибок страниц?)
  • 0
    real часто называют «настенными часами» времени.
3

Минимальные выполнимые примеры POSIX C

Для того, чтобы сделать вещи более конкретными, я хочу, чтобы иллюстрировать несколько крайних случаев time с некоторыми минимальными тестовыми программами C.

Все программы могут быть скомпилированы и запущены с:

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.

спать

Незанятый сон не считается ни 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.

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

#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

Несколько потоков

В следующем примере niters итерации бесполезной тяжелой работы nthreads потоках 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 upstream + сюжетный код.

Затем мы отображаем стену, пользователя и sys как функцию времени для фиксированных 10 ^ 10 итераций на моем 8-ми процессоре с гиперпоточностью:

Изображение 7814

Из графика мы видим, что:

  • для одноядерного приложения, интенсивно использующего процессор, стена и пользователь примерно одинаковы

  • для 2 ядер пользователь примерно в 2 раза больше стены, что означает, что пользовательское время учитывается во всех потоках

  • это продолжается до 8 потоков, что соответствует моему числу гиперпотоков.

    Это немного ниже, чем в 8 раз, потому что другие процессы в моей системе должны конкурировать за время потока

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

Sys тяжелая работа

Самая тяжелая рабочая нагрузка sys, которую я мог придумать, состояла в том, чтобы использовать sendfile, который выполняет операцию копирования файла в пространстве ядра: копирование файла разумным, безопасным и эффективным способом.

Так что я представил, что этот memcpy будет memcpy процессор.

Сначала я инициализирую большой 10GiB случайный файл:

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.c time_command, которая использует:

  • gettimeofday() и getrusage() если оба доступны
  • times() противном случае

все это системные вызовы Linux и функции POSIX.

Исходный код GNU Coreutils

Если мы называем это как:

/usr/bin/time

затем он использует реализацию GNU Coreutils.

Это немного сложнее, но соответствующий источник, кажется, находится в resuse.c, и он делает:

  • вызов не POSIX BSD wait3 если он доступен
  • times и gettimeofday противном случае
-8

real - количество минут, которые вы указали в своем отчете о состоянии, чтобы понять "время". это в основном сумма (пользователь + (sys * мозги)).

пользователь - это время, которое вы потратили на чтение этого блога. Это не включает время, когда ваш мозг делает другие вещи, такие как управление прерыванием боссов.

sys - это время, которое на самом деле думает и пытается понять, что он говорит. Если бы у вас было несколько мозгов, вы могли бы многозадачно и, надеюсь, это было бы быстрее. Не забудьте включить только время, потраченное на эту проблему. Не время, потраченное на обед или что-то еще.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню