Quick benchmarking script - SVF-tools/SVF GitHub Wiki
Below is a quick script (leaning towards hacky) to quickly test improvement/regression between options. Example usage:
usage: ./bench.sh SVF_BIN ARGS1 ARGS2 TIME_LIMIT MEM_LIMIT BITCODE...
: TIME_LIMIT is in hours
: MEM_LIMIT is in GB
where
SVF_BIN
is the path to SVF'swpa
binary.ARGS1
is the set of arguments to SVF which form the baseline (remember to pass these as a single argument, e.g."one two three"
).ARGS2
form the opposite.TIME_LIMIT
andMEM_LIMIT
are resource limits.BITCODE...
is a list of bitcode files to analyse.
The script uses GNU's time
to gather time (real) and memory (maximum RSS), so it's not suitable for anything serious. It also includes everything from bitcode parsing to analysis to statistics collection.
Example command and output:
$ ./bench.sh ~/clone/svf-upstream/Release-build/bin/wpa "-fspta -marked-clocks-only" "-vfspta -marked-clocks-only -node-alloc-strat=dense -ptd=persistent" 5 20 ~/bitcode/gnuplot.bc ~/bitcode/tmux.bc ~/bitcode/i3.bc
= args =
svf binary : /home/mbarbar/clone/svf-upstream/Release-build/bin/wpa
args1 : -fspta -marked-clocks-only
args2 : -vfspta -marked-clocks-only -node-alloc-strat=dense -ptd=persistent
time limit : 5 hours
memory limit : 20 gigabytes
bitcode files : /home/mbarbar/bitcode/gnuplot.bc /home/mbarbar/bitcode/tmux.bc /home/mbarbar/bitcode/i3.bc (3)
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ARGS 1 ā ARGS 2 ā Difference (1/2) ā
ā Program āāāāāāāāāāāāāā¬āāāāāāāāāāā¼āāāāāāāāāāāāā¬āāāāāāāāāāā¼āāāāāāāāāāāāāā¬āāāāāāāāāāāāāā¤
ā ā Time ā Memory ā Time ā Memory ā Time ā Memory ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāā[s]āā¼āāāāā[GB]āā¼āāāāāāāā[s]āā¼āāāāā[GB]āā¼āāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¤
ā /home/mbarbar/bitcode/gnuplot.bc ā 131.12 ā 3.92 ā 89.34 ā 2.73 ā 1.47x ā 1.43x ā
ā /home/mbarbar/bitcode/tmux.bc ā 257.81 ā 8.08 ā 82.92 ā 3.05 ā 3.11x ā 2.65x ā
ā /home/mbarbar/bitcode/i3.bc ā 5.33 ā 0.47 ā 4.74 ā 0.42 ā 1.12x ā 1.11x ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¤
ā Geo. Mean ā ā 2.26x ā 2.05x ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāāā
#!/bin/sh
# GNU time command/path.
time_cmd="/usr/bin/time"
# GNU coreutils timeout command/path.
timeout_cmd="timeout"
usage_exit() {
echo "usage: $0 SVF_BIN ARGS1 ARGS2 TIME_LIMIT MEM_LIMIT BITCODE..." 1>&2
echo " : TIME_LIMIT is in hours" 1>&2
echo " : MEM_LIMIT is in GB" 1>&2
exit 1
}
[ $# -ge 6 ] || usage_exit
wpa="$1"
args1="$2"
args2="$3"
time_limit="$4"
mem_limit="$5"
shift 5
files="$@"
nfiles=`expr $#`
# Check args.
[ -r "$wpa" ] || usage_exit
expr "$time_limit" : "[0-9][0-9]*" > /dev/null || usage_exit
expr "$mem_limit" : "[0-9][0-9]*" > /dev/null || usage_exit
# 9 is the length of "Geo. Mean".
longest_filename=9
for f in $files; do
[ -r "$f" ] || usage_exit;
if [ "${#f}" -gt "$longest_filename" ]; then
longest_filename=${#f}
fi
done
echo " = args = "
echo " svf binary : $wpa"
echo " args1 : $args1"
echo " args2 : $args2"
echo " time limit : $time_limit hours"
echo " memory limit : $mem_limit gigabytes"
echo " bitcode files : $files ($nfiles)"
time_limit_s=$(($time_limit * 60 * 60))
mem_limit_b=$(($mem_limit * 1000 * 1000 * 1000))
time_fmt="real %e\nrss %M"
time_txt=`mktemp`
echo > log.txt
printf "āā%sāā¬āāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāā\n"\
$(head -c $longest_filename < /dev/zero | sed -e 's/\x0/ā/g')
printf "ā %${longest_filename}s ā ARGS 1 ā ARGS 2 ā Difference (1/2) ā\n" ""
printf "ā %${longest_filename}s āāāāāāāāāāāāāā¬āāāāāāāāāāā¼āāāāāāāāāāāāā¬āāāāāāāāāāā¼āāāāāāāāāāāāāā¬āāāāāāāāāāāāāā¤\n" "Program"
printf "ā %${longest_filename}s ā %10s ā %8s ā %10s ā %8s ā %11s ā %11s ā\n"\
" " "Time" "Memory" "Time" "Memory" "Time" "Memory"
printf "āā%sāā¼āāāāāāāā[s]āā¼āāāāā[GB]āā¼āāāāāāāā[s]āā¼āāāāā[GB]āā¼āāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¤\n"\
$(head -c $longest_filename < /dev/zero | sed -e 's/\x0/ā/g')
mem_diff_product_cmd="1"
time_diff_product_cmd="1"
# Whether the mean time/mem. diff. needs to have a sign in front of it.
mean_time_diff_gt=""
mean_time_diff_lt=""
mean_mem_diff_gt=""
mean_mem_diff_lt=""
for f in $files; do
cmd="$wpa $args1 $f"
oox1=""
oox2=""
echo " = running $cmd" >> log.txt
if $timeout_cmd --foreground $time_limit_s \
$time_cmd -f "$time_fmt" -o "$time_txt" \
prlimit --as=$mem_limit_b: \
$cmd $f >> log.txt 2>&1; then
echo " = success" >> log.txt
time1=`grep real $time_txt | cut -f2 -d\ `
disp_time1=$(printf "%10.2f" "$time1")
mem1=`grep rss $time_txt | cut -f2 -d\ `
disp_mem1=$(printf "%8.2f" $(echo "$mem1 / 1000 / 1000" | bc -l))
else
if [ $? -eq 124 ]; then
echo " = OOT" >> log.txt
oox1="OOT"
time1=$time_limit_s
disp_time1="OOT"
disp_mem1="--"
else
echo " = OOM" >> log.txt
oox1="OOM"
mem1=$(($mem_limit_b / 1000))
disp_time1="--"
disp_mem1="OOM"
fi
fi
# Repeat twice, easier than a loop.
cmd="$wpa $args2 $f"
echo " = running $cmd" >> log.txt
if $timeout_cmd --foreground $time_limit_s \
$time_cmd -f "$time_fmt" -o "$time_txt" \
prlimit --as=$mem_limit_b: \
$cmd $f >> log.txt 2>&1; then
echo " = success" >> log.txt
time2=`grep real $time_txt | cut -f2 -d\ `
disp_time2=$(printf "%8.2f" "$time2")
mem2=`grep rss $time_txt | cut -f2 -d\ `
disp_mem2=$(printf "%8.2f" $(echo "$mem2 / 1000 / 1000" | bc -l))
else
if [ $? -eq 124 ]; then
echo " = OOT" >> log.txt
oox2="OOT"
time2=$time_limit_s
disp_time2="OOT"
disp_mem2="--"
else
echo " = OOM" >> log.txt
oox2="OOM"
mem2=$(($mem_limit_b / 1000))
disp_time2="--"
disp_mem2="OOM"
fi
fi
# Recall, when OOM, we set mem to be the memory limit. Similarly for OOT/time.
# When OOM or OOT, we can calculate time or memory, respectively.
if [ "$oox1" != "OOM" -a "$oox2" != "OOM" ]; then
time_diff=$(printf "%.2f" $(echo $time1 / $time2 | bc -l))
fi
if [ "$oox1" != "OOT" -a "$oox2" != "OOT" ]; then
mem_diff=$(printf "%.2f" $(echo $mem1 / $mem2 | bc -l))
fi
# Whether we have a value or not depends on whether we went OOM/OOT.
if [ -n "$oox1" -a -n "$oox2" ]; then
time_diff="--"
mem_diff="--"
elif [ "$oox1" = "OOT" ]; then
time_diff_product_cmd="$time_diff_product_cmd * $time_diff"
time_diff=$(printf "%9s" ">=$time_diff")
time_diff_gt=">="
mem_diff="--"
elif [ "$oox2" = "OOT" ]; then
time_diff_product_cmd="$time_diff_product_cmd * $time_diff"
time_diff=$(printf "%9s" "<=$time_diff")
time_diff_lt="<="
mem_diff="--"
elif [ "$oox1" = "OOM" ]; then
time_diff="--"
mem_diff_product_cmd="$mem_diff_product_cmd * $mem_diff"
mem_diff=$(printf "%9s" ">=$mem_diff")
mem_diff_gt=">="
elif [ "$oox2" = "OOM" ]; then
time_diff="--"
mem_diff_product_cmd="$mem_diff_product_cmd * $mem_diff"
mem_diff=$(printf "%9s" "<=$mem_diff")
mem_diff_lt="<="
else
time_diff_product_cmd="$time_diff_product_cmd * $time_diff"
time_diff=$(printf "%9s" "$time_diff")
mem_diff_product_cmd="$mem_diff_product_cmd * $mem_diff"
mem_diff=$(printf "%9s" "$mem_diff")
fi
printf "ā %${longest_filename}s ā %10s ā %8s ā %10s ā %8s ā %10sx ā %10sx ā\n"\
"$f" "$disp_time1" "$disp_mem1" "$disp_time2" "$disp_mem2" "$time_diff" "$mem_diff"
done
# We might have gotten <==> so we need to sed it out.
time_diff_sign=$(echo "${time_diff_lt}${time_diff_gt}" | sed -e 's/==/=/')
mem_diff_sign=$(echo "${mem_diff_lt}${mem_diff_gt}" | sed -e 's/==/=/')
# Count the number of asteriks (multiplication) in the diff command. 0 means we have no diffs, 1 means
# we have 1 diff and so the mean is just that, and for any more we perform the standard mean calculation.
mult_in_time_diff=$(echo "$time_diff_product_cmd" | tr -cd '*' | wc -c)
mult_in_mem_diff=$(echo "$mem_diff_product_cmd" | tr -cd '*' | wc -c)
if [ "$mult_in_time_diff" -gt 1 ]; then
disp_time_diff_geomean=$(printf "${time_diff_sign}%.2f" $(echo "sqrt($time_diff_product_cmd)" | bc -l))
elif [ "$mult_in_time_diff" -eq 1 ]; then
disp_time_diff_geomean=$(printf "${time_diff_sign}%.2f" $(echo "$time_diff_product_cmd" | bc -l))
else
disp_time_diff_geomean="--"
fi
if [ "$mult_in_mem_diff" -gt 1 ]; then
disp_mem_diff_geomean=$(printf "${mem_diff_sign}%.2f" $(echo "sqrt($mem_diff_product_cmd)" | bc -l))
elif [ "$mult_in_mem_diff" -eq 1 ]; then
disp_mem_diff_geomean=$(printf "${mem_diff_sign}%.2f" $(echo "$mem_diff_product_cmd" | bc -l))
else
disp_mem_diff_geomean="--"
fi
printf "āā%sāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¼āāāāāāāāāāāāāā¤\n"\
$(head -c $longest_filename < /dev/zero | sed -e 's/\x0/ā/g')
printf "ā %${longest_filename}s ā ā %10sx ā %10sx ā\n"\
"Geo. Mean" "$disp_time_diff_geomean" "$disp_mem_diff_geomean"
printf "āā%sāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāā“āāāāāāāāāāāāāā\n"\
$(head -c $longest_filename < /dev/zero | sed -e 's/\x0/ā/g')
Limitations
- Uses
prlimit
, which is Linux specific - Errors are not handled well (crash is treated as OOM, for example)
- Some corner cases missed
- Only one run with each set of args is performed
- Progress is not printed (process viewer does the job for now)