diff --git a/test/performance-plot.py b/test/performance-plot.py new file mode 100755 index 00000000..7d51fcc5 --- /dev/null +++ b/test/performance-plot.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import argparse +import os + +import matplotlib.pyplot as plt +import numpy as np + +parser = argparse.ArgumentParser(description='Display performance plots.') +parser.add_argument('output_dir', + help='directory containing the measurement files') + +args = parser.parse_args() + +output_directory = args.output_dir + +plt.axes([0.1, 0.1, 0.6, 0.75]) +plt.xlabel("# database entries") +plt.ylabel("time [s]") +plt.title("timew performance") + +for filename in os.listdir(output_directory): + if filename.endswith(".log"): + cmd = "-".join(filename.split('-')[1:-1]) + try: + x, y = np.loadtxt(os.path.join(output_directory, filename), + delimiter='\t', + usecols=(1, 2), + unpack=True) + plt.plot(x, y, label=cmd, marker=".", linestyle='') + except ValueError as e: + print("Invalid file: {} {}".format(filename, e)) + +plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0., ncol=2) +plt.show() diff --git a/test/performance-test.sh b/test/performance-test.sh new file mode 100755 index 00000000..891c3f58 --- /dev/null +++ b/test/performance-test.sh @@ -0,0 +1,344 @@ +#!/bin/bash + +set -e + +THREE_HOURS_BEFORE=$( date -v "-3H" "+%Y%m%dT%H0000" ) +TWO_HOURS_BEFORE=$( date -v "-2H" "+%Y%m%dT%H0000" ) +ONE_HOUR_BEFORE=$( date -v "-1H" "+%Y%m%dT%H0000" ) + +TIMEW_BIN="${BASH_SOURCE[0]%/*}/../src/timew" + +[[ -x "${TIMEW_BIN}" ]] || exit 1 + +function test_performance_annotate() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} annotate @1 ANNOTATION >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_cancel() +{ + # setup + ${TIMEW_BIN} start "${TWO_HOURS_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} cancel >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_continue() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} continue @1 >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} cancel >/dev/null + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_day() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} day >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_delete() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} delete @1 >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_export() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} export >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_gaps() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} gaps >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_get() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} get dom.active >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_join() +{ + # setup + ${TIMEW_BIN} track "${THREE_HOURS_BEFORE}" - "${TWO_HOURS_BEFORE}" TEST >/dev/null + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} join @1 @2 >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_lengthen() +{ + # setup + ${TIMEW_BIN} track "${THREE_HOURS_BEFORE}" - "${TWO_HOURS_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} lengthen @1 30min >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_month() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} month >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_modify-end() +{ + # setup + ${TIMEW_BIN} track "${THREE_HOURS_BEFORE}" - "${TWO_HOURS_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} modify end @1 "${ONE_HOUR_BEFORE}" >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_modify-start() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} modify start @1 "${THREE_HOURS_BEFORE}" >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_move() +{ + # setup + ${TIMEW_BIN} track "${THREE_HOURS_BEFORE}" - "${TWO_HOURS_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} move @1 "${TWO_HOURS_BEFORE}" >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_resize() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} resize @1 30min >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_shorten() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} shorten @1 30min >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_split() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} split @1 >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 @2 >/dev/null +} + +function test_performance_start() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} start "${TWO_HOURS_BEFORE}" TEST >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} cancel >/dev/null +} + +function test_performance_stop() +{ + # setup + ${TIMEW_BIN} start "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} start "${TWO_HOURS_BEFORE}" TEST >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_summary() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} summary >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_tag() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} tag @1 TAG >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_tags() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} tags >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') +} + +function test_performance_track() +{ + # test + ( ( time -p ( + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_undo() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} undo >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup +} + +function test_performance_untag() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} untag @1 TEST >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +function test_performance_week() +{ + # setup + ${TIMEW_BIN} track "${TWO_HOURS_BEFORE}" - "${ONE_HOUR_BEFORE}" TEST >/dev/null + # test + ( ( time -p ( + ${TIMEW_BIN} week >/dev/null + ) 2>&1 >/dev/null ) | awk '{a[NR]=$2}; END {for(i=1;i<=3;i++){printf "%s\t",a[i]}}') + # cleanup + ${TIMEW_BIN} delete @1 >/dev/null +} + +OUTPUT_DIR="${1-/tmp/timew-performance}" + +export TIMEWARRIORDB=/tmp/timewarriordb +mkdir -p ${TIMEWARRIORDB}/data +rm -f ${TIMEWARRIORDB}/data/*.data +:> ${TIMEWARRIORDB}/timewarrior.cfg + +mkdir -p "${OUTPUT_DIR}" +rm -rf "${OUTPUT_DIR:?}"/* + +TIMEW_COMMANDS="annotate cancel continue day delete export gaps get join lengthen modify-end modify-start month move resize shorten split start stop summary tag tags track undo untag week" + +# Write headers +for timew_cmd in ${TIMEW_COMMANDS} ; do + echo -e "#Performance ${timew_cmd}" > "${OUTPUT_DIR}/timew-${timew_cmd}-performance.log" + echo -e "#TAG\tENTRIES\tREAL\tUSER\tSYS" >> "${OUTPUT_DIR}/timew-${timew_cmd}-performance.log" +done + +for step in $( seq 0 20 ) ; do + YEAR_MONTH=$( date -v "-${step}m" "+%Y-%m" ) + + if [[ "${step}" -gt 0 ]] ; then + year=${YEAR_MONTH%-*} + month=${YEAR_MONTH#*-} + # Increase database by 100 entries + for day in $(seq -w 1 25) ; do + { + echo "inc ${year}${month}${day}T040000Z - ${year}${month}${day}T050000Z # FOO" + echo "inc ${year}${month}${day}T073000Z - ${year}${month}${day}T113000Z # BAR" + echo "inc ${year}${month}${day}T163000Z - ${year}${month}${day}T183000Z # BAZ" + echo "inc ${year}${month}${day}T193000Z - ${year}${month}${day}T203000Z # FOO BAR" + } >> "${TIMEWARRIORDB}/data/${year}-${month}.data" + done + ENTRIES=$( wc -l /tmp/timewarriordb/data/????-??.data | awk '{a=$1} ; END {printf "%s",a}' ) + fi + + echo -en "Testing with ${ENTRIES-0} entries...\r" + + # Run performance tests + for timew_cmd in ${TIMEW_COMMANDS} ; do + { + echo -en "${YEAR_MONTH}\t${ENTRIES-0}\t" + "test_performance_${timew_cmd}" + echo "" + } >> "${OUTPUT_DIR}/timew-${timew_cmd}-performance.log" + done +done