perf-test: initial version of memory test infrastructure.

The idea is run some code under valgrind --leak-check=full and report
a summary, leaving the user to peruse the log file if they want.

We go to some lengths to preserve the log files from accidental
overwriting; the full corpus takes about 3 hours to run under valgrind
on my machine.

The naming of the log directories may be slightly controversial; in
the unlikely event of two runs in less than a second, the log will be
overwritten. A previous version with mktemp+timestamp was dismissed as
overkill; just mktemp alone does not sort nicely.

One new test is included, to check notmuch new for memory leaks.
This commit is contained in:
David Bremner 2012-12-16 08:33:17 -04:00
parent 047cd7a595
commit 098ef4af4d
5 changed files with 126 additions and 38 deletions

View file

@ -1,3 +1,4 @@
tmp.*/ tmp.*/
log.*/
corpus/ corpus/
notmuch.cache.*/ notmuch.cache.*/

15
performance-test/M00-new Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
test_description='notmuch new'
. ./perf-test-lib.sh
# ensure initial 'notmuch new' is run by memory_start
uncache_database
memory_start
# run 'notmuch new' a second time, to test different code paths
memory_run "notmuch new" "notmuch new"
memory_done

View file

@ -4,14 +4,24 @@ dir := performance-test
include $(dir)/version.sh include $(dir)/version.sh
TIME_TEST_SCRIPT := ${dir}/notmuch-time-test
MEMORY_TEST_SCRIPT := ${dir}/notmuch-memory-test
CORPUS_NAME := notmuch-email-corpus-$(PERFTEST_VERSION).tar.xz CORPUS_NAME := notmuch-email-corpus-$(PERFTEST_VERSION).tar.xz
TXZFILE := ${dir}/download/${CORPUS_NAME} TXZFILE := ${dir}/download/${CORPUS_NAME}
SIGFILE := ${TXZFILE}.asc SIGFILE := ${TXZFILE}.asc
TEST_SCRIPT := ${dir}/notmuch-perf-test
DEFAULT_URL := http://notmuchmail.org/releases/${CORPUS_NAME} DEFAULT_URL := http://notmuchmail.org/releases/${CORPUS_NAME}
perf-test: time-test memory-test
time-test: setup-perf-test all time-test: setup-perf-test all
$(TEST_SCRIPT) $(OPTIONS) @echo
$(TIME_TEST_SCRIPT) $(OPTIONS)
memory-test: setup-perf-test all
@echo
$(MEMORY_TEST_SCRIPT) $(OPTIONS)
.PHONY: download-corpus setup-perf-test .PHONY: download-corpus setup-perf-test
@ -29,4 +39,4 @@ $(TXZFILE):
download-corpus: download-corpus:
wget -O ${TXZFILE} ${DEFAULT_URL} wget -O ${TXZFILE} ${DEFAULT_URL}
CLEAN := $(CLEAN) $(dir)/tmp.* $(dir)/corpus $(dir)/notmuch.cache.* CLEAN := $(CLEAN) $(dir)/tmp.* $(dir)/log.* $(dir)/corpus $(dir)/notmuch.cache.*

View file

@ -1,3 +1,10 @@
Performance Tests
-----------------
This directory contains two kinds of performance tests: time tests,
and memory tests. The former use gnu time, and the latter use
valgrind.
Pre-requisites Pre-requisites
-------------- --------------
@ -5,9 +12,10 @@ In addition to having notmuch, you need:
- gpg - gpg
- gnu tar - gnu tar
- gnu time - gnu time (for the time tests)
- xz. Some speedup can be gotten by installing "pixz", but this is - xz. Some speedup can be gotten by installing "pixz", but this is
probably only worthwhile if you are debugging the tests. probably only worthwhile if you are debugging the tests.
- valgrind (for the memory tests)
Getting set up to run tests: Getting set up to run tests:
---------------------------- ----------------------------
@ -36,34 +44,47 @@ for a list of mirrors.
Running tests Running tests
------------- -------------
The easiest way to run performance tests is to say "make time-test", (or The easiest way to run performance tests is to say "make perf-test".
simply run the notmuch-time-test script). Either command will run all This will run all time and memory tests. Be aware that the memory
available performance tests. tests are quite time consuming when run on the full corpus, and that
depending on your interests it may be more sensible to run "make
Alternately, you can run a specific subset of tests by simply invoking time-test" or "make memory-test". You can also invoke one of the
one of the executable scripts in this directory, (such as ./basic). scripts notmuch-time-test or notmuch-memory-test or run a more
Each test script supports the following arguments specific subset of tests by simply invoking one of the executable
scripts in this directory, (such as ./T00-new). Each test script
supports the following arguments
--small / --medium / --large Choose corpus size. --small / --medium / --large Choose corpus size.
--debug Enable debugging. In particular don't delete --debug Enable debugging. In particular don't delete
temporary directories. temporary directories.
When using the make targets, you can pass arguments to all test
scripts by defining the make variable OPTIONS.
Writing tests Writing tests
------------- -------------
Have a look at "T01-dump-restore" for an example. Sourcing Have a look at "T01-dump-restore" for an example time test and
"perf-test-lib.sh" is mandatory. Utility functions include "M00-new" for an example memory test. In both cases sourcing
"perf-test-lib.sh" is mandatory.
- 'add_email_corpus' unpacks a set of messages and adds them to the database. Basics:
- 'cache_database': makes a snapshot of the current database
- 'uncache_database': forces the next 'add_email_corpus' to rebuild the - '(time|memory)_start' unpacks the mail corpus and calls notmuch new if it
database.
- 'time_start' unpacks the mail corpus and calls notmuch new if it
cannot find a cache of the appropriate corpus. cannot find a cache of the appropriate corpus.
- 'time_done' does the cleanup; comment it out or pass --debug to the - '(time|memory)_run' runs the command under time or valgrind. Currently
"memory_run" does not support i/o redirection in the command.
- '(time|memory)_done' does the cleanup; comment it out or pass --debug to the
script to leave the temporary files around. script to leave the temporary files around.
Utility functions include
- 'add_email_corpus' unpacks a set of messages and tags
- 'cache_database': makes a snapshot of the current database
- 'uncache_database': forces the next '(time|memory)_start' to rebuild the
database.
Scripts are run in the order specified in notmuch-perf-test. In the Scripts are run in the order specified in notmuch-perf-test. In the
future this order might be chosen automatically so please follow the future this order might be chosen automatically so please follow the
convention of starting the name with 'T' followed by two digits to convention of starting the name with 'T' or 'M' followed by two digits
specify the order. to specify the order.

