time(1)の出力で「real」、「user」、「sys」は何を意味しますか?


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

時間の出力における「リアル」、「ユーザー」、「sys」の意味は何ですか?

私のアプリをベンチマークするとき、どれが意味がありますか?


2
それらの1つにのみアクセスするにはどうすればよいですか?例えばただリアルタイム?
Mojtaba Ahmadi

1
@ConcernedOfTunbridgeWells
Ahmadi


7
プログラムがそのように速く終了した場合、それらのどれも意味がなく、起動オーバーヘッドのみです。でプログラム全体を測定する場合はtime、少なくとも1秒かかるような処理を実行させます。
Peter Cordes

5
これはtimebashキーワードであることを覚えておくことは本当に重要です。だから、タイピングがman timeされていない bashのためにあなたのmanページを与えtime、むしろそれはのmanページを与えています/usr/bin/time。これは私をつまずかせました。
irritable_phd_syndrom

回答:


2063

実、ユーザー、システムプロセス時間統計

これらの1つは他のものとは異なります。Realは実際の経過時間を指します。ユーザーとシステムは、プロセスでのみ使用されるCPU時間を参照します。

  • 実数時間であり、通話の開始から終了までの時間です。これは、他のプロセスで使用されているタイムスライスやプロセスがブロックされて費やした時間(I / Oが完了するのを待っている場合など)を含むすべての経過時間です。

  • ユーザーは、プロセス内の(カーネル外の)ユーザーモードコードで費やされたCPU時間です。これは、プロセスの実行に使用される実際のCPU時間のみです。他のプロセスと、プロセスがブロックして費やした時間は、この数値には含まれません。

  • Sysは、プロセス内のカーネルで費やされたCPU時間の量です。これは、まだユーザー空間で実行されているライブラリコードとは対照的に、カーネル内のシステムコールに費やされたCPU時間の実行を意味します。「ユーザー」と同様に、これはプロセスで使用されるCPU時間のみです。カーネルモード(「スーパーバイザー」モードとも呼ばれる)とシステムコールメカニズムの簡単な説明については、以下を参照してください。

User+Sysプロセスが使用した実際のCPU時間を示します。これはすべてのCPUにまたがるので、プロセスに複数のスレッドがある場合(およびこのプロセスが複数のプロセッサーを搭載したコンピューターで実行されているReal場合)、報告されたウォールクロック時間を超える可能性があります(これは通常発生します)。出力にこれらの数字が含まれていることを注意UserしてSys、彼らが収集されている可能性がときにすべての子プロセス(とその子孫)の時だけでなく、例えばによってwait(2)waitpid(2)、基礎となるシステム・コールは、別途プロセスの統計とその子を返しますが。

によって報告された統計の起源 time (1)

によって報告さtimeれる統計情報は、さまざまなシステムコールから収集されます。'User'と 'Sys' は、特定のシステムに応じて、wait (2)POSIX)またはtimes (2)POSIX)から取得されます。「実際」は、gettimeofday (2)通話から収集された開始時間と終了時間から計算されます。システムのバージョンによっては、コンテキストスイッチの数など、その他のさまざまな統計情報もによって収集される場合がありますtime

マルチプロセッサマシンでは、マルチスレッドプロセスまたは子をフォークするプロセスは、異なるスレッドまたはプロセスが並行して実行される可能性があるため、合計CPU時間よりも経過時間が短い可能性があります。また、報告された時間統計はさまざまな起源から来ているため、非常に短い実行タスクについて記録された時間は、元のポスターで示された例が示すように、丸め誤差の影響を受ける可能性があります。

カーネルモードとユーザーモードの概要

Unixまたは任意の保護メモリオペレーティングシステムでは、「カーネル」または「スーパーバイザ」モードは、CPUが動作できる特権モードを指します。セキュリティまたは安定性に影響を与える可能性がある特定の特権アクションは、CPUが動作しているときにのみ実行できますこのモード。これらのアクションは、アプリケーションコードでは使用できません。このようなアクションの例としては、MMUを操作して別のプロセスのアドレス空間にアクセスすることが挙げられます。通常、ユーザーモードコードはこれを行うことはできません(正当な理由があります)が、カーネルから共有メモリを要求できます。複数のプロセスによって読み書きされる。この場合、共有メモリは安全なメカニズムを介してカーネルから明示的に要求され、両方のプロセスはそれを使用するために明示的に接続する必要があります。

