There are a number of tickets in trac about performance regressions in
Sage.  I'm sure there are far more performance regressions which we don't
know about because nobody noticed.

As someone writing library code, it's generally not obvious that one is
about to introduce a performance regression (otherwise you'd probably not do
it).  There have been instances where I've wondered whether a change I was
making might have unintended consequences for performance, but testing all
the ways in which this could manifest is laborious.

Consequently, I've been thinking recently about how to detect performance
regressions automatically.  There are really two parts to the problem:
gathering timing data on the Sage library, and analyzing that data to
determine if regression have occurred (and how serious they are).


Data gathering:

One could modify local/bin/sage-doctest to allow the option of changing each
doctest by wrapping it in a "timeit()" call.  This would then generate a
timing datum for each doctest line.
* these timings vary from run to run (presumably due to differing levels of
load on the machine).  I don't know how to account for this, but usually
it's a fairly small effect (on the order of 10% error).
* if you're testing against a previous version of sage, the doctest
structure will have changed because people wrote more doctests.  And doctest
lines depend on each other: you define variables that are used in later
lines.  So inserting a line could make timings of later lines incomparable
to the exact same line without the inserted line.  We might be able to parse
the lines and check that various objects are actually the same (across
different versions of sage, so this would require either a version-invariant
hash or saving in one version, loading in the other and comparing.  And you
would have to do that for each variable that occurs in the line), but that
seems to be getting too complicated...

Many users will only have one version of sage installed.  And even with
multiple versions, you need somewhere to PUT the data in order to compare to
later.

One way of handling these problems is to create a relational database to put
timings in.  This could also be interesting for benchmarketing purposes: we
could have timings on various machines, and we highlight performance
improvements, in addition to watching for performance regressions.

So, here's a first draft for a database schema to put timing data into.
I've included a description of each table, along with a description of
columns I thought were non-obvious.  I'm definitely interested in
suggestsion for improving this schema.

Table: Hosts
# computer information; including identifying data to determine when running
on same host
col: id
col: identifying_data # some way to uniquely identify the computer on which
a test is being run. Presumably the output of some unix function, but I
don't know what.

Table: Sage_version
# a table giving each existing version of Sage an id
# the ids for official sage releases should be consistent across databases;
# users can also create their own temporary versions which use a different
block of id numbers.
# when a new version is created, the Files, Doctest_blocks and Doctest_lines
tables are updated
col: id
col: version_name # string defining the version
col: prev_version_id # versions should be totally ordered.  Since we want to
reserve some id space for official sage versions and other space for users,
this can't be done by just the numerical id.

Table: Files
# a table for all the files in sage that have doctests
col: id
col: filename # e.g. "sage/rings/integer.pyx"
# we might also want some other data, like when the file was added (or
deleted), whether it's a python vs cython file….

Table: Doctest_blocks
# a table for the triple-quote-encased strings that hold doctests
# theoretically, these could be smaller groupings within such a larger
block.  But while it's generally true that doctest blocks can be executed
independently of each other, it's hard to identify subsets of the blocks
that are independent of each other.
col: id
col: name # usually the name of the function, class or file to which this
doctest belongs
col: version_id # sage version in which this block lies
col: file_id # sage file in which this block lies
col: prev_id # doctest block id for analogous block in previous version of
sage.  -1 indicates nonexistent

Table: Doctest_lines
# a table for lines beginning with "sage:" that are executed by the doctest
framework.
# lines that occur many times in the Sage library (e.g. "R.<x> = QQ[]") have

col: id
col: code # e.g. "R.<x> = QQ[]"
# could theoretically support storing equivalent lines of code, such as
"R.<x> = PolynomialRing(QQ)"

Table: Block_line_incidences
# a table to record the many-to-many relation between doctest_blocks and
doctest_lines
col: id
col: block_id
col: line_id
col: line_number # which line in the block this is, e.g. 1 (or 0?) if this
is the first doctest line in the block

Table: Regression_runs
# a table to record times (possibly on different machines) where data was
entered into this database from a regression-test-run
col: id
col: host_id # the computer on which this was run
col: version_id # the sage version
col: start_time # the time this run was started
col: duration # how long this run lasted (would end_time be more useful?)
# doesn't necessarily have to be a run through the whole library; could only
include a subset of the files.
# could include other info, like loading, though I don't know how to measure
this…

Table: Timing_data
# A table to record the timing data we're actually interested in
col: id
col: bli_id # the id for a block_line_incidence
col: run_id # the regression run that this measurement was a part of
col: cpu_time # the amount of time this took to run, as measured through
timeit
# presumably we require that the two version_ids you can get tracing up
through the bli and the run are the same version.


There are also questions of how this would be distributed.  Presumably the
data part wouldn't come standard with sage.  Maybe an optional spkg?  Of
course, you're mainly interested in comparing DIFFERENT versions of sage on
the SAME computer, which doesn't really fit with how we normally distribute
code and data.


Analysis:
Once we have the database set up and have gathered some data, you can do all
kinds of things with it.  I'm most interested in how to find speed
regressions, and I can certainly imagine writing code to do so.  You have
data from a previous version of sage (or your own, before you applied some
patch(es)) and you run a regression test with "sage -rt" or something; this
generates the same kind of timing data and then you look for slowdowns,
either absolute or relative to the average ratio of your current run to the
previous run for a given line of code.  It can then print out a sorted list
of lines with slowdowns, or ones above a certain threshold, or...

I'd like to hear more ideas on how to improve all of this.
David

-- 
To post to this group, send an email to sage-devel@googlegroups.com
To unsubscribe from this group, send an email to 
sage-devel+unsubscr...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/sage-devel
URL: http://www.sagemath.org

Reply via email to