Browse Source

repro and parse_logs scripts for Figures 6, 7, 8

Ian Goldberg 6 months ago
parent
commit
e71014a360
2 changed files with 714 additions and 0 deletions
  1. 477 0
      repro/parse_logs
  2. 237 0
      repro/repro

+ 477 - 0
repro/parse_logs

@@ -0,0 +1,477 @@
+#!/usr/bin/perl
+
+# Parse the log output files of run-experiment
+# Send concatenated log files to stdin, or list them on the command
+# line.
+
+use strict;
+
+my $showmemusage = 0;
+my $netsetup = '';
+my %preproc_resources = ();
+my @preproc_seconds = (0, 0, 0);
+my @preproc_kib = (0, 0, 0);
+my @preproc_latencies = (0, 0, 0);
+my @preproc_mem_mib = (0, 0, 0);
+my %preproc_s_data = ();
+my %preproc_kib_data = ();
+my %preproc_latencies_data = ();
+my %preproc_P0mem_mib_data = ();
+my %preproc_P1mem_mib_data = ();
+my %preproc_P2mem_mib_data = ();
+my %online_s_data = ();
+my %online_kib_data = ();
+my %online_latencies_data = ();
+my %online_P0mem_mib_data = ();
+my %online_P1mem_mib_data = ();
+my %online_P2mem_mib_data = ();
+
+if ($ARGV[0] eq "-m") {
+    shift;
+    $showmemusage = 1;
+}
+
+while(<>) {
+    chomp;
+    if (/Network setup: (.*)/) {
+        $netsetup = "$1 ";
+        next;
+    }
+    if (/===== Running prac (.*)/) {
+        my $cmdline = $1;
+        if ($cmdline =~ /-[pa]/) {
+            &parse_preproc($cmdline);
+            next;
+        } elsif ($cmdline =~ /^-- read/) {
+            &parse_read($cmdline);
+        } elsif ($cmdline =~ /^-- b?bsearch/) {
+            &parse_bsearch($cmdline);
+        } elsif ($cmdline =~ /^-- heap/) {
+            &parse_heap($cmdline);
+        } else {
+            warn "Unknown cmdline: $cmdline\n";
+            while(<>) {
+                last if /===== End/;
+            }
+        }
+        %preproc_resources = ();
+        @preproc_seconds = (0, 0, 0);
+        @preproc_kib = (0, 0, 0);
+        @preproc_latencies = (0, 0, 0);
+        @preproc_mem_mib = (0, 0, 0);
+        next;
+    }
+}
+
+# Convert the data (in the form [n, sum, sum_squares]) to statistics (in
+# the form [mean, variance])
+my %preproc_s_stats = ();
+my %preproc_kib_stats = ();
+my %preproc_latencies_stats = ();
+my %preproc_P0mem_mib_stats = ();
+my %preproc_P1mem_mib_stats = ();
+my %preproc_P2mem_mib_stats = ();
+my %online_s_stats = ();
+my %online_kib_stats = ();
+my %online_latencies_stats = ();
+my %online_P0mem_mib_stats = ();
+my %online_P1mem_mib_stats = ();
+my %online_P2mem_mib_stats = ();
+&statsify(\%preproc_s_stats, \%preproc_s_data);
+&statsify(\%preproc_kib_stats, \%preproc_kib_data);
+&statsify(\%preproc_latencies_stats, \%preproc_latencies_data);
+&statsify(\%preproc_P0mem_mib_stats, \%preproc_P0mem_mib_data);
+&statsify(\%preproc_P1mem_mib_stats, \%preproc_P1mem_mib_data);
+&statsify(\%preproc_P2mem_mib_stats, \%preproc_P2mem_mib_data);
+&statsify(\%online_s_stats, \%online_s_data);
+&statsify(\%online_kib_stats, \%online_kib_data);
+&statsify(\%online_latencies_stats, \%online_latencies_data);
+&statsify(\%online_P0mem_mib_stats, \%online_P0mem_mib_data);
+&statsify(\%online_P1mem_mib_stats, \%online_P1mem_mib_data);
+&statsify(\%online_P2mem_mib_stats, \%online_P2mem_mib_data);
+
+# The total values are the sums of the preproc and online values
+my %total_s_stats = ();
+my %total_kib_stats = ();
+&sum_preproc_online(\%total_s_stats, \%preproc_s_stats, \%online_s_stats);
+&sum_preproc_online(\%total_kib_stats, \%preproc_kib_stats, \%online_kib_stats);
+
+# Output the data
+&output_stats(\%preproc_s_stats, "Preprc", "s");
+&output_stats(\%preproc_kib_stats, "Preprc", "KiB");
+&output_stats(\%preproc_latencies_stats, "Preprc", "latencies");
+if ($showmemusage) {
+    &output_stats(\%preproc_P0mem_mib_stats, "Preprc", "P0MemMiB");
+    &output_stats(\%preproc_P1mem_mib_stats, "Preprc", "P1MemMiB");
+    &output_stats(\%preproc_P2mem_mib_stats, "Preprc", "P2MemMiB");
+}
+&output_stats(\%online_s_stats, "Onln", "s");
+&output_stats(\%online_kib_stats, "Onln", "KiB");
+&output_stats(\%online_latencies_stats, "Onln", "latencies");
+if ($showmemusage) {
+    &output_stats(\%online_P0mem_mib_stats, "Onln", "P0MemMiB");
+    &output_stats(\%online_P1mem_mib_stats, "Onln", "P1MemMiB");
+    &output_stats(\%online_P2mem_mib_stats, "Onln", "P2MemMiB");
+}
+&output_stats(\%total_s_stats, "Totl", "s");
+&output_stats(\%total_kib_stats, "Totl", "KiB");
+
+# Subroutines
+
+sub parse_preproc {
+    my $cmdline = $_[0];
+    my $who = 0;
+    # Reset the preproc usages unless we're appending to a previous
+    # preproc
+    unless ($cmdline =~ /-a/) {
+        %preproc_resources = ();
+        @preproc_seconds = (0, 0, 0);
+        @preproc_kib = (0, 0, 0);
+        @preproc_latencies = (0, 0, 0);
+        @preproc_mem_mib = (0, 0, 0);
+    }
+    &parse_resources(\%preproc_resources, $cmdline);
+    while(<>) {
+        if (/===== P([012]) output/) {
+            $who = $1;
+            next;
+        }
+        last if /===== End/;
+        # The log was malformed
+        die "Malformed log" if /===== Running/;
+        if (/^(\d+) message bytes sent/) {
+            $preproc_kib[$who] += $1 / 1024;
+        } elsif (/^(\d+) Lamport clock/) {
+            $preproc_latencies[$who] += $1;
+        } elsif (/^(\d+) milliseconds wall clock/) {
+            $preproc_seconds[$who] += $1 / 1000;
+        } elsif (/^Mem: (\d+) KiB/) {
+            $preproc_mem_mib[$who] += $1 / 1024;
+        }
+    }
+}
+
+# Parse a resource usage string, and accumulate the values into the
+# given dict
+sub parse_resources {
+    my ($dict, $resstr) = @_;
+    while ($resstr =~ /(\S+):(\d+)/g) {
+        $dict->{$1} = 0 unless defined $dict->{$1};
+        $dict->{$1} += $2;
+    }
+}
+
+# Serialize a resource usage dict back into a canonical string
+sub serialize_resources {
+    my $dict = $_[0];
+    my $res = '';
+    my $k;
+    foreach $k (sort keys %$dict) {
+        if ($res ne '') {
+            $res .= ' ';
+        }
+        $res .= $k . ":" . $dict->{$k};
+    }
+    $res;
+}
+
+sub parse_read {
+    my $cmdline = $_[0];
+    my $who = 0;
+    my @online_seconds = (0, 0, 0);
+    my @online_kib = (0, 0, 0);
+    my @online_latencies = (0, 0, 0);
+    my @online_mem_mib = (0, 0, 0);
+
+    unless ($cmdline =~ /read (\d+) (\d+)/) {
+        die "Cannot parse read cmdline: $cmdline";
+    }
+    my ($size, $num) = ($1, $2);
+    while(<>) {
+        if (/===== P([012]) output/) {
+            $who = $1;
+            next;
+        }
+        last if /===== End/;
+        # The log was malformed
+        die "Malformed log" if /===== Running/;
+        if (/^(\d+) message bytes sent/) {
+            $online_kib[$who] = $1 / 1024;
+        } elsif (/^(\d+) Lamport clock/) {
+            $online_latencies[$who] = $1;
+        } elsif (/^(\d+) milliseconds wall clock/) {
+            $online_seconds[$who] = $1 / 1000;
+        } elsif (/^Mem: (\d+) KiB/) {
+            $online_mem_mib[$who] = $1 / 1024;
+        } elsif ($who == 0 && /^Precomputed values used: (.*)/) {
+            my %used_resources = ();
+            &parse_resources(\%used_resources, $1);
+            my $preproc_resources_str = &serialize_resources(\%preproc_resources);
+            my $used_resources_str = &serialize_resources(\%used_resources);
+            if ($preproc_resources_str ne $used_resources_str) {
+                warn "Resource usage does not match preprocessing:\n" .
+                    "Preproc: $preproc_resources_str\n" .
+                    "Used: $used_resources_str\n ";
+            }
+        }
+    }
+    my $label = "PRAC read $netsetup$size $num";
+    &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
+    &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
+    &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
+    &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
+    &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
+    &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
+    &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
+    &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
+    &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
+    &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
+    &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
+    &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
+}
+
+sub parse_bsearch {
+    my $cmdline = $_[0];
+    my $optimized = "Opt";
+    if ($cmdline =~ /bbsearch/) {
+        $optimized = "Basic";
+    }
+    my $who = 0;
+    my $section = '';
+    my @online_seconds = (0, 0, 0);
+    my @online_kib = (0, 0, 0);
+    my @online_latencies = (0, 0, 0);
+    my @online_mem_mib = (0, 0, 0);
+
+    unless ($cmdline =~ /b?bsearch (\d+) (\d+)/) {
+        die "Cannot parse bsearch cmdline: $cmdline";
+    }
+    my ($size, $num) = ($1, $2);
+    while(<>) {
+        if (/===== P([012]) output/) {
+            $who = $1;
+            next;
+        }
+        if (/===== ([A-Z ]+) =====/) {
+            $section = $1;
+            next;
+        }
+        last if /===== End/;
+        # The log was malformed
+        die "Malformed log" if /===== Running/;
+        if ($section eq "BINARY SEARCH") {
+            if (/^(\d+) message bytes sent/) {
+                $online_kib[$who] = $1 / 1024;
+            } elsif (/^(\d+) Lamport clock/) {
+                $online_latencies[$who] = $1;
+            } elsif (/^(\d+) milliseconds wall clock/) {
+                $online_seconds[$who] = $1 / 1000;
+            } elsif (/^Mem: (\d+) KiB/) {
+                $online_mem_mib[$who] = $1 / 1024;
+            }
+        }
+        if ($who == 0 && /^Precomputed values used: (.*)/) {
+            my %used_resources = ();
+            &parse_resources(\%used_resources, $1);
+            my $preproc_resources_str = &serialize_resources(\%preproc_resources);
+            my $used_resources_str = &serialize_resources(\%used_resources);
+            if ($preproc_resources_str ne $used_resources_str) {
+                warn "Resource usage does not match preprocessing:\n" .
+                    "Preproc: $preproc_resources_str\n" .
+                    "Used: $used_resources_str\n ";
+            }
+        }
+    }
+    my $label = "${optimized}PRAC bsearch $netsetup$size $num";
+    &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
+    &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
+    &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
+    &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
+    &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
+    &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
+    &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
+    &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
+    &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
+    &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
+    &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
+    &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
+}
+
+sub parse_heap {
+    my $cmdline = $_[0];
+    my $who = 0;
+    my $section = '';
+    my @online_seconds = (0, 0, 0);
+    my @online_kib = (0, 0, 0);
+    my @online_latencies = (0, 0, 0);
+    my @online_mem_mib = (0, 0, 0);
+
+    my $optimized = "Opt";
+    my $oper = "";
+    unless ($cmdline =~ /heap -m (\d+) -d \d+ -i (\d+) -e (\d+) -opt (\d+) -s 0/) {
+        die "Cannot parse heap cmdline: $cmdline";
+    }
+    my ($size, $nins, $next, $optflag) = ($1, $2, $3, $4);
+    if ($nins > 0 && $next > 0) {
+        die "heap does both insertions and extractions: $cmdline\n";
+    }
+    $oper = "Ins" if $nins > 0;
+    $oper = "Ext" if $next > 0;
+    if ($optflag == 0) {
+        $optimized = "Basic";
+    }
+    my $num = ($nins + $next);
+    while(<>) {
+        if (/===== P([012]) output/) {
+            $who = $1;
+            next;
+        }
+        if (/===== ([A-Za-z ]+) Stats =====/) {
+            $section = $1;
+            next;
+        }
+        last if /===== End/;
+        # The log was malformed
+        die "Malformed log" if /===== Running/;
+        my $rightsection = 0;
+        if ($section eq "Insert" && $oper eq "Ins") {
+            $rightsection = 1;
+        }
+        if ($section eq "Extract Min" && $oper eq "Ext") {
+            $rightsection = 1;
+        }
+        if ($rightsection) {
+            if (/^(\d+) message bytes sent/) {
+                $online_kib[$who] = $1 / 1024;
+            } elsif (/^(\d+) Lamport clock/) {
+                $online_latencies[$who] = $1;
+            } elsif (/^(\d+) milliseconds wall clock/) {
+                $online_seconds[$who] = $1 / 1000;
+            } elsif (/^Mem: (\d+) KiB/) {
+                $online_mem_mib[$who] = $1 / 1024;
+            }
+        }
+        if ($who == 0 && /^Precomputed values used: (.*)/) {
+            my %used_resources = ();
+            &parse_resources(\%used_resources, $1);
+            my $preproc_resources_str = &serialize_resources(\%preproc_resources);
+            my $used_resources_str = &serialize_resources(\%used_resources);
+            if ($preproc_resources_str ne $used_resources_str) {
+                warn "Resource usage does not match preprocessing:\n" .
+                    "Preproc: $preproc_resources_str\n" .
+                    "Used: $used_resources_str\n ";
+            }
+        }
+    }
+    my $label = "${optimized}PRAC heap$oper $netsetup$size $num";
+    &accum_data(\%preproc_s_data, $label, &maxarray(@preproc_seconds));
+    &accum_data(\%preproc_kib_data, $label, &avgarray(@preproc_kib));
+    &accum_data(\%preproc_latencies_data, $label, &maxarray(@preproc_latencies));
+    &accum_data(\%preproc_P0mem_mib_data, $label, $preproc_mem_mib[0]);
+    &accum_data(\%preproc_P1mem_mib_data, $label, $preproc_mem_mib[1]);
+    &accum_data(\%preproc_P2mem_mib_data, $label, $preproc_mem_mib[2]);
+    &accum_data(\%online_s_data, $label, &maxarray(@online_seconds));
+    &accum_data(\%online_kib_data, $label, &avgarray(@online_kib));
+    &accum_data(\%online_latencies_data, $label, &maxarray(@online_latencies));
+    &accum_data(\%online_P0mem_mib_data, $label, $online_mem_mib[0]);
+    &accum_data(\%online_P1mem_mib_data, $label, $online_mem_mib[1]);
+    &accum_data(\%online_P2mem_mib_data, $label, $online_mem_mib[2]);
+}
+
+sub maxarray {
+    my $max = $_[0];
+    foreach (@_) {
+        $max = $_ if $_ > $max;
+    }
+    $max;
+}
+
+sub avgarray {
+    my $sum = 0;
+    my $n = 0;
+    foreach (@_) {
+        $sum += $_;
+        $n += 1;
+    }
+    $sum / $n;
+}
+
+# Pass:
+# - a reference to a dictionary
+# - the key into that dictionary
+# - the new data point
+# Data is stored in the dictionary as a triple (n, sum, sum_squares)
+sub accum_data {
+    my ($dict, $key, $data) = @_;
+    $dict->{$key} = [0, 0, 0] unless defined $dict->{$key};
+    $dict->{$key}->[0] += 1;
+    $dict->{$key}->[1] += $data;
+    $dict->{$key}->[2] += ($data * $data);
+}
+
+# Convert data (in the form [n, sum, sum_squares]) to statistics (in
+# the form [mean, variance])
+sub statsify {
+    my ($sdict, $ddict) = @_;
+    my $key;
+    foreach $key (keys %$ddict) {
+        my $data = $ddict->{$key};
+        my $n = $data->[0];
+        my $sum = $data->[1];
+        my $sumsq = $data->[2];
+        if ($n == 0) {
+            $sdict->{$key} = [undef, undef];
+        } elsif ($n == 1) {
+            $sdict->{$key} = [$sum, undef];
+        } else {
+            $sdict->{$key} = [$sum/$n, ($sumsq - ($sum*$sum/$n))/($n-1)];
+        }
+    }
+}
+
+# Turn a stat array [mean, variance] into a string to display
+sub statstr {
+    my $data = $_[0];
+    if (defined $data->[1]) {
+        my $mean = $data->[0];
+        my $stddev = $data->[1] > 0 ? sqrt($data->[1]) : 0;
+        return "$mean ± $stddev";
+    } elsif (defined $data->[0]) {
+        return $data->[0];
+    } else {
+        return "none"
+    }
+}
+
+# Sum two stat arrays
+sub statsum {
+    my ($data0, $data1) = @_;
+    if (defined $data0->[1] && defined $data1->[1]) {
+        return [$data0->[0] + $data1->[0], $data0->[1] + $data1->[1]];
+    } else {
+        return [$data0->[0] + $data1->[0], undef];
+    }
+}
+
+# Add the preproc and online stats to get the total stats
+sub sum_preproc_online {
+    my ($tdict, $pdict, $odict) = @_;
+    my $key;
+    foreach $key (keys %$pdict) {
+        if (defined $odict->{$key}) {
+            $tdict->{$key} = &statsum($pdict->{$key}, $odict->{$key});
+        }
+    }
+}
+
+# Output the stats in the given dictionary. Append $phase to the
+# protocol name, and add $units to the end.
+sub output_stats {
+    my ($dict, $phase, $units) = @_;
+    my $label;
+    foreach $label (sort keys %$dict) {
+        my $printlabel = $label;
+        $printlabel =~ s/PRAC/PRAC$phase/;
+        print $printlabel, " ", &statstr($dict->{$label}), " $units\n";
+    }
+}

