[unix] 시간 (1)의 출력에서 ​​’실제’, ‘사용자’및 ‘sys’는 무엇을 의미합니까?

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

‘실제’, ‘사용자’및 ‘sys’는 시간의 출력에서 ​​무엇을 의미합니까?

내 앱을 벤치마킹 할 때 어떤 의미가 있습니까?



답변

실제, 사용자 및 Sys 프로세스 시간 통계

이 중 하나는 다른 것과 다릅니다. 실제는 실제 경과 시간을 나타냅니다. 사용자 및 Sys 는 프로세스에서만 사용되는 CPU 시간을 나타 냅니다.

  • 실제 벽 시계 시간은 – 전화의 처음부터 끝까지 시간입니다. 다른 프로세스에서 사용한 시간 조각과 프로세스가 차단 된 시간 (예 : I / O가 완료되기를 기다리는 경우)을 포함하여 모두 경과 된 시간입니다.

  • User 는 프로세스 에서 사용자 모드 코드 (커널 외부)에 소비 된 CPU 시간 입니다. 이것은 프로세스 실행에 사용 된 실제 CPU 시간입니다. 프로세스가 차단 한 다른 프로세스와 시간은이 수치에 포함되지 않습니다.

  • Sys 는 프로세스 내 커널에서 소비 한 CPU 시간입니다. 이것은 여전히 사용자 공간에서 실행되는 라이브러리 코드와 달리 커널 내의 시스템 호출에 소비 된 CPU 시간을 실행하는 것을 의미 합니다. ‘user’와 마찬가지로 프로세스에서 사용하는 CPU 시간입니다. 커널 모드 ( ‘감독자’모드라고도 함) 및 시스템 호출 메커니즘에 대한 간략한 설명은 아래를 참조하십시오.

User+Sys프로세스에서 사용한 실제 CPU 시간을 알려줍니다. 이는 모든 CPU에 적용되므로 프로세스에 여러 스레드가 있고 (이 프로세스가 둘 이상의 프로세서가있는 컴퓨터에서 실행중인 Real경우) 일반적으로 발생하는 벽시계 시간을 초과 할 수 있습니다 . 출력에 이러한 수치에는 모든 하위 프로세스 (및 하위 항목)의 시간 UserSys시간뿐만 아니라 예를 들어 wait(2)또는 waitpid(2)에 의해 수집 될 수있는 시간 도 포함 됩니다 ( 기본 시스템 호출은 프로세스 및 하위 프로세스에 대한 통계를 개별적으로 리턴 함).

에 의해보고 된 통계의 기원 time (1)

보고 된 통계는 time다양한 시스템 호출에서 수집됩니다. ‘User’및 ‘Sys’ 는 특정 시스템에 따라 wait (2)( POSIX ) 또는 times (2)( POSIX )에서 가져옵니다. ‘실제’는 gettimeofday (2)통화 에서 수집 된 시작 및 종료 시간으로 계산됩니다 . 시스템 버전에 따라 컨텍스트 스위치 수와 같은 다양한 기타 통계도 수집 할 수 있습니다 time.

다중 프로세서 시스템에서 다중 스레드 프로세스 또는 하위 프로세스를 처리하는 프로세스는 다른 스레드 또는 프로세스가 병렬로 실행될 수 있으므로 전체 CPU 시간보다 경과 시간이 더 짧을 수 있습니다. 또한보고 된 시간 통계는 서로 다른 출처에서 왔으므로 매우 짧은 실행 작업에 대해 기록 된 시간에는 원래 포스터에서 제공 한 예와 같이 반올림 오류가 발생할 수 있습니다.

커널 대 사용자 모드에 대한 간단한 입문서

Unix 또는 모든 보호 메모리 운영 체제에서 ‘Kernel’또는 ‘Supervisor’ 모드는 CPU가 작동 할 수 있는 권한 모드 를 나타냅니다 . 보안 또는 안정성에 영향을 줄 수있는 특정 권한 동작은 CPU가 작동 할 때만 수행 할 수 있습니다. 이 모드; 이러한 조치는 애플리케이션 코드에서 사용할 수 없습니다. 이러한 조치의 예는 다른 프로세스의 주소 공간에 액세스하기 위해 MMU 를 조작하는 것입니다 . 일반적으로 사용자 모드 코드는 커널에서 공유 메모리 를 요청할 수 있지만 (이유가있는 경우)이를 수행 할 수 없습니다.여러 프로세스에서 읽거나 쓸 수 있습니다. 이 경우 공유 메커니즘은 보안 메커니즘을 통해 커널에서 명시 적으로 요청되며이를 사용하려면 두 프로세스 모두 명시 적으로 연결해야합니다.

