parse_logs 19 KB

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