parse_logs 19 KB

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