1 /++ 2 A POSIX application wrapping getrusage(2) for simple benchmarks. 3 4 SYNOPSIS: 5 `getr <#runs> <command> [<args> ...]` 6 7 OUTPUT: 8 --- 9 $ getr 1000 ./fizzbuzz >/dev/null 10 User time : 0 s, 894347 us 11 System time : 0 s, 673832 us 12 Time : 1568.179 ms (1.568 ms/per) 13 Max RSS : 952 kB 14 Page reclaims : 239344 15 Page faults : 1 16 Block inputs : 0 17 Block outputs : 0 18 vol ctx switches : 0 19 invol ctx switches : 1298 20 --- 21 22 DESCRIPTION: 23 getr is a simple wrapper around the `getrusage`(2) syscall, which can be 24 relied on for basic resource usage reports under Linux, OpenBSD, and macOS 25 (among others). A child command is repeatedly spawned and waited for, and 26 then a `RUSAGE_CHILDREN` report is generated. This program was created as 27 the author was used to very simple bash loops to test performance, which 28 was then found didn't work at all under ksh on OpenBSD. getr is just as 29 easy and just as simple. 30 31 EXIT_STATUS: 32 getr exits with status 1 if it fails to spawn a process, or if its own 33 arguments aren't understood. It exits with status 0 in all other cases, 34 including if the spawned program returns a nonzero exit status. 35 36 EXAMPLES: 37 `getr 1000 ./fizzbuzz > /dev/null` 38 39 `fizzbuzz` is invoked 1000 times, with no arguments, and with getr's own 40 (and therefore `fizzbuzz`'s) standard output piped to /dev/null. The 41 resulting usage report would still be printed to standard error. 42 43 `getr 100 rdmd -c ''` 44 45 `rdmd` in PATH is asked, 100 times, to evaluate the empty string. 46 47 SEE_ALSO: 48 `getrusage`(2), `which`(1), `time`(1), `perf`(1), `valgrind`(1). 49 +/ 50 51 module getr; 52 53 private: 54 55 extern (C) int getrusage(int who, RUsage* usage); 56 57 enum RUSAGE_CHILDREN = -1; 58 59 struct Timeval { 60 long tv_sec; 61 long tv_usec; 62 } 63 64 struct RUsage { 65 Timeval ru_utime; 66 Timeval ru_stime; 67 long ru_maxrss; 68 long ru_ixrss; 69 long ru_idrss; 70 long ru_isrss; 71 long ru_minflt; 72 long ru_majflt; 73 long ru_nswap; 74 long ru_inblock; 75 long ru_oublock; 76 long ru_msgsnd; 77 long ru_msgrcv; 78 long ru_nsignals; 79 long ru_nvcsw; 80 long ru_nivcsw; 81 } 82 83 void report(int times) @trusted { 84 import std.stdio : stderr; 85 86 RUsage usage = void; 87 88 getrusage(RUSAGE_CHILDREN, &usage); 89 immutable long seconds = usage.ru_utime.tv_sec + usage.ru_stime.tv_sec, 90 microseconds = usage.ru_utime.tv_usec + usage.ru_stime.tv_usec, 91 milliseconds = seconds * 1000 + microseconds / 1000; 92 immutable double ms_per = cast(double) milliseconds / cast(double) times; 93 94 stderr.writef!q"REPORT 95 User time : %d s, %d us 96 System time : %d s, %d us 97 Time : %d ms (%.3f ms/per) 98 Max RSS : %s 99 Page reclaims : %d 100 Page faults : %d 101 Block inputs : %d 102 Block outputs : %d 103 vol ctx switches : %d 104 invol ctx switches : %d 105 REPORT"(usage.ru_utime.tv_sec, usage.ru_utime.tv_usec, 106 usage.ru_stime.tv_sec, usage.ru_stime.tv_usec, milliseconds, ms_per, 107 readable_rss(cast(real) usage.ru_maxrss), usage.ru_minflt, usage.ru_majflt, 108 usage.ru_inblock, usage.ru_oublock, usage.ru_nvcsw, usage.ru_nivcsw); 109 } 110 111 string readable_rss(real kb) { 112 import std.format : format; 113 114 if (kb < 1024.0) 115 return format!"%.0f kB"(kb); 116 else if (kb < 1024.0 * 1024.0) 117 return format!"%.1f MB"(kb / 1024.0); 118 else 119 return format!"%.2f GB"(kb / (1024.0 * 1024.0)); 120 } 121 122 void main(string[] args) { 123 import std.stdio : stderr; 124 import core.stdc.stdlib : exit; 125 import std.exception : ifThrown; 126 import std.conv : to; 127 import std.process : spawnProcess, wait; 128 129 void usage() @trusted { 130 stderr.writeln("usage: ", args[0], " <#runs> <command> [<args> ...]"); 131 exit(1); 132 } 133 134 if (args.length < 3) 135 usage(); 136 137 immutable int count = to!int(args[1]).ifThrown(0); 138 139 if (count < 1) 140 usage(); 141 142 foreach (i; 0 .. count) { 143 wait(spawnProcess(args[2 .. $])); 144 } 145 146 report(count); 147 }