'Thực', 'người dùng' và 'sys' nghĩa là gì trong đầu ra của thời gian (1)?


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

'Thực', 'người dùng' và 'sys' nghĩa là gì trong đầu ra của thời gian?

Cái nào có ý nghĩa khi điểm chuẩn ứng dụng của tôi?


2
Làm thế nào tôi có thể truy cập chỉ một trong số họ? ví dụ chỉ là thời gian thực?
Mojtaba Ahmadi

1
@ConcernedOfTunbridgeWells
Mojtaba Ahmadi


7
Nếu chương trình của bạn thoát nhanh như vậy, không ai trong số chúng có ý nghĩa, tất cả chỉ là khởi động. Nếu bạn muốn đo toàn bộ chương trình time, hãy thực hiện một vài thứ sẽ mất ít nhất một giây.
Peter Cordes

5
Điều thực sự quan trọng cần lưu ý đó timelà một từ khóa bash. Vì vậy, gõ không phảiman time là cung cấp cho bạn một trang man cho bash , thay vào đó nó là cho trang man cho . Điều này đã làm tôi vấp ngã. time/usr/bin/time
cáu kỉnh_phd_syndrom

Câu trả lời:


2062

Thống kê thời gian thực, người dùng và Sys

Một trong những điều này không giống như người kia. Real đề cập đến thời gian trôi qua thực tế; Người dùng và Sys đề cập đến thời gian CPU chỉ được sử dụng bởi quy trình.

  • Real là thời gian đồng hồ treo tường - thời gian từ khi bắt đầu đến khi kết thúc cuộc gọi. Đây là tất cả thời gian đã trôi qua bao gồm các lát thời gian được sử dụng bởi các quy trình khác và thời gian quy trình bị chặn (ví dụ nếu nó đang chờ I / O hoàn thành).

  • Người dùng là lượng thời gian CPU dành cho mã chế độ người dùng (bên ngoài kernel) trong quy trình. Đây chỉ là thời gian CPU thực tế được sử dụng để thực hiện quá trình. Các quy trình và thời gian khác mà quy trình chi tiêu bị chặn không được tính vào con số này.

  • Sys là lượng thời gian CPU dành cho kernel trong tiến trình. Điều này có nghĩa là thực thi thời gian CPU dành cho các cuộc gọi hệ thống trong nhân, trái ngược với mã thư viện, vẫn đang chạy trong không gian người dùng. Giống như 'người dùng', đây chỉ là thời gian CPU được sử dụng bởi quy trình. Xem bên dưới để biết mô tả ngắn gọn về chế độ kernel (còn được gọi là chế độ 'giám sát viên) và cơ chế gọi hệ thống.

User+Syssẽ cho bạn biết bao nhiêu thời gian CPU thực tế mà quá trình của bạn sử dụng. Lưu ý rằng điều này là trên tất cả các CPU, vì vậy nếu quá trình có nhiều luồng (và quá trình này đang chạy trên một máy tính có nhiều bộ xử lý), nó có thể có khả năng vượt quá thời gian đồng hồ treo tường được báo cáo bởi Real(thường xảy ra). Lưu ý rằng trong đầu ra, các số liệu này bao gồm UserSysthời gian của tất cả các quy trình con (và con cháu của chúng) cũng như khi chúng có thể được thu thập, ví dụ bằng wait(2)hoặc waitpid(2), mặc dù các lệnh hệ thống cơ bản trả về các số liệu thống kê cho quy trình và con riêng của nó.

Nguồn gốc của số liệu thống kê được báo cáo bởi time (1)

Các số liệu thống kê được báo cáo timeđược thu thập từ các cuộc gọi hệ thống khác nhau. 'Người dùng' và 'Sys' đến từ wait (2)( POSIX ) hoặc times (2)( POSIX ), tùy thuộc vào hệ thống cụ thể. 'Real' được tính từ thời gian bắt đầu và kết thúc được thu thập từ gettimeofday (2)cuộc gọi. Tùy thuộc vào phiên bản của hệ thống, nhiều số liệu thống kê khác như số lượng công tắc ngữ cảnh cũng có thể được thu thập theo time.

Trên máy đa bộ xử lý, một quy trình đa luồng hoặc quá trình giả mạo trẻ em có thể có thời gian trôi qua nhỏ hơn tổng thời gian CPU - vì các luồng hoặc quy trình khác nhau có thể chạy song song. Ngoài ra, số liệu thống kê thời gian được báo cáo đến từ các nguồn gốc khác nhau, do đó thời gian được ghi cho các tác vụ chạy rất ngắn có thể bị lỗi làm tròn, như ví dụ được đưa ra bởi các poster ban đầu cho thấy.

Một đoạn tóm tắt ngắn gọn về Kernel so với chế độ Người dùng

Trên Unix hoặc bất kỳ hệ điều hành bộ nhớ được bảo vệ nào , chế độ 'Kernel' hoặc 'Người giám sát' đề cập đến chế độ đặc quyền mà CPU có thể hoạt động. Một số hành động đặc quyền có thể ảnh hưởng đến bảo mật hoặc sự ổn định chỉ có thể được thực hiện khi CPU hoạt động. chế độ này; những hành động này không có sẵn cho mã ứng dụng. Một ví dụ về một hành động như vậy có thể là thao túng MMU để có quyền truy cập vào không gian địa chỉ của một quy trình khác. Thông thường, mã chế độ người dùng không thể làm điều này (với lý do chính đáng), mặc dù nó có thể yêu cầu bộ nhớ chia sẻ từ kernel, có thểđược đọc hoặc viết bởi nhiều hơn một quá trình. Trong trường hợp này, bộ nhớ dùng chung được yêu cầu rõ ràng từ kernel thông qua cơ chế bảo mật và cả hai quá trình phải gắn vào nó một cách rõ ràng để sử dụng nó.

Chế độ đặc quyền thường được gọi là chế độ 'kernel' vì kernel được CPU thực thi trong chế độ này. Để chuyển sang chế độ kernel, bạn phải đưa ra một lệnh cụ thể (thường được gọi là bẫy ) để chuyển CPU sang chế độ kernel và chạy mã từ một vị trí cụ thể được giữ trong bảng nhảy. Vì lý do bảo mật, bạn không thể chuyển sang chế độ kernel và thực thi mã tùy ý - các bẫy được quản lý thông qua một bảng địa chỉ không thể được ghi vào trừ khi CPU đang chạy ở chế độ giám sát. Bạn bẫy với số bẫy rõ ràng và địa chỉ được tra cứu trong bảng nhảy; hạt nhân có số lượng điểm vào hữu hạn được kiểm soát.

Các cuộc gọi 'hệ thống' trong thư viện C (đặc biệt là các cuộc gọi được mô tả trong Phần 2 của trang man) có thành phần chế độ người dùng, đó là những gì bạn thực sự gọi từ chương trình C của mình. Đằng sau hậu trường, họ có thể đưa ra một hoặc nhiều lệnh gọi hệ thống tới kernel để thực hiện các dịch vụ cụ thể như I / O, nhưng chúng vẫn có mã chạy ở chế độ người dùng. Bạn cũng có thể trực tiếp đưa ra một cái bẫy cho chế độ kernel từ bất kỳ mã không gian người dùng nào nếu muốn, mặc dù bạn có thể cần phải viết một đoạn ngôn ngữ lắp ráp để thiết lập các thanh ghi chính xác cho cuộc gọi.

Tìm hiểu thêm về 'sys'

Có những thứ mà mã của bạn không thể thực hiện từ chế độ người dùng - những thứ như phân bổ bộ nhớ hoặc truy cập phần cứng (HDD, mạng, v.v.). Đây là dưới sự giám sát của hạt nhân, và một mình nó có thể làm chúng. Một số hoạt động như mallochoặc fread/ fwritesẽ gọi các hàm kernel này và sau đó sẽ được tính là thời gian của hệ thống. Thật không may, nó không đơn giản như "mọi cuộc gọi đến malloc sẽ được tính vào thời gian của hệ thống". Cuộc gọi mallocsẽ thực hiện một số xử lý của riêng nó (vẫn được tính theo thời gian của người dùng) và sau đó ở đâu đó trên đường đi, nó có thể gọi hàm trong kernel (tính theo thời gian của 'sys'). Sau khi trở về từ cuộc gọi kernel, sẽ có thêm một chút thời gian trong 'người dùng' và sau đómallocsẽ trở lại mã của bạn. Về việc khi chuyển đổi xảy ra và bao nhiêu trong số đó được sử dụng trong chế độ kernel ... bạn không thể nói. Nó phụ thuộc vào việc thực hiện của thư viện. Ngoài ra, các chức năng dường như vô hại khác cũng có thể sử dụng mallocvà tương tự trong nền, một lần nữa sẽ có một số thời gian trong 'sys'.


15
Thời gian dành cho các quá trình con có được tính vào real / sys không?
ron

1
@ron - Theo trang man Linux, nó tổng hợp số lần 'c' với thời gian xử lý, vì vậy tôi nghĩ là có. Tuy nhiên, thời gian cha mẹ và thời gian con có sẵn tách biệt với cuộc gọi (2) lần. Tôi đoán phiên bản Solaris / SysV của thời gian (1) làm điều gì đó tương tự.
Mối quan tâmOfTunbridgeWells

3
User + Sys cho phép bạn đo mức sử dụng CPU của một tiến trình. Bạn có thể sử dụng nó để hiệu suất điểm chuẩn. Điều này đặc biệt hữu ích cho mã đa luồng trong đó nhiều lõi CPU có thể hoạt động trên một tính toán.
Mối quan tâmOfTunbridgeWells

1
Tuy nhiên, không chính xác về chủ đề: Chạy "\ time <cmd>" rất thú vị - nó cung cấp nhiều chi tiết hơn: (tha thứ cho định dạng kém trong nhận xét): $ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps real 0m0.025s người dùng 0m0.004s sys 0m0.018s $ \ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 giờ 11462 pts / 19 00:00:00 ps 0.00user 0.01system 0: 00.02elapsed 95% CPU (0avgtext + 0avgdata 2160maxresident) k 0inputs + 0outputs (0major + 103minor) pagefaults 0swaps $
Kaiwan

1
(Ran hết ký tự trong bình luận trước vậy): Chi tiết hơn? Sử dụng perf [1], [2]. [1] perf.wiki.kernel.org/index.php/Main_Page [2] brendangregg.com/perf.html
Kaiwan

286

Để mở rộng câu trả lời được chấp nhận , tôi chỉ muốn cung cấp một lý do khác tại sao realuser+ sys.

Hãy nhớ rằng realđại diện cho thời gian trôi qua thực tế, trong khi usersyscác giá trị đại diện cho thời gian thực hiện CPU. Kết quả là, trên một hệ thống đa lõi, uservà / hoặc systhời gian (cũng như tổng của chúng) thực sự có thể vượt quá thời gian thực. Ví dụ: trên ứng dụng Java tôi đang chạy cho lớp, tôi nhận được bộ giá trị này:

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

11
Tôi luôn tự hỏi về điều này. Vì tôi biết rằng các chương trình của tôi là một luồng đơn, sự khác biệt giữa người dùng và thời gian thực phải là chi phí VM, đúng không?
Quantum7

9
không cần thiết; Sun JVM trên các máy Solaris cũng như JVM của Apple trên Mac OS X quản lý để sử dụng nhiều hơn một lõi ngay cả trong các ứng dụng đơn luồng. Nếu bạn thực hiện một mẫu của một quy trình java, bạn sẽ thấy những thứ như bộ sưu tập rác chạy trên các luồng riêng biệt (và một số nội dung khác mà tôi không nhớ ra khỏi đỉnh đầu). Tôi không biết nếu bạn thực sự muốn thuật ngữ "VM trên không".
lensovet

4
Tôi đoán số lượng phiếu bầu đã cho bạn đủ danh tiếng bây giờ: D. Vì vậy, bạn nghĩ gì về realvượt quá usersystổng số? Hệ điều hành như chuyển mạch ngữ cảnh có thể là gì?
Muhammad Gelbana

19
Một vấn đề tiềm năng khác có thể là I / O: nếu ứng dụng của bạn dành nhiều thời gian chờ đợi để nhận tệp hoặc luồng, thì rõ ràng thời gian thực sẽ vượt quá thời gian của người dùng vì không sử dụng thời gian CPU trong khi chờ truy cập đến một tập tin hoặc một cái gì đó tương tự.
lensovet

1
@MuhammadGelbana - điều này có thể xảy ra nếu ứng dụng bị chặn thực thi vì bất kỳ lý do gì. Ví dụ, nếu nó đang chờ trên các kết nối I / O, IPC hoặc ổ cắm, nó sẽ ở trạng thái chờ, không tích lũy thời gian CPU cho đến khi cuộc gọi chặn trở lại.
Mối quan

41

thực tế : Thời gian thực tế dành cho việc chạy quá trình từ đầu đến cuối, như thể nó được đo bởi một người bằng đồng hồ bấm giờ

người dùng : Thời gian tích lũy dành cho tất cả các CPU trong quá trình tính toán

sys : Thời gian tích lũy dành cho tất cả các CPU trong các tác vụ liên quan đến hệ thống như cấp phát bộ nhớ.

Lưu ý rằng đôi khi người dùng + sys có thể lớn hơn thực, vì nhiều bộ xử lý có thể hoạt động song song.


systhời gian CPU dành cho các cuộc gọi hệ thống (và trình xử lý lỗi trang?)
Peter Cordes

1
realthường được mô tả là thời gian "đồng hồ treo tường".
Peter Cordes

30

Ví dụ POSIX C có thể chạy tối thiểu

Để làm cho mọi thứ cụ thể hơn, tôi muốn làm gương một vài trường hợp cực đoan timevới một số chương trình thử nghiệm C tối thiểu.

Tất cả các chương trình có thể được biên dịch và chạy với:

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

và đã được thử nghiệm trong Ubuntu 18.10, GCC 8.2.0, glibc 2.28, Linux kernel 4.18, máy tính xách tay ThinkPad P51, CPU Intel Core i7-7820HQ (4 lõi / 8 luồng), 2 lần RAM Samsung M471A2K43BB1-CRC (2x 16GiB).

ngủ

Giấc ngủ không bận rộn không được tính vào một trong hai userhoặc sys, chỉ real.

Ví dụ, một chương trình ngủ trong một giây:

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

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

GitHub ngược dòng .

xuất ra một cái gì đó như:

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

Điều tương tự giữ cho các chương trình bị chặn trên IO trở nên có sẵn.

Ví dụ, chương trình sau đây chờ người dùng nhập một ký tự và nhấn enter:

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

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

GitHub ngược dòng .

Và nếu bạn đợi khoảng một giây, nó sẽ xuất ra giống như ví dụ về giấc ngủ giống như:

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

Vì lý do này timecó thể giúp bạn phân biệt giữa các chương trình bị ràng buộc CPU và IO: Các thuật ngữ "ràng buộc CPU" và "ràng buộc I / O" nghĩa là gì?

Nhiều chủ đề

Ví dụ sau đây niterslặp đi lặp lại các công việc hoàn toàn vô dụng của CPU trên các nthreadsluồng:

#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 ngược dòng + mã cốt truyện .

Sau đó, chúng ta vẽ sơ đồ tường, người dùng và hệ thống như là một hàm số lượng luồng cho một lần lặp 10 ^ 10 cố định trên CPU 8 siêu phân luồng của tôi:

nhập mô tả hình ảnh ở đây

Lô dữ liệu .

Từ biểu đồ, chúng ta thấy rằng:

  • Đối với một ứng dụng lõi đơn chuyên sâu về CPU, tường và người dùng giống nhau

  • đối với 2 lõi, người dùng có khoảng 2 lần tường, có nghĩa là thời gian của người dùng được tính trên tất cả các luồng.

    người dùng về cơ bản tăng gấp đôi, và trong khi tường vẫn như cũ.

  • điều này tiếp tục lên đến 8 luồng, phù hợp với số lượng siêu phân luồng trong máy tính của tôi.

    Sau 8, tường cũng bắt đầu tăng, vì chúng tôi không có thêm CPU để đặt thêm công việc trong một khoảng thời gian nhất định!

    Các cao nguyên tỷ lệ tại điểm này.

Lưu ý rằng biểu đồ này chỉ rõ ràng và đơn giản vì công việc hoàn toàn bị ràng buộc bởi CPU: nếu nó bị ràng buộc về bộ nhớ, thì chúng ta sẽ bị giảm hiệu năng sớm hơn với ít lõi hơn vì các truy cập bộ nhớ sẽ bị nghẽn cổ chai như trong Điều gì các thuật ngữ "ràng buộc CPU" và "ràng buộc I / O" có nghĩa là gì?

Sys làm việc nặng với sendfile

Khối lượng công việc sys nặng nhất tôi có thể sử dụng là sử dụng sendfilethao tác sao chép tệp trên không gian kernel: Sao chép tệp theo cách lành mạnh, an toàn và hiệu quả

Vì vậy, tôi tưởng tượng rằng trong nhân memcpynày sẽ là một hoạt động chuyên sâu của CPU.

Đầu tiên tôi khởi tạo một tệp ngẫu nhiên 10GiB lớn với:

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

Sau đó chạy mã:

#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 ngược dòng .

về cơ bản là thời gian hệ thống như mong đợi:

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

Tôi cũng tò mò muốn xem liệu timecó phân biệt được các tòa nhà của các quy trình khác nhau không, vì vậy tôi đã thử:

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

Và kết quả là:

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

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

Thời gian sys gần giống nhau cho cả hai quy trình, nhưng thời gian treo tường lớn hơn vì các quy trình đang cạnh tranh để truy cập đọc đĩa.

Vì vậy, có vẻ như trên thực tế nó giải thích cho quá trình nào đã bắt đầu một công việc kernel đã cho.

Mã nguồn Bash

Khi bạn chỉ làm time <cmd>trên Ubuntu, nó sử dụng từ khóa Bash như có thể thấy từ:

type time

đầu ra nào:

time is a shell keyword

Vì vậy, chúng tôi grep nguồn trong mã nguồn Bash 4.19 cho chuỗi đầu ra:

git grep '"user\b'

dẫn chúng ta đến hàm exec_cmd.ctime_command , sử dụng:

  • gettimeofday()getrusage()nếu cả hai đều có sẵn
  • times() nếu không thì

tất cả đều là các cuộc gọi hệ thống Linuxcác chức năng POSIX .

Mã nguồn GNU Coreutils

Nếu chúng ta gọi nó là:

/usr/bin/time

sau đó nó sử dụng triển khai GNU Coreutils.

Cái này phức tạp hơn một chút, nhưng nguồn có liên quan dường như ở resuse.c và nó có:

  • một wait3cuộc gọi BSD không phải POSIX nếu có sẵn
  • timesgettimeofdaynếu không

14

Real cho thấy tổng thời gian quay vòng cho một quá trình; trong khi Người dùng hiển thị thời gian thực hiện cho các hướng dẫn do người dùng xác định và Sys là thời gian để thực hiện các cuộc gọi hệ thống!

Thời gian thực bao gồm cả thời gian chờ cũng (thời gian chờ cho I / O, v.v.)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.