parse_logs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. #!/usr/bin/perl
  2. # Parse the log output files of run-experiment
  3. # Send concatenated log files to stdin, or list them on the command
  4. # line.
  5. use strict;
  6. my $showmemusage = 0;
  7. my $netsetup = '';
  8. my %preproc_resources = ();
  9. my @preproc_seconds = (0, 0, 0);
  10. my @preproc_kib = (0, 0, 0);
  11. my @preproc_latencies = (0, 0, 0);
  12. my @preproc_mem_mib = (0, 0, 0);
  13. my %preproc_s_data = ();
  14. my %preproc_kib_data = ();
  15. my %preproc_latencies_data = ();
  16. my %preproc_P0mem_mib_data = ();
  17. my %preproc_P1mem_mib_data = ();
  18. my %preproc_P2mem_mib_data = ();
  19. my %online_s_data = ();
  20. my %online_kib_data = ();
  21. my %online_latencies_data = ();
  22. my %online_P0mem_mib_data = ();
  23. my %online_P1mem_mib_data = ();
  24. my %online_P2mem_mib_data = ();
  25. if ($ARGV[0] eq "-m") {
  26. shift;
  27. $showmemusage = 1;
  28. }
  29. while(<>) {
  30. chomp;
  31. if (/Network setup: (.*)/) {
  32. $netsetup = "$1 ";
  33. next;
  34. }
  35. if (/===== Running prac (.*)/) {
  36. my $cmdline = $1;
  37. if ($cmdline =~ /-[pa]/) {
  38. &parse_preproc($cmdline);
  39. next;
  40. } elsif ($cmdline =~ /^-- read/) {
  41. &parse_read($cmdline);
  42. } elsif ($cmdline =~ /^-- b?bsearch/) {
  43. &parse_bsearch($cmdline);
  44. } elsif ($cmdline =~ /^-- heap/) {
  45. &parse_heap($cmdline);
  46. } else {
  47. warn "Unknown cmdline: $cmdline\n";
  48. while(<>) {
  49. last if /===== End/;
  50. }
  51. }
  52. %preproc_resources = ();
  53. @preproc_seconds = (0, 0, 0);
  54. @preproc_kib = (0, 0, 0);
  55. @preproc_latencies = (0, 0, 0);
  56. @preproc_mem_mib = (0, 0, 0);
  57. next;
  58. }
  59. }
  60. # Convert the data (in the form [n, sum, sum_squares]) to statistics (in
  61. # the form [mean, variance])
  62. my %preproc_s_stats = ();
  63. my %preproc_kib_stats = ();
  64. my %preproc_latencies_stats = ();
  65. my %preproc_P0mem_mib_stats = ();
  66. my %preproc_P1mem_mib_stats = ();
  67. my %preproc_P2mem_mib_stats = ();
  68. my %online_s_stats = ();
  69. my %online_kib_stats = ();
  70. my %online_latencies_stats = ();
  71. my %online_P0mem_mib_stats = ();
  72. my %online_P1mem_mib_stats = ();
  73. my %online_P2mem_mib_stats = ();
  74. &statsify(\%preproc_s_stats, \%preproc_s_data);
  75. &statsify(\%preproc_kib_stats, \%preproc_kib_data);
  76. &statsify(\%preproc_latencies_stats, \%preproc_latencies_data);
  77. &statsify(\%preproc_P0mem_mib_stats, \%preproc_P0mem_mib_data);
  78. &statsify(\%preproc_P1mem_mib_stats, \%preproc_P1mem_mib_data);
  79. &statsify(\%preproc_P2mem_mib_stats, \%preproc_P2mem_mib_data);
  80. &statsify(\%online_s_stats, \%online_s_data);
  81. &statsify(\%online_kib_stats, \%online_kib_data);
  82. &statsify(\%online_latencies_stats, \%online_latencies_data);
  83. &statsify(\%online_P0mem_mib_stats, \%online_P0mem_mib_data);
  84. &statsify(\%online_P1mem_mib_stats, \%online_P1mem_mib_data);
  85. &statsify(\%online_P2mem_mib_stats, \%online_P2mem_mib_data);
  86. # The total values are the sums of the preproc and online values
  87. my %total_s_stats = ();
  88. my %total_kib_stats = ();
  89. &sum_preproc_online(\%total_s_stats, \%preproc_s_stats, \%online_s_stats);
  90. &sum_preproc_online(\%total_kib_stats, \%preproc_kib_stats, \%online_kib_stats);
  91. # Output the data
  92. &output_stats(\%preproc_s_stats, "Preprc", "s");
  93. &output_stats(\%preproc_kib_stats, "Preprc", "KiB");
  94. &output_stats(\%preproc_latencies_stats, "Preprc", "latencies");
  95. if ($showmemusage) {
  96. &output_stats(\%preproc_P0mem_mib_stats, "Preprc", "P0MemMiB");
  97. &output_stats(\%preproc_P1mem_mib_stats, "Preprc", "P1MemMiB");
  98. &output_stats(\%preproc_P2mem_mib_stats, "Preprc", "P2MemMiB");
  99. }
  100. &output_stats(\%online_s_stats, "Onln", "s");
  101. &output_stats(\%online_kib_stats, "Onln", "KiB");
  102. &output_stats(\%online_latencies_stats, "Onln", "latencies");
  103. if ($showmemusage) {
  104. &output_stats(\%online_P0mem_mib_stats, "Onln", "P0MemMiB");
  105. &output_stats(\%online_P1mem_mib_stats, "Onln", "P1MemMiB");
  106. &output_stats(\%online_P2mem_mib_stats, "Onln", "P2MemMiB");
  107. }
  108. &output_stats(\%total_s_stats, "Totl", "s");
  109. &output_stats(\%total_kib_stats, "Totl", "KiB");
  110. # Subroutines
  111. sub parse_preproc {
  112. my $cmdline = $_[0];
  113. my $who = 0;
  114. # Reset the preproc usages unless we're appending to a previous
  115. # preproc
  116. unless ($cmdline =~ /-a/) {
  117. %preproc_resources = ();
  118. @preproc_seconds = (0, 0, 0);
  119. @preproc_kib = (0, 0, 0);
  120. @preproc_latencies = (0, 0, 0);
  121. @preproc_mem_mib = (0, 0, 0);
  122. }
  123. &parse_resources(\%preproc_resources, $cmdline);
  124. while(<>) {
  125. if (/===== P([012]) output/) {
  126. $who = $1;
  127. next;
  128. }
  129. last if /===== End/;
  130. # The log was malformed
  131. die "Malformed log" if /===== Running/;
  132. if (/^(\d+) message bytes sent/) {
  133. $preproc_kib[$who] += $1 / 1024;
  134. } elsif (/^(\d+) Lamport clock/) {
  135. $preproc_latencies[$who] += $1;
  136. } elsif (/^(\d+) milliseconds wall clock/) {
  137. $preproc_seconds[$who] += $1 / 1000;
  138. } elsif (/^Mem: (\d+) KiB/) {
  139. $preproc_mem_mib[$who] += $1 / 1024;
  140. }
  141. }
  142. }
  143. # Parse a resource usage string, and accumulate the values into the
  144. # given dict
  145. sub parse_resources {
  146. my ($dict, $resstr) = @_;
  147. while ($resstr =~ /(\S+):(\d+)/g) {
  148. $dict->{$1} = 0 unless defined $dict->{$1};
  149. $dict->{$1} += $2;
  150. }
  151. }
  152. # Serialize a resource usage dict back into a canonical string
  153. sub serialize_resources {
  154. my $dict = $_[0];
  155. my $res = '';
  156. my $k;
  157. foreach $k (sort keys %$dict) {
  158. if ($res ne '') {
  159. $res .= ' ';
  160. }
  161. $res .= $k . ":" . $dict->{$k};
  162. }
  163. $res;
  164. }
  165. sub parse_read {
  166. my $cmdline = $_[0];
  167. my $who = 0;
  168. my @online_seconds = (0, 0, 0);
  169. my @online_kib = (0, 0, 0);
  170. my @online_latencies = (0, 0, 0);
  171. my @online_mem_mib = (0, 0, 0);
  172. unless ($cmdline =~ /read (\d+) (\d+)/) {
  173. die "Cannot parse read cmdline: $cmdline";
  174. }
  175. my ($size, $num) = ($1, $2);
  176. while(<>) {
  177. if (/===== P([012]) output/) {
  178. $who = $1;
  179. next;
  180. }
  181. last if /===== End/;
  182. # The log was malformed
  183. die "Malformed log" if /===== Running/;
  184. if (/^(\d+) message bytes sent/) {
  185. $online_kib[$who] = $1 / 1024;
  186. } elsif (/^(\d+) Lamport clock/) {
  187. $online_latencies[$who] = $1;
  188. } elsif (/^(\d+) milliseconds wall clock/) {
  189. $online_seconds[$who] = $1 / 1000;
  190. } elsif (/^Mem: (\d+) KiB/) {
  191. $online_mem_mib[$who] = $1 / 1024;
  192. } elsif ($who == 0 && /^Precomputed values used: (.*)/) {
  193. my %used_resources = ();
  194. &parse_resources(\%used_resources, $1);
  195. my $preproc_resources_str = &serialize_resources(\%preproc_resources);
  196. my $used_resources_str = &serialize_resources(\%used_resources);
  197. if ($preproc_resources_str ne $used_resources_str) {
  198. warn "Resource usage does not match preprocessing:\n" .
  199. "Preproc: $preproc_resources_str\n" .
  200. "Used: $used_resources_str\n ";
  201. }
  202. }
  203. }
  204. my $label = "PRAC read $netsetup$size $num";
  205. &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
  206. &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
  207. &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
  208. &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
  209. &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
  210. &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
  211. &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
  212. &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
  213. &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
  214. &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
  215. &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
  216. &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
  217. }
  218. sub parse_bsearch {
  219. my $cmdline = $_[0];
  220. my $optimized = "Opt";
  221. if ($cmdline =~ /bbsearch/) {
  222. $optimized = "Basic";
  223. }
  224. my $who = 0;
  225. my $section = '';
  226. my @online_seconds = (0, 0, 0);
  227. my @online_kib = (0, 0, 0);
  228. my @online_latencies = (0, 0, 0);
  229. my @online_mem_mib = (0, 0, 0);
  230. unless ($cmdline =~ /b?bsearch (\d+) (\d+)/) {
  231. die "Cannot parse bsearch cmdline: $cmdline";
  232. }
  233. my ($size, $num) = ($1, $2);
  234. while(<>) {
  235. if (/===== P([012]) output/) {
  236. $who = $1;
  237. next;
  238. }
  239. if (/===== ([A-Z ]+) =====/) {
  240. $section = $1;
  241. next;
  242. }
  243. last if /===== End/;
  244. # The log was malformed
  245. die "Malformed log" if /===== Running/;
  246. if ($section eq "BINARY SEARCH") {
  247. if (/^(\d+) message bytes sent/) {
  248. $online_kib[$who] = $1 / 1024;
  249. } elsif (/^(\d+) Lamport clock/) {
  250. $online_latencies[$who] = $1;
  251. } elsif (/^(\d+) milliseconds wall clock/) {
  252. $online_seconds[$who] = $1 / 1000;
  253. } elsif (/^Mem: (\d+) KiB/) {
  254. $online_mem_mib[$who] = $1 / 1024;
  255. }
  256. }
  257. if ($who == 0 && /^Precomputed values used: (.*)/) {
  258. my %used_resources = ();
  259. &parse_resources(\%used_resources, $1);
  260. my $preproc_resources_str = &serialize_resources(\%preproc_resources);
  261. my $used_resources_str = &serialize_resources(\%used_resources);
  262. if ($preproc_resources_str ne $used_resources_str) {
  263. warn "Resource usage does not match preprocessing:\n" .
  264. "Preproc: $preproc_resources_str\n" .
  265. "Used: $used_resources_str\n ";
  266. }
  267. }
  268. }
  269. my $label = "${optimized}PRAC bsearch $netsetup$size $num";
  270. &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
  271. &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
  272. &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
  273. &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
  274. &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
  275. &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
  276. &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
  277. &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
  278. &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
  279. &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
  280. &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
  281. &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
  282. }
  283. sub parse_heap {
  284. my $cmdline = $_[0];
  285. my $who = 0;
  286. my $section = '';
  287. my @online_seconds = (0, 0, 0);
  288. my @online_kib = (0, 0, 0);
  289. my @online_latencies = (0, 0, 0);
  290. my @online_mem_mib = (0, 0, 0);
  291. my $optimized = "Opt";
  292. my $oper = "";
  293. unless ($cmdline =~ /heap -m (\d+) -d \d+ -i (\d+) -e (\d+) -opt (\d+) -s 0/) {
  294. die "Cannot parse heap cmdline: $cmdline";
  295. }
  296. my ($size, $nins, $next, $optflag) = ($1, $2, $3, $4);
  297. if ($nins > 0 && $next > 0) {
  298. die "heap does both insertions and extractions: $cmdline\n";
  299. }
  300. $oper = "Ins" if $nins > 0;
  301. $oper = "Ext" if $next > 0;
  302. if ($optflag == 0) {
  303. $optimized = "Basic";
  304. }
  305. my $num = ($nins + $next);
  306. while(<>) {
  307. if (/===== P([012]) output/) {
  308. $who = $1;
  309. next;
  310. }
  311. if (/===== ([A-Za-z ]+) Stats =====/) {
  312. $section = $1;
  313. next;
  314. }
  315. last if /===== End/;
  316. # The log was malformed
  317. die "Malformed log" if /===== Running/;
  318. my $rightsection = 0;
  319. if ($section eq "Insert" && $oper eq "Ins") {
  320. $rightsection = 1;
  321. }
  322. if ($section eq "Extract Min" && $oper eq "Ext") {
  323. $rightsection = 1;
  324. }
  325. if ($rightsection) {
  326. if (/^(\d+) message bytes sent/) {
  327. $online_kib[$who] = $1 / 1024;
  328. } elsif (/^(\d+) Lamport clock/) {
  329. $online_latencies[$who] = $1;
  330. } elsif (/^(\d+) milliseconds wall clock/) {
  331. $online_seconds[$who] = $1 / 1000;
  332. } elsif (/^Mem: (\d+) KiB/) {
  333. $online_mem_mib[$who] = $1 / 1024;
  334. }
  335. }
  336. if ($who == 0 && /^Precomputed values used: (.*)/) {
  337. my %used_resources = ();
  338. &parse_resources(\%used_resources, $1);
  339. my $preproc_resources_str = &serialize_resources(\%preproc_resources);
  340. my $used_resources_str = &serialize_resources(\%used_resources);
  341. if ($preproc_resources_str ne $used_resources_str) {
  342. warn "Resource usage does not match preprocessing:\n" .
  343. "Preproc: $preproc_resources_str\n" .
  344. "Used: $used_resources_str\n ";
  345. }
  346. }
  347. }
  348. my $label = "${optimized}PRAC heap$oper $netsetup$size $num";
  349. &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
  350. &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
  351. &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
  352. &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
  353. &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
  354. &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
  355. &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
  356. &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
  357. &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
  358. &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
  359. &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
  360. &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
  361. }
  362. sub maxarray {
  363. my $max = $_[0];
  364. foreach (@_) {
  365. $max = $_ if $_ > $max;
  366. }
  367. $max;
  368. }
  369. sub avgarray {
  370. my $sum = 0;
  371. my $n = 0;
  372. foreach (@_) {
  373. $sum += $_;
  374. $n += 1;
  375. }
  376. $sum / $n;
  377. }
  378. # Pass:
  379. # - a reference to a dictionary
  380. # - the key into that dictionary
  381. # - the new data point
  382. # Data is stored in the dictionary as a triple (n, sum, sum_squares)
  383. sub accum_data {
  384. my ($dict, $key, $data) = @_;
  385. $dict->{$key} = [0, 0, 0] unless defined $dict->{$key};
  386. $dict->{$key}->[0] += 1;
  387. $dict->{$key}->[1] += $data;
  388. $dict->{$key}->[2] += ($data * $data);
  389. }
  390. # Convert data (in the form [n, sum, sum_squares]) to statistics (in
  391. # the form [mean, variance])
  392. sub statsify {
  393. my ($sdict, $ddict) = @_;
  394. my $key;
  395. foreach $key (keys %$ddict) {
  396. my $data = $ddict->{$key};
  397. my $n = $data->[0];
  398. my $sum = $data->[1];
  399. my $sumsq = $data->[2];
  400. if ($n == 0) {
  401. $sdict->{$key} = [undef, undef];
  402. } elsif ($n == 1) {
  403. $sdict->{$key} = [$sum, undef];
  404. } else {
  405. $sdict->{$key} = [$sum/$n, ($sumsq - ($sum*$sum/$n))/($n-1)];
  406. }
  407. }
  408. }
  409. # Turn a stat array [mean, variance] into a string to display
  410. sub statstr {
  411. my $data = $_[0];
  412. if (defined $data->[1]) {
  413. my $mean = $data->[0];
  414. my $stddev = $data->[1] > 0 ? sqrt($data->[1]) : 0;
  415. return "$mean ± $stddev";
  416. } elsif (defined $data->[0]) {
  417. return $data->[0];
  418. } else {
  419. return "none"
  420. }
  421. }
  422. # Sum two stat arrays
  423. sub statsum {
  424. my ($data0, $data1) = @_;
  425. if (defined $data0->[1] && defined $data1->[1]) {
  426. return [$data0->[0] + $data1->[0], $data0->[1] + $data1->[1]];
  427. } else {
  428. return [$data0->[0] + $data1->[0], undef];
  429. }
  430. }
  431. # Add the preproc and online stats to get the total stats
  432. sub sum_preproc_online {
  433. my ($tdict, $pdict, $odict) = @_;
  434. my $key;
  435. foreach $key (keys %$pdict) {
  436. if (defined $odict->{$key}) {
  437. $tdict->{$key} = &statsum($pdict->{$key}, $odict->{$key});
  438. }
  439. }
  440. }
  441. # Output the stats in the given dictionary. Append $phase to the
  442. # protocol name, and add $units to the end.
  443. sub output_stats {
  444. my ($dict, $phase, $units) = @_;
  445. my $label;
  446. foreach $label (sort keys %$dict) {
  447. my $printlabel = $label;
  448. $printlabel =~ s/PRAC/PRAC$phase/;
  449. print $printlabel, " ", &statstr($dict->{$label}), " $units\n";
  450. }
  451. }