+ 237 - 0
repro/repro

@@ -0,0 +1,237 @@
+#!/bin/bash
+
+# Reproduce the PRAC experiments from our paper:
+
+# Sajin Sasy, Adithya Vadapalli, Ian Goldberg. PRAC: Round-Efficient
+# 3-Party MPC for Dynamic Data Structures
+
+# cd into the directory containing this script (from the bash faq 028)
+if [[ $BASH_SOURCE = */* ]]; then
+  cd -- "${BASH_SOURCE%/*}/" || exit
+fi
+
+# Allow running only subsets of the experiment suite.  Valid values are
+# "test", "all", "none".  ("none" is useful if you just want to re-parse
+# the output of existing logs.)  You can also say "single" followed by
+# all the arguments to "run" (below) to run a single experiment; for
+# example:
+# ./repro single read 20 1
+if [ "$1" = "" ]; then
+    whichexps="test"
+else
+    whichexps="$1"
+fi
+
+# The number of operations per run; the graphs in the paper use 3
+if [ "$whichexps" = "single" -o "$2" = "" ]; then
+    # If there's an explicit experiment on the command line, don't read
+    # the next argument as the number of operations.  $numops will be
+    # ignored, anyway, since it will be specified as part of the
+    # command.
+    numiters=3
+else
+    numiters="$2"
+fi
+
+logname='log'
+
+# Run one experiment
+# The arguments are just the arguments to run-experiment
+run() {
+    now=`date`
+    echo "$now: Running $* ..."
+    logfile="prac_${logname}.out${LOGSUFFIX}"
+    mkdir -p data
+    ../docker/run-experiment $* >> data/$logfile
+}
+
+# Parse the output logs.  We run this in the docker in case you don't
+# have perl installed on the host.
+# Arguments: a list of logfiles
+parse() {
+    if [ "$RAMEN_PARSE_HOST" = "1" ]; then
+        ./parse_logs $*
+    else
+        cat $* | docker exec -w /root/prac/repro -i prac_p0 ./parse_logs
+    fi
+}
+
+# A very small kick-the-tires test to ensure everything compiled and
+# built properly
+if [ "$whichexps" = "test" ]; then
+    echo "Running test experiment..."
+    logname='test'
+    run -o read 16 1
+    echo
+    echo "# Test output"
+    echo
+    parse data/prac_test.out${LOGSUFFIX}
+    echo
+    echo "# End test output"
+    echo
+    exit
+fi
+
+# Be able to run a single experiment specified on the command line
+if [ "$whichexps" = "single" ]; then
+    echo "Running single experiment..."
+    shift
+    run $*
+    exit
+fi
+
+now=`date`
+echo "$now: Starting experiments"
+
+if [ "$whichexps" = "fig6" -o "$whichexps" = "all" ]; then
+    echo "Running Figure 6 experiments..."
+    for iter in $(seq 1 $numiters); do
+        # Figure 6(a)
+        logname='fig6a'
+        for num in 16 32 64 128 256 512 1024 2048; do
+            run -p r20:$num
+            run read 20 $num
+        done
+        # Figure 6(b,c)
+        logname='fig6bc'
+        for size in 16 18 20 22 24 26 28 30; do
+            run -p r${size}:10
+            run read $size 10
+        done
+    done
+fi
+
+if [ "$whichexps" = "fig7" -o "$whichexps" = "all" ]; then
+    echo "Running Figure 7 experiments..."
+    for iter in $(seq 1 $numiters); do
+        # Figure 7(a)
+        logname='fig7a'
+        for num in 4 8 16 32 64; do
+            run -p i19:$num c:$((num*20))
+            run bsearch 20 $num
+        done
+        for num in 4 8 16 32 64; do
+            run -p m:$((num*20)) r20:$((num*20)) c:$((num*20))
+            run bbsearch 20 $num
+        done
+        # Figure 7(b,c)
+        logname='fig7bc'
+        for size in 16 18 20 22 24 26 28; do
+            run -p i$((size-1)):1 c:${size}
+            run bsearch $size 1
+        done
+        for size in 16 18 20 22 24 26 28; do
+            run -p m:${size} r${size}:${size} c:${size}
+            run bbsearch $size 1
+        done
+    done
+fi
+
+if [ "$whichexps" = "fig8" -o "$whichexps" = "all" ]; then
+    echo "Running Figure 8 experiments..."
+    for iter in $(seq 1 $numiters); do
+        # Figure 8(a)
+        logname='fig8a'
+        for num in 4 8 16 32; do
+            run -p m:$((num*57)) a:$((num*19)) s:$((num*18)) i19.3:${num} c:$((num*38))
+            run heap -m 20 -d 20 -i 0 -e ${num} -opt 1 -s 0
+        done
+        for num in 4 8 16 32; do
+            run -p m:$((num*57)) a:$((num*19)) s:$((num*18)) r20:$((num*108)) c:$((num*38))
+            run heap -m 20 -d 20 -i 0 -e ${num} -opt 0 -s 0
+        done
+        # Figure 8(b,c)
+        logname='fig8bc'
+        for size in 16 18 20 22 24 26 28 30; do
+            run -p m:$((size*3-3)) a:$((size-1)) s:$((size-2)) i$((size-1)).3:1 c:$((size*2-2))
+            run heap -m ${size} -d ${size} -i 0 -e 1 -opt 1 -s 0
+        done
+        for size in 16 18 20 22 24 26 28 30; do
+            run -p m:$((size*3-3)) a:$((size-1)) s:$((size-2)) r${size}:$((size*6-12)) c:$((size*2-2))
+            run heap -m ${size} -d ${size} -i 0 -e 1 -opt 0 -s 0
+        done
+    done
+fi
+
+now=`date`
+echo "$now: Experiments complete"
+
+# If you specified a custom log suffix, you're going to be parsing the
+# outputs differently.
+if [ "$LOGSUFFIX" = "" ]; then
+
+parse data/*.out > data/prac.dat
+
+echo
+echo "# Figure 6(a)"
+egrep 'PRACOnln read 20 (16|32|64|128|256|512|1024|2048) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'PRACTotl read 20 (16|32|64|128|256|512|1024|2048) .* s$' data/prac.dat | sort -k4 -n
+echo
+echo "# Figure 6(b)"
+egrep 'PRACOnln read ([0-9]+) 10 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'PRACTotl read ([0-9]+) 10 .* s$' data/prac.dat | sort -k3 -n
+echo
+echo "# Figure 6(c)"
+egrep 'PRACOnln read ([0-9]+) 10 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'PRACTotl read ([0-9]+) 10 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+echo "# Figure 7(a)"
+egrep 'BasicPRACOnln bsearch 20 (4|8|16|32|64) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'BasicPRACTotl bsearch 20 (4|8|16|32|64) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'OptPRACOnln bsearch 20 (4|8|16|32|64) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'OptPRACTotl bsearch 20 (4|8|16|32|64) .* s$' data/prac.dat | sort -k4 -n
+echo
+echo "# Figure 7(b)"
+egrep 'BasicPRACOnln bsearch ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'BasicPRACTotl bsearch ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACOnln bsearch ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACTotl bsearch ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+echo "# Figure 7(c)"
+egrep 'BasicPRACOnln bsearch ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'BasicPRACTotl bsearch ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACOnln bsearch ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACTotl bsearch ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+echo "# Figure 8(a)"
+egrep 'BasicPRACOnln heapExt 20 (4|8|16|32) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'BasicPRACTotl heapExt 20 (4|8|16|32) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'OptPRACOnln heapExt 20 (4|8|16|32) .* s$' data/prac.dat | sort -k4 -n
+echo
+egrep 'OptPRACTotl heapExt 20 (4|8|16|32) .* s$' data/prac.dat | sort -k4 -n
+echo
+echo "# Figure 8(b)"
+egrep 'BasicPRACOnln heapExt ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'BasicPRACTotl heapExt ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACOnln heapExt ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACTotl heapExt ([0-9]+) 1 .* s$' data/prac.dat | sort -k3 -n
+echo
+echo "# Figure 8(c)"
+egrep 'BasicPRACOnln heapExt ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'BasicPRACTotl heapExt ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACOnln heapExt ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+egrep 'OptPRACTotl heapExt ([0-9]+) 1 .* KiB$' data/prac.dat | sort -k3 -n
+echo
+echo "# End figures"
+
+fi