תהליכים · זמן

times#

דיווח על זמן CPU שנצרך על־ידי תהליך זה וצאצאיו שהסתיימו.

times asks the kernel how many seconds of user-mode and kernel-mode CPU have been charged to the current process, and - separately - to children that have already been reaped. The return is four floating-point seconds: user, system, child-user, child-system. It answers ”how much CPU have I burned?“, not ”how much wall-clock time has passed?“ - for that, see time.

תקציר#

my ($user, $system, $cuser, $csystem) = times;
my $user_only                         = times;    # scalar context

מה מקבלים בחזרה#

בהקשר רשימה, ארבעה ערכים:

  • $user - זמן CPU שהוצא בקוד מצב־משתמש של תהליך זה.

  • $system - זמן CPU שהוצא בקרנל בשם תהליך זה.

  • $cuser - זמן CPU במצב־משתמש של תהליכי ילד שהסתיימו.

  • $csystem - זמן CPU במצב־קרנל של תהליכי ילד שהסתיימו.

כל הארבעה הם שניות כ־floats בדיוק כפול; הרזולוציה היא מיקרו־שנייה ב־Linux (ממקור getrusage).

בהקשר סקלר, times מחזיר רק את $user.

times אינו לוקח ארגומנטים ואינו יכול להיכשל באופן הנראה לקורא.

זמני הצאצאים - כלל ה־״הסתיים״#

$cuser ו־$csystem מצברים CPU רק מצאצאים שנאספו - כלומר wait או waitpid אספו בהצלחה את סטטוס היציאה שלהם, או שהם נאספו אוטומטית משום ש־$SIG{CHLD} הוגדר ל־'IGNORE'. ילד שעדיין רץ, או שיצא אך טרם נאסף (zombie), אינו תורם דבר.

זה משנה כאשר מודדים pipeline שיוצר עובדים:

my $pid = fork;
if ($pid == 0) { do_work(); exit }
# (busy child runs here)
my ($u0, $s0, $cu0, $cs0) = times;     # $cu0, $cs0 still 0
waitpid($pid, 0);
my ($u1, $s1, $cu1, $cs1) = times;     # now $cu1, $cs1 reflect child

עד ש־waitpid חוזר, ה־CPU של הילד אינו מופיע ב־times של ההורה.

דוגמאות#

מדידת עלות CPU של בלוק עבודה, לא כולל sleeps של שעון־קיר או המתנות I/O:

my ($u0, $s0) = times;
compute_heavy();
my ($u1, $s1) = times;
printf "compute used %.3f s user + %.3f s system\n",
       $u1 - $u0, $s1 - $s0;

השוואה של זמן CPU לזמן שעון־קיר - יחס נמוך אומר שהתהליך היה ברובו ממתין ולא מחשב:

use Time::HiRes qw(time);
my $t0 = time;
my ($u0, $s0) = times;
do_mixed_io_and_compute();
my $wall = time - $t0;
my ($u1, $s1) = times;
printf "cpu/wall = %.2f\n", ($u1 - $u0 + $s1 - $s0) / $wall;

ייחוס עומס עבודה מקבילי לצאצאיו. אספו כל ילד לפני קריאת times:

my @pids = map { my $p = fork; $p == 0 ? (worker($_), exit) : $p } 1..4;
waitpid($_, 0) for @pids;
my (undef, undef, $cu, $cs) = times;
printf "workers used %.2f s user + %.2f s system\n", $cu, $cs;

הקשר סקלר כאשר רק זמן המשתמש חשוב - לדוגמה הדפסת self-profiling מהירה:

printf STDERR "[%.2f] reached checkpoint\n", scalar times;

מקרי קצה#

  • ללא ארגומנטים, אין prototype לדאוג ממנו. times הוא built-in ללא ארגומנטים; times() ו־times מתפענחים בצורה זהה. הוא אינו אופרטור התעתוק - זה tr/// (כינוי y///), שהוא טוקן נפרד.

  • הקשר סקלר לעומת הקשר רשימה נושא־עומס. כתיבת my $t = times; קושרת את $user ל־$t, ומשליכה את שלושת הערכים האחרים בשקט. השתמשו ב־my ($u,$s,$cu,$cs) = times; כאשר אתם רוצים את כולם.

  • ילדים שלא נאספו אינם נספרים. ראו את כלל ה־״הסתיים״ למעלה. קריאת times לפני wait נותנת $cuser == $csystem == 0 גם כאשר הילד שורף CPU גלוי ב־top.

  • Threads כן נספרים. ב־Linux, עבודה ב־kernel-threads בתהליך הנוכחי מופיעה ב־$user / $system בדיוק כמו עבודה ב־single-thread. Threads בתהליך נפרד לא, עד שאותו תהליך יוצא ונאסף.

  • השעון מונוטוני־בתוך־תהליך, לא שעון־קיר. times אף פעם אינו הולך אחורה לאורך חיי תהליך יחיד, אך קריאות times עוקבות אינן ניתנות להשוואה ישירה בין הורה לילד - כל חשבונאות getrusage היא מקומית.

  • רזולוציה. Linux מדווח על ticks ברזולוציית מיקרו־שנייה דרך getrusage, ולכן מדידות קצרות (תת־מילישנייה) רועשות אך לא אפס. לתזמון שעון־קיר בדיוק גבוה, השתמשו ב־Time::HiRes::time, לא ב־times.

  • גלישה. הערכים הם floats; הם יכולים עקרונית לאבד דיוק תת־מיקרו־שנייה לאחר ריצות ארוכות מאוד (שנים של CPU), אך זה אינו דאגה לתוכניות רגילות.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • time - שניות שעון־קיר מאז ה־epoch; השתמשו בו כאשר אתם רוצים זמן ממשי שחלף, לא זמן CPU

  • Time::HiRes::time - זמן שעון־קיר ברזולוציה גבוהה, השותף הרגיל לחשבונאות CPU

  • POSIX::times - עטיפה של POSIX המחזירה ערך נוסף של ״clock ticks since boot״ בזמן ממשי; השתמשו בו כאשר אתם זקוקים לשדה החלף שה־built-in משמיט

  • wait - אספו ילד כך שה־CPU שלו יתחיל להיספר ב־$cuser / $csystem

  • waitpid - אספו ילד ספציפי מאותה סיבה, ללא חסימה על ילדים לא־קשורים

  • fork - הפעולה שעלותה מופיעה בעמודות הילד ברגע שהילד נאסף