カーネルはこのモードで実行されているCPUによって実行されるため、特権モードは通常「カーネル」モードと呼ばれます。カーネルモードに切り替えるには、CPUをカーネルモードで実行するように切り替え、ジャンプテーブルに保持されている特定の場所からコードを実行する特定の命令(しばしばトラップと呼ばれる)を発行する必要があります。 セキュリティ上の理由から、カーネルモードに切り替えて任意のコードを実行することはできません。トラップは、CPUがスーパーバイザモードで実行されていない限り、書き込むことができないアドレスのテーブルを通じて管理されます。明示的なトラップ番号でトラップすると、ジャンプテーブルでアドレスが検索されます。カーネルには有限数の制御されたエントリポイントがあります。

Cライブラリの「システム」呼び出し(特に、マニュアルページのセクション2で説明されているもの)には、ユーザーモードコンポーネントがあります。これは、Cプログラムから実際に呼び出すものです。舞台裏では、I / Oなどの特定のサービスを実行するためにカーネルに1つ以上のシステムコールを発行する場合がありますが、ユーザーモードで実行されているコードもあります。必要に応じて、アセンブリ空間のスニペットを記述して、呼び出しのレジスタを正しく設定する必要がある場合でも、ユーザー空間コードからカーネルモードに直接トラップを発行することも可能です。

「sys」の詳細

メモリの割り当てやハードウェア(HDD、ネットワークなど)へのアクセスなど、ユーザーモードからコードで実行できない処理があります。これらはカーネルの監視下にあり、それだけで実行できます。mallocor fread/ などの一部の操作は、fwriteこれらのカーネル関数を呼び出し、「sys」時間としてカウントされます。残念ながら、「mallocの呼び出しはすべて「sys」時間でカウントされる」ほど単純ではありません。への呼び出しmallocは、それ自体の処理(まだ「ユーザー」時間でカウントされます)を実行し、その後、カーネルで関数を呼び出すことができる途中(「sys」時間でカウントされます)のどこかで実行します。カーネルコールから戻った後、「user」にしばらく時間がかかり、その後mallocコードに戻ります。いつ切り替えが発生し、そのどれだけがカーネルモードで費やされているかについては...言うことはできません。ライブラリの実装に依存します。また、一見無害な他の機能もmallocバックグラウンドで使用されている可能性があり、その場合も「sys」でしばらく時間がかかります。


15
子プロセスが費やした時間は実数/システムにカウントされますか?
ron

1
@ron-Linuxのmanページによると、「c」時間とプロセス時間を集計しているため、そうだと思います。ただし、親時間と子時間はtimes(2)呼び出しとは別に利用できます。Solaris(SysV)バージョンのtime(1)も同様のことをしていると思います。
ConcernedOfTunbridgeWells 2011

3
User + Sysを使用すると、プロセスのCPU使用率を測定できます。パフォーマンスのベンチマークに使用できます。これは、複数のCPUコアが計算に取り組んでいる可能性があるマルチスレッドコードで特に役立ちます。
ConcernedOfTunbridgeWells 2014年

1
正確にはトピックではありませんが、それでも「\ time <cmd>」を実行することは興味深いです-詳細を提供します:(コメントの不適切なフォーマットを許可):$ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11459 pts / 19 00:00:00 ps real 0m0.025s user 0m0.004s sys 0m0.018s $ \ time ps PID TTY TIME CMD 9437 pts / 19 00:00:00 bash 11461 pts / 19 00:00:00 time 11462 pts / 19 00:00:00 ps 0.00user 0.01system 0:00.02経過95%CPU(0avgtext + 0avgdata 2160maxresident)k 0inputs + 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

286

受け入れられた答えを拡張するために、私はrealuser+である別の理由を提供したかっただけsysです。

realは実際の経過時間を表しusersys値はCPU実行時間を表すことに注意してください。その結果、マルチコアシステムでは、userandおよび/またはsys時間(およびそれらの合計)が実際にリアルタイムを超える可能性があります。たとえば、クラスで実行しているJavaアプリでは、次の値のセットを取得します。

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

11
私はいつもこれについて疑問に思っていました。私のプログラムはシングルスレッドであることを知っているので、ユーザーとリアルタイムの違いはVMオーバーヘッドであるに違いありません。
Quantum7、

