Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Airspeed Velocity for Regression Testing #35046

Draft
wants to merge 21 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d7ff532
A proof of concept of airspeed velocity integration
saraedum Aug 11, 2018
89d4afb
Merge remote-tracking branch 'trac/develop' into HEAD
saraedum Sep 12, 2019
68869ae
Merge branch 'u/saraedum/25262' in 9.5.b1
fchapoton Sep 17, 2021
bf9844b
Merge remote-tracking branch 'trac/public/airspeed_velo' into HEAD
saraedum Feb 9, 2023
8883d8d
Add asv_stats_path option to sage -t
roed314 Feb 9, 2023
beed616
Remove doubled newlines
roed314 Feb 9, 2023
8ffe8a9
Save optional tags on each example
roed314 Feb 9, 2023
e9751af
Better recovery of function names when arguments wrap
roed314 Feb 9, 2023
169fd19
Use sage source
roed314 Feb 9, 2023
d441f2f
Fix some doctests
roed314 Feb 9, 2023
9d43f55
Implement generation of benchmarks from previous doctest run statistics
saraedum Feb 9, 2023
9fb7e1d
Try to fix the mysterious errors
roed314 Feb 10, 2023
024cc9f
Merge branch 'asv' of github.com:saraedum/sage into asv
roed314 Feb 10, 2023
8ea099f
Revert "Try to fix the mysterious errors"
saraedum Feb 10, 2023
e58617b
Read and write from tmpfile to not block queue
saraedum Feb 10, 2023
6645eed
Try to improve name detection
roed314 Feb 10, 2023
129a168
Resolve slowness in benchmark consumption by using a module name that…
saraedum Feb 11, 2023
e1001a8
Merge branch 'asv' of github.com:saraedum/sage into asv
saraedum Feb 11, 2023
ab5414c
Fix slowness when consuming asv results
saraedum Feb 11, 2023
bb762cb
Merge branch 'asv' of github.com:saraedum/sage into asv
saraedum Mar 2, 2023
5bdfec0
Merge branch 'develop' into asv
saraedum Mar 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ Untitled*.ipynb
# GitLab CI generated files
gitlab-build-docker.log

# airspeed velocity
/.asv

# Byte-compiled / optimized / DLL files
__pycache__/
**/__pycache__
Expand Down
12 changes: 12 additions & 0 deletions asv.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"project": "sage",
"project_url": "https://sagemath.org",
"repo": ".",
"plugins": ["sage_asv"],
"environment_type": "sage",
"env_dir": ".asv/env",
"results_dir": ".asv/results",
"html_dir": ".asv/html",
"benchmark_dir": "src/sage/benchmark/"
}
128 changes: 128 additions & 0 deletions src/sage/benchmark/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from sage.all import *
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW: in #35049 (trivial PR moving some existing benchmark files to the same place) I used sage.tests.benchmarks instead of sage.benchmark. No opinion on which is best, but I guess we don't want to keep both.


from sage.doctest.control import DocTestDefaults, DocTestController
from sage.doctest.forker import SageDocTestRunner, DocTestTask
from sage.doctest.parsing import parse_optional_tags

import timeit
import doctest

DEFAULTS = DocTestDefaults()
DEFAULTS.serial = True
DEFAULTS.long = True

PREFIX = 'track__'

def myglob(path, pattern):
# python 2 does not have support for ** in glob patterns
import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, pattern):
matches.append(os.path.join(root, filename))
return matches

class BenchmarkMetaclass(type):
_dir = None

def _run(cls, fname=[SAGE_SRC + '/sage/']):
old_runner = DocTestTask.runner
DocTestTask.runner = BenchmarkRunner
try:
DocTestController(DEFAULTS, fname).run()
finally:
DocTestTask.runner = old_runner

def __dir__(cls):
if cls._dir is None:
cls._dir = set()
BenchmarkRunner._dir = cls._dir
cls._run()
return list(cls._dir)

def __getattr__(cls, name):
if not name.startswith(PREFIX):
raise AttributeError
cls.create_timer(name)
return getattr(cls, name)

def create_timer(cls, name):
try:
type.__getattr__(cls, name)
except AttributeError:
def time_doctest(self):
BenchmarkRunner._selected = name
BenchmarkRunner._time = 0
cls._run(myglob(os.path.join(SAGE_SRC,'sage'), BenchmarkRunner.decode(name)+"*.*"))
return BenchmarkRunner._time

time_doctest.__name__ = name
setattr(cls, name, time_doctest)

class Benchmarks(object):
__metaclass__ = BenchmarkMetaclass

def __getattr__(self, name):
if not name.startswith(PREFIX):
raise AttributeError
type(self).create_timer(name)
return getattr(self, name)

class BenchmarkRunner(SageDocTestRunner):
_selected = None
_dir = set()
_time = None

@classmethod
def encode(cls, prefix, filename, name, digest):
module = os.path.splitext(os.path.basename(filename))[0]
method = name.split('.')[-1]
return PREFIX+module+"__"+method+"__"+digest

@classmethod
def decode(cls, name):
# This is not the full file name but only the bit up to the first _.
# But that's good enough as we later seach for this with a glob pattern
return name.split("__")[1]

def run(self, test, clear_globs=True, *args, **kwargs):
self._do_not_run_tests = True
super(BenchmarkRunner, self).run(test, *args, clear_globs=False, **kwargs)

name = BenchmarkRunner.encode(PREFIX, test.filename, test.name, self.running_doctest_digest.hexdigest())
if name not in BenchmarkRunner._dir:
for example in test.examples:
if isinstance(example, doctest.Example):
if "long time" in parse_optional_tags(example.source):
BenchmarkRunner._dir.add(name)
break

self._do_not_run_tests = False
if type(self)._selected is not None:
if name == type(self)._selected:
pass
else:
self._do_not_run_tests = True
if not self._do_not_run_tests:
super(BenchmarkRunner, self).run(test, *args, clear_globs=clear_globs, **kwargs)
return

def compile_and_execute(self, example, compiler, globs, *args, **kwargs):
if self._do_not_run_tests:
compiler = lambda example: compile("print(%s.encode('utf8'))"%(repr(example.want),), "want.py", "single")
super(BenchmarkRunner, self).compile_and_execute(example, compiler, globs, *args, **kwargs)
else:
compiled = compiler(example)
# TODO: According to https://asv.readthedocs.io/en/latest/writing_benchmarks.html, time.process_time would be better here but it's not available in Python 2
start = timeit.default_timer()
exec(compiled, globs)
end = timeit.default_timer()
type(self)._time += end-start

class BenchmarkRunningRunner(SageDocTestRunner):
def run(self, test, *args, **kwargs):
print(test,BenchmarkRunningRunner._test)
if test == BenchmarkRunningRunner._test:
super(BenchmarkRunningRunner).run(test, *args, **kwargs)
5 changes: 3 additions & 2 deletions src/sage/doctest/forker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2389,8 +2389,9 @@ class DocTestTask():
sage: sorted(results.keys())
['cputime', 'err', 'failures', 'optionals', 'tests', 'walltime', 'walltime_skips']
"""

extra_globals = {}

runner = SageDocTestRunner
"""
Extra objects to place in the global namespace in which tests are run.
Normally this should be empty but there are special cases where it may
Expand Down Expand Up @@ -2467,7 +2468,7 @@ def __call__(self, options, outtmpfile=None, msgfile=None, result_queue=None):
"""
result = None
try:
runner = SageDocTestRunner(
runner = DocTestTask.runner(
SageOutputChecker(),
verbose=options.verbose,
outtmpfile=outtmpfile,
Expand Down