특권 모드는 커널이이 모드에서 실행되는 CPU에 의해 실행되므로 일반적으로 ‘커널’모드라고합니다. 커널 모드로 전환하려면 CPU를 커널 모드에서 실행으로 전환 하고 점프 테이블에있는 특정 위치에서 코드 를 실행 하는 특정 명령 (종종 트랩 ) 을 발행해야 합니다. 보안상의 이유로 커널 모드로 전환하고 임의 코드를 실행할 수 없습니다. 트랩은 CPU가 감독자 모드에서 실행되고 있지 않으면 쓸 수없는 주소 테이블을 통해 관리됩니다. 명시적인 트랩 번호로 트랩하면 주소가 점프 테이블에서 조회됩니다. 커널에는 유한 한 수의 제어 된 진입 점이 있습니다.

C 라이브러리의 ‘시스템’호출 (특히 매뉴얼 페이지의 섹션 2에 설명 된 호출)에는 실제로 C 프로그램에서 호출하는 사용자 모드 구성 요소가 있습니다. 뒤에서, I / O와 같은 특정 서비스를 수행하기 위해 커널에 하나 이상의 시스템 호출을 발행 할 수 있지만 여전히 사용자 모드에서 실행되는 코드가 있습니다. 원하는 경우 사용자 공간 코드에서 커널 모드로 트랩을 직접 발행하는 것도 가능하지만 호출에 대한 레지스터를 올바르게 설정하기 위해 어셈블리 언어 스 니펫을 작성해야 할 수도 있습니다.

‘sys’에 대한 추가 정보

메모리 할당 또는 하드웨어 액세스 (HDD, 네트워크 등)와 같이 사용자 모드에서 코드가 수행 할 수없는 작업이 있습니다. 이것들은 커널의 감독하에 있으며 혼자서도 할 수 있습니다. malloc또는 fread/ 와 같은 일부 작업 fwrite은 이러한 커널 기능을 호출 한 다음 ‘sys’시간으로 계산됩니다. 불행히도 “모든 malloc 호출은 ‘sys’시간으로 계산됩니다”만큼 간단하지 않습니다. 호출 malloc은 자체 처리 (여전히 ‘사용자’시간으로 계산)를 수행 한 다음 커널에서 함수를 호출 할 수있는 방식 ( ‘sys’시간으로 계산)을 수행합니다. 커널 호출에서 돌아온 후 ‘user’에 시간이 더 있습니다.malloc코드로 돌아갑니다. 스위치가 언제 발생하고 커널 모드에서 소비되는 양은 말할 수 없습니다. 라이브러리 구현에 따라 다릅니다. 또한 다른 겉보기에 무해한 기능도 malloc백그라운드에서 사용할 수 있으며 ‘sys’에 시간이 다시 걸립니다.


답변

허용 된 답변 을 확장 하기 위해 realuser+ 이유를 제공하고 싶었습니다 sys.

염두에 유지 real하면서, 실제 경과 시간을 표시 user하고 sys값이 CPU의 실행 시간을 나타낸다. 결과적으로 멀티 코어 시스템에서 user및 / 또는 sys시간 ( 및 합계)은 실제로 실시간을 초과 할 수 있습니다 . 예를 들어, Java 응용 프로그램에서 클래스에 대해 실행중인 경우 다음 값 세트를 얻습니다.

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


답변

real : 초시계를 가진 사람이 측정 한 것처럼 프로세스를 처음부터 끝까지 실행하는 데 소요 된 실제 시간

user : 계산 중 모든 CPU에서 소비 한 누적 시간

sys : 메모리 할당과 같은 시스템 관련 작업 중에 모든 CPU에서 소비 한 누적 시간입니다.

여러 프로세서가 동시에 작동 할 수 있기 때문에 때때로 user + sys가 실제보다 클 수 있습니다.


답변