9
必ずしも; Solarisマシン上のSun JVMとMac OS X上のAppleのJVMは、シングルスレッドアプリでも複数のコアを使用するように管理しています。Javaプロセスのサンプルを実行すると、ガベージコレクションなどが別のスレッドで実行されていることがわかります(他にも、頭の中で覚えていないことがいくつかあります)。「VMオーバーヘッド」という言葉を本当に言いたいのかどうかはわかりません。
lensovet 2010年

4
賛成票の数は今あなたに十分な評判を与えたと思います:D。では、real超過usersys合計についてどう思いますか?スレッドコンテキストの切り替えなどのOSオーバーヘッドは?
Muhammad Gelbana 2012

19
もう1つの潜在的な問題はI / Oである可能性があります。アプリケーションがファイルまたはストリームの受信を待機するのにかなりの時間を費やしている場合、アクセスの取得を待機している間CPU時間は使用されないため、明らかにリアルタイムはユーザー/ sys時間を大幅に超えます。ファイルまたは類似のものに。
lensovet 2012

1
@MuhammadGelbana-何らかの理由でアプリケーションの実行がブロックされた場合に発生する可能性があります。たとえば、I / O、IPC、またはソケット接続で待機している場合、アイドル状態になり、ブロッキング呼び出しが戻るまでCPU時間を累積しません。
ConcernedOfTunbridgeWells

41

real:プロセスを最初から最後まで実行するのに費やされた実際の時間。まるでそれがストップウォッチを持つ人間によって測定されたかのようです。

ユーザー:すべてのCPUが計算中に費やした累積時間

sys:メモリ割り当てなどのシステム関連タスク中にすべてのCPUが費やした累積時間。

複数のプロセッサが並行して動作する場合があるため、user + sysが実際よりも大きくなる場合があることに注意してください。


sysシステムコールで費やされたCPU時間です(と、ページフォールトハンドラは?)
ピーター・コルド

1
real多くの場合、「壁時計」時間と呼ばれます。
Peter Cordes

30

最小限の実行可能な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

たとえば、1秒間スリープするプログラム:

#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

このためtime、CPUとIOバウンドプログラムを区別するのに役立ちます。「CPUバウンド」と「I / Oバウンド」という用語はどういう意味ですか?

複数のスレッド

次の例はnitersnthreadsスレッド上で純粋にCPUにバインドされた無用な作業を繰り返し実行します。

#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アップストリーム+プロットコード

次に、壁、ユーザー、およびsysを、8つのハイパースレッドCPUで固定された10 ^ 10反復のスレッド数の関数としてプロットします。

ここに画像の説明を入力してください

データをプロットします

グラフから、次のことがわかります。

  • CPU集中型のシングルコアアプリケーションの場合、壁とユーザーはほぼ同じです

  • 2コアの場合、ユーザーは約2倍の壁です。つまり、ユーザー時間はすべてのスレッドでカウントされます。

    ユーザーは基本的に2倍になり、壁は同じままでした。

  • これは最大8スレッドまで続き、これは私のコンピューターのハイパースレッドの数と一致します。

    8時間後、一定の時間内により多くの作業を行うための追加のCPUがないため、壁も増加し始めます!

    この時点で比率は安定しています。

それはメモリがバインドされた場合に示すように、メモリアクセスがボトルネックになるので、その後、私たちはずっと以前より少ないコアでパフォーマンスの低下を得るでしょう:仕事は純粋にCPUバウンドであるため、このグラフだけそれほど明確でシンプルであることに注意してくださいどのような「CPUバウンド」および「I / Oバウンド」という用語はどういう意味ですか?

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、異なるプロセスのシステムコールを区別できるかどうかも知りたくて、試しました。

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ソースコードでソースをgrepします。

git grep '"user\b'

これは、execute_cmd.c functionにつながりtime_commandます。

  • gettimeofday()そして、getrusage()の両方が利用可能な場合
  • times() さもないと

これらはすべてLinuxシステムコールPOSIX関数です。

GNU Coreutilsソースコード

それを次のように呼ぶと、

/usr/bin/time

次に、GNU Coreutils実装を使用します。

これはもう少し複雑ですが、関連するソースはresuse.cにあるようで、次のようになります。

  • 非POSIX BSD wait3呼び出しが利用可能な場合
  • timesgettimeofdayそうでない場合は

14

Realは、プロセスの総所要時間を示します。一方、ユーザーはユーザー定義の命令の実行時間を示し、Sysはシステムコールの実行時間を示します。

リアルタイムには、待ち時間も含まれます(I / Oの待ち時間など)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.