View file

@ -89,24 +89,59 @@ add_email_corpus ()
cp -lr $TAG_CORPUS $TMP_DIRECTORY/corpus.tags cp -lr $TAG_CORPUS $TMP_DIRECTORY/corpus.tags
cp -lr $MAIL_CORPUS $MAIL_DIR cp -lr $MAIL_CORPUS $MAIL_DIR
} }
time_start () { notmuch_new_with_cache ()
{
add_email_corpus
print_header
if [ -d $DB_CACHE_DIR ]; then if [ -d $DB_CACHE_DIR ]; then
cp -r $DB_CACHE_DIR ${MAIL_DIR}/.notmuch cp -r $DB_CACHE_DIR ${MAIL_DIR}/.notmuch
else else
time_run 'Initial notmuch new' "notmuch new" "$1" 'Initial notmuch new' "notmuch new"
cache_database cache_database
fi fi
} }
cache_database () { time_start ()
{
add_email_corpus
print_header
notmuch_new_with_cache time_run
}
memory_start ()
{
add_email_corpus
local timestamp=$(date +%Y%m%dT%H%M%S)
log_dir="${TEST_DIRECTORY}/log.$(basename $0)-$corpus_size-${timestamp}"
mkdir -p ${log_dir}
notmuch_new_with_cache memory_run
}
memory_run ()
{
test_count=$(($test_count+1))
log_file=$log_dir/$test_count.log
printf "[ %d ]\t%s\n" $test_count "$1"
valgrind --leak-check=full --log-file="$log_file" $2
awk '/LEAK SUMMARY/,/suppressed/ { sub(/^==[0-9]*==/," "); print }' "$log_file"
echo
}
memory_done ()
{
time_done
}
cache_database ()
{
if [ -d $MAIL_DIR/.notmuch ]; then if [ -d $MAIL_DIR/.notmuch ]; then
cp -r $MAIL_DIR/.notmuch $DB_CACHE_DIR cp -r $MAIL_DIR/.notmuch $DB_CACHE_DIR
else else
@ -114,17 +149,20 @@ cache_database () {
fi fi
} }
uncache_database () { uncache_database ()
{
rm -rf $DB_CACHE_DIR rm -rf $DB_CACHE_DIR
} }
print_header () { print_header ()
printf "[v%4s %6s] Wall(s)\tUsr(s)\tSys(s)\tRes(K)\tIn/Out(512B)\n" \ {
${PERFTEST_VERSION} ${corpus_size} printf "\t\t\tWall(s)\tUsr(s)\tSys(s)\tRes(K)\tIn/Out(512B)\n"
} }
time_run () { time_run ()
{
printf " %-22s" "$1" printf " %-22s" "$1"
test_count=$(($test_count+1))
if test "$verbose" != "t"; then exec 4>test.output 3>&4; fi if test "$verbose" != "t"; then exec 4>test.output 3>&4; fi
if ! eval >&3 "/usr/bin/time -f '%e\t%U\t%S\t%M\t%I/%O' $2" ; then if ! eval >&3 "/usr/bin/time -f '%e\t%U\t%S\t%M\t%I/%O' $2" ; then
test_failure=$(($test_failure + 1)) test_failure=$(($test_failure + 1))
@ -133,7 +171,8 @@ time_run () {
return 0 return 0
} }
time_done () { time_done ()
{
if [ "$test_failure" = "0" ]; then if [ "$test_failure" = "0" ]; then
rm -rf "$remove_tmp" rm -rf "$remove_tmp"
exit 0 exit 0
@ -144,6 +183,8 @@ time_done () {
cd -P "$test" || error "Cannot setup test environment" cd -P "$test" || error "Cannot setup test environment"
test_failure=0 test_failure=0
test_count=0
echo printf "\n%-55s [%s %s]\n" \
echo $(basename "$0"): "Testing ${test_description:-notmuch performance}" "$(basename "$0"): Testing ${test_description:-notmuch performance}" \
"${PERFTEST_VERSION}" "${corpus_size}"