#!/bin/sh
|
#
|
# Benchmarking harness for json-c
|
#
|
# Use this to compare the behavior of two different versions of the library,
|
# such as json-c-0.14 release vs master, master vs local changes, etc...
|
#
|
|
set -e
|
|
trap 'echo "FAILED!"' EXIT
|
|
RUNDIR=$(dirname "$0")
|
RUNDIR=$(cd "$RUNDIR" && pwd)
|
|
TOP=$(cd "$RUNDIR/.." && pwd)
|
|
usage()
|
{
|
exitval=$1
|
errmsg=$2
|
if [ $exitval -ne 0 ] ; then
|
exec 1>&2
|
fi
|
if [ ! -z "$errmsg" ] ; then
|
echo "ERROR: $errmsg" 1>&2
|
fi
|
cat <<EOF
|
Usage: $0 [-h] [-v] [--build] [--run] [--compare] ...XAX...
|
EOF
|
|
exit $extival
|
}
|
|
before_arg=
|
after_arg=
|
do_all=1
|
do_build=0
|
do_run=0
|
do_compare=0
|
|
while [ $# -gt 0 ] ; do
|
case "$1" in
|
--before)
|
before_arg=$2
|
shift
|
;;
|
--after)
|
after_arg=$2
|
shift
|
;;
|
--build)
|
do_all=0
|
do_build=1
|
;;
|
--run)
|
do_all=0
|
do_run=1
|
;;
|
--compare)
|
do_all=0
|
do_compare=1
|
;;
|
-h)
|
usage 0 ""
|
;;
|
-v)
|
set -x
|
;;
|
*)
|
usage 1 "Unknown args: $*"
|
;;
|
esac
|
shift
|
done
|
|
WORK="${RUNDIR}/work"
|
mkdir -p "${WORK}"
|
|
DATA="${RUNDIR}/data"
|
mkdir -p "${DATA}"
|
|
for file in citm_catalog.json twitter.json canada.json ; do
|
if [ ! -r "${DATA}/${file}" ] ; then
|
echo "Fetching ${file} from github.com/mloskot/json_benchmark"
|
URL="https://github.com/mloskot/json_benchmark/raw/master/data/${file}"
|
curl -s -L -o "${DATA}/${file}" "$URL"
|
fi
|
done
|
echo
|
|
# Identify "after" commit hash, in order of preference
|
if [ ! -z "$after_arg" -a -d "$after_arg" ] ; then
|
# Use provided directory
|
after_src_dir="$after_arg"
|
after_commit=
|
echo "Using provided directory [$after_arg] as 'after'"
|
else
|
_commit=
|
if [ ! -z "$after_arg" ] ; then
|
# Use provided commit hash
|
_commit=$(git rev-parse --verify "$after_arg")
|
fi
|
if [ ! -z "$_commit" ] ;then
|
after_src_dir= # i.e. current tree
|
after_commit="$_commit"
|
echo "Using provided commit [$after_arg => $_commit] as 'after'"
|
else
|
# Local changes in current working directory
|
# ${cur_branch}
|
after_src_dir=$TOP
|
after_commit=
|
echo "Using local changes in $TOP as 'after'"
|
fi
|
fi
|
|
# Identify "before" commit hash, in order of preference
|
if [ ! -z "$before_arg" -a -d "$before_arg" ] ; then
|
# Use provided directory
|
before_src_dir="$before_arg"
|
before_commit=
|
echo "Using provided directory [$before_arg] as 'before'"
|
else
|
_commit=
|
if [ ! -z "$before_arg" ] ; then
|
# Use provided commit hash
|
_commit=$(git rev-parse --verify "$before_arg")
|
fi
|
if [ ! -z "$_commit" ] ;then
|
before_src_dir= # i.e. current tree
|
before_commit="$_commit"
|
echo "Using provided commit [$before_arg => $_commit] as 'before'"
|
else
|
# Use origin/${cur_branch}, if different from ${after_commit}
|
_cur_branch=$(git rev-parse --abbrev-ref HEAD)
|
_commit=
|
if [ ! -z "${_cur_branch}" ] ; then
|
_commit=$(git rev-parse --verify "origin/${_cur_branch}")
|
echo "Using origin/${_cur_branch} [$_commit] as 'before'"
|
fi
|
if [ "$_commit" = "${after_commit}" ] ; then
|
_commit=
|
fi
|
fi
|
|
if [ ! -z "$_commit" ] ; then
|
before_src_dir= # i.e. current tree
|
before_commit="$_commit"
|
else
|
# Use previous release
|
before_src_dir= # i.e. current tree
|
before_commit="$(git tag | sort | tail -1)"
|
echo "Using previous release [$before_commit] as 'before'"
|
fi
|
fi
|
|
echo
|
|
compile_benchmark()
|
{
|
local bname=$1
|
local src_dir="$2"
|
local src_commit="$3"
|
|
local build_dir="${WORK}/$bname/build"
|
local inst_dir="${WORK}/$bname/install"
|
local bench_dir="${WORK}/$bname/bench"
|
|
echo
|
echo "=========== $bname ==========="
|
echo
|
|
mkdir -p "${build_dir}"
|
mkdir -p "${inst_dir}"
|
mkdir -p "${bench_dir}"
|
|
if [ ! -z "$src_commit" ] ; then
|
# Resolve the short hash, tag or branch name to full hash
|
src_commit=$(git rev-parse $src_commit)
|
fi
|
|
# No src dir specified, clone and checkout $src_commit
|
if [ -z "$src_dir" ] ; then
|
src_dir="${WORK}/$bname/src"
|
echo "=== Using sources in $src_dir"
|
mkdir -p "$src_dir"
|
at_commit=$(git --git-dir="$src_dir/.git" rev-parse HEAD 2> /dev/null || true)
|
echo "at_commit: $at_commit"
|
if [ -z "$at_commit" ] ; then
|
# Assume it's an empty dir
|
git clone -n "$TOP" "$src_dir"
|
fi
|
git -C "$src_dir" --git-dir="$src_dir/.git" checkout "$src_commit"
|
fi
|
# else, use the provided $src_dir
|
|
if [ -e "${src_dir}/CMakeLists.txt" ] ; then
|
cd "${build_dir}"
|
cmake -DCMAKE_INSTALL_PREFIX="${inst_dir}" "${src_dir}"
|
else
|
# Old versions of json-c used automake/autoconf
|
cd "${src_dir}"
|
sh autogen.sh # always run it, configure doesn't always work
|
cd "${build_dir}"
|
"${src_dir}/configure" --prefix="${inst_dir}"
|
fi
|
make all install
|
|
cd "${bench_dir}"
|
cmake -DCMAKE_PREFIX_PATH="${inst_dir}" "${TOP}/apps"
|
make all
|
}
|
|
# XXX TODO: name "after" and "before" uniquely using the dir & commit
|
|
if [ $do_all -ne 0 -o $do_build -ne 0 ] ; then
|
sleep 5 # Wait slightly, to allow the human to read the message
|
# about what exactly we're doing to benchmark.
|
compile_benchmark "after" "${after_src_dir}" "${after_commit}"
|
compile_benchmark "before" "${before_src_dir}" "${before_commit}"
|
fi
|
|
run_benchmark()
|
{
|
local bname=$1
|
local inst_dir="${WORK}/$bname/install"
|
local bench_dir="${WORK}/$bname/bench"
|
|
local INPUT=${DATA}/canada.json
|
|
cd "${bench_dir}"
|
mkdir -p results
|
(time ./json_parse -n "${INPUT}") > results/basic_timing.out 2>&1
|
valgrind --tool=massif --massif-out-file=massif.out ./json_parse -n "${INPUT}"
|
ms_print massif.out > results/ms_print.out
|
heaptrack -o heaptrack_out ./json_parse -n "${INPUT}"
|
heaptrack --analyze heaptrack_out.gz -H histogram.out > results/heaptrack.out
|
awk ' { s=$1; count=$2; ru=(int((s+ 15) / 16)) * 16; wasted = ((ru-s)*count); print s, count, ru-s, wasted; total=total+wasted} END { print "Total: ", total }' histogram.out > results/histogram2.out
|
|
# XXX stamp some info about what was built & run into ./results/.
|
|
echo "DONE with $bname"
|
}
|
|
if [ $do_all -ne 0 -o $do_run -ne 0 ] ; then
|
run_benchmark "after"
|
run_benchmark "before"
|
fi
|
|
if [ $do_compare -ne 0 ] ; then
|
# XXX this needs better analysis
|
cd "${WORK}"
|
diff -udr before/bench/results after/bench/results || true
|
else
|
echo "To compare results, run:"
|
echo "$0 --compare"
|
fi
|
|
trap '' EXIT
|
|
:<<=cut
|
|
Benchmarks to run:
|
|
* Parse JSON strings, of various sizes and characteristics
|
* Flags: STRICT vs. non-STRICT, validate UTF8
|
|
* Serialization time
|
* plain, spaces, pretty
|
|
* json_c_visit tests
|
* JSON pointer tests
|
|
Things to record and compare:
|
|
* Running time
|
* Peak memory usage
|
* Useful bytes vs. overhead for memory allocations
|
* Total number of allocations
|
* Average allocation size
|
* Log of all allocation sizes
|
|
=cut
|