|
48 | 48 | #elif defined(_AIX) && !defined(__PASE__)
|
49 | 49 | #include <libperfstat.h>
|
50 | 50 | #elif defined(linux) || defined(__GLIBC__)
|
| 51 | +#include <fstream> |
51 | 52 | #include <sys/sysinfo.h>
|
52 | 53 | #endif
|
53 | 54 |
|
@@ -597,6 +598,77 @@ double GetLoadAverage() {
|
597 | 598 | }
|
598 | 599 | #endif // _WIN32
|
599 | 600 |
|
| 601 | +double GetCPUWaitRatio(size_t subproc_number, int parallelism) { |
| 602 | +#if defined(linux) |
| 603 | + static double oncpu_ratio = 100.0; |
| 604 | + static uint64_t prev_user(0), prev_system(0), prev_wall(0); |
| 605 | + |
| 606 | + uint64_t user(0), system(0), wall(0); |
| 607 | + string token; |
| 608 | + |
| 609 | + // Fetch user, system and timestamp counters. |
| 610 | + ifstream cpustat("/sys/fs/cgroup/cpuacct/cpuacct.stat", ifstream::in); |
| 611 | + while (cpustat >> token) { |
| 612 | + if (token == "user") |
| 613 | + cpustat >> user; |
| 614 | + else if (token == "system") |
| 615 | + cpustat >> system; |
| 616 | + } |
| 617 | + ifstream schedstat("/proc/schedstat", ifstream::in); |
| 618 | + while (schedstat >> token) { |
| 619 | + if (token == "timestamp") { |
| 620 | + schedstat >> wall; |
| 621 | + break; |
| 622 | + } |
| 623 | + } |
| 624 | + |
| 625 | + if (user > 0 && system > 0 && wall > 0) |
| 626 | + { |
| 627 | + uint64_t oncpu_ticks = (user - prev_user) + (system - prev_system); |
| 628 | + uint64_t wall_ticks = (wall - prev_wall) * subproc_number; |
| 629 | + |
| 630 | + // Adjust CONFIG_HZ vs _SC_CLK_TCK scaling. |
| 631 | + // CONFIG_HZ is set to 250 in Ubuntu kernels, and I can't find a good |
| 632 | + // way to query this setting from user-space. |
| 633 | + oncpu_ticks *= 250; // CONFIG_HZ |
| 634 | + wall_ticks *= sysconf(_SC_CLK_TCK); |
| 635 | + |
| 636 | + // oncpu_ticks is the amount of time that processes in our cgroup |
| 637 | + // spent on all CPUs combined since previous measurement. |
| 638 | + // wall_ticks is the amount of time that elapsed since previous |
| 639 | + // measurement scaled by number of current subprocesses. |
| 640 | + // |
| 641 | + // Therefore, wall_ticks is the time that subprocesses should have |
| 642 | + // got on CPU with no contention, and oncpu_ticks is the time that |
| 643 | + // they have actually were allowed to run up on all CPUs. |
| 644 | + |
| 645 | + if (wall_ticks > 0) { |
| 646 | + // Clock advanced, so update oncpu_ratio with latest measurments. |
| 647 | + // Pass new measurements through a simple noise filter. |
| 648 | + if (prev_user != 0) { |
| 649 | + oncpu_ratio *= ((double) subproc_number |
| 650 | + / (subproc_number + 1)); |
| 651 | + oncpu_ratio += ((100.0 * oncpu_ticks / wall_ticks) |
| 652 | + / (subproc_number + 1)); |
| 653 | + } |
| 654 | + |
| 655 | + prev_user = user; |
| 656 | + prev_system = system; |
| 657 | + prev_wall = wall; |
| 658 | + } else |
| 659 | + // Clock didn't advance, this usually happens during initial |
| 660 | + // startup, when we start config_.parallelism tasks in rapid |
| 661 | + // succession. Slightly reduce oncpu_ratio to throttle startup |
| 662 | + // of new processes until we get an updated measurement. |
| 663 | + oncpu_ratio *= (double) parallelism / (parallelism + 1); |
| 664 | + } |
| 665 | + |
| 666 | + return 100.0 - oncpu_ratio; |
| 667 | +#else |
| 668 | + return -1.0; |
| 669 | +#endif |
| 670 | +} |
| 671 | + |
600 | 672 | string ElideMiddle(const string& str, size_t width) {
|
601 | 673 | switch (width) {
|
602 | 674 | case 0: return "";
|
|
0 commit comments