최소 실행 가능 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, ThinkPad P51 랩톱, Intel Core i7-7820HQ CPU (4 코어 / 8 스레드), 2x Samsung M471A2K43BB1-CRC RAM (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의 상류 .

그리고 약 1 초 정도 기다리면 수면 예제와 같이 다음과 같이 출력됩니다.

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

이러한 이유로 timeCPU와 IO 바운드 프로그램을 구별 할 수 있습니다. “CPU 바운드”및 “I / O 바운드”라는 용어는 무엇을 의미합니까?

여러 스레드

다음 예제는 스레드 niters에서 쓸모없는 순수한 CPU 바운드 작업의 반복을 수행 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 업스트림 + 플롯 코드 .

그런 다음 8 하이퍼 스레드 CPU에서 고정 10 ^ 10 반복에 대한 스레드 수의 함수로 wall, user 및 sys를 플로팅합니다.

여기에 이미지 설명을 입력하십시오

데이터를 플롯합니다 .

그래프에서 우리는 다음을 볼 수 있습니다.

  • CPU를 많이 사용하는 단일 코어 애플리케이션의 경우 월과 사용자는 거의 같습니다

  • 2 코어의 경우 사용자는 벽의 약 2 배이므로 모든 스레드에서 사용자 시간이 계산됩니다.

    사용자는 기본적으로 두 배가되었고 벽은 동일하게 유지되었습니다.

  • 이것은 최대 8 개의 스레드까지 계속되며, 이는 컴퓨터의 여러 하이퍼 스레드와 일치합니다.

    8 시간이 지나면 주어진 시간에 더 많은 작업을 할 CPU가 없기 때문에 벽도 증가하기 시작합니다!

    이 시점의 비율은 정점입니다.

이 메모리가 결합 된 경우에서와 같이 메모리 액세스가 병목이 될 것이기 때문에, 우리는 훨씬 이전에 적은 코어의 성능 하락을 얻을 것입니다 : 작업이 순수하게 CPU 바인딩 때문에이 그래프는 매우 명확하고 간단합니다 무엇 “CPU bound”및 “I / O bound”라는 용어는 무엇을 의미합니까?

Sys의 중작 업 sendfile

가장 무거운 SYS 내가를 사용하는 것이었다 가지고 올 수있는 워크로드 sendfile파일 복사 커널 공간에서 작업을 수행하는 : 복사 온전한에서 파일을 안전하고 효율적인 방법

그래서이 커널 내부 memcpy가 CPU 집약적 인 작업이 될 것이라고 상상했습니다 .

먼저 다음과 같이 큰 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다른 프로세스의 syscall을 구분할 수 있는지 궁금해서 시도했습니다.

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 시간은 단일 프로세스의 경우와 거의 동일하지만 프로세스가 디스크 읽기 액세스를 위해 경쟁하고 있기 때문에 소요 시간이 더 큽니다.

따라서 실제로 어떤 프로세스가 주어진 커널 작업을 시작했는지 설명하는 것 같습니다.

배쉬 소스 코드

time <cmd>우분투에서 할 때 다음 과 같이 Bash 키워드를 사용합니다.

type time

어떤 출력 :

time is a shell keyword

따라서 출력 문자열에 대한 Bash 4.19 소스 코드에서 소스를 grep하십시오.

git grep '"user\b'

이를 통해 우리는 execute_cmd.c 함수 time_command를 사용합니다.

  • gettimeofday()그리고 getrusage()두 경우 사용할 수 있습니다
  • times() 그렇지 않으면

이것들은 모두 리눅스 시스템 호출POSIX 함수 입니다.

GNU Coreutils 소스 코드

우리가 다음과 같이 부르면 :

/usr/bin/time

그런 다음 GNU Coreutils 구현을 사용합니다.

이것은 조금 더 복잡하지만 관련 소스는 resuse.c 에있는 것 같습니다 .

  • POSIX가 아닌 BSD wait3호출
  • timesgettimeofday달리

답변

Real은 프로세스의 총 처리 시간을 보여줍니다. User는 사용자 정의 명령어의 실행 시간을 표시하고 Sys는 시스템 호출을 실행하는 시간입니다!

실시간에는 대기 시간도 포함됩니다 (I / O 대기 시간 등).


답변