Skip to content

Commit 7e4f7bd

Browse files
author
Release Manager
committed
gh-36226: Use CPU time for --warn-long Finally address #32981. Using wall time to measure which tests are "long" doesn't make much sense. Wall time is subject to every conflating factor: 1. Faster hardware runs tests in less wall time 2. Faster compiler flags makes code that runs in less wall time 3. A busy machine will run tests slower than an unburdened one 4. Operating system priority can increase or decrease the time taken by a process 5. etc. Ideally, everyone would agree on how long is too long for a test to run. Attaining that ideal is not so easy, but the first step is to switch`--warn-long` away from using wall time and towards using CPU time. A second phase (#33022) will normalize the CPU time. ### Examples #### --warn-long output The user-facing portion of this can be tested with a low `--warn-long` value, ``` $ sage -t --warn-long=1 src/sage/rings/qqbar.py ... Doctesting 1 file. sage -t --warn-long --random- seed=309742757431591512726158538133919135994 src/sage/rings/qqbar.py ********************************************************************** File "src/sage/rings/qqbar.py", line 511, in sage.rings.qqbar Warning, slow doctest: z, = [r for r in p4.roots(QQbar, False) if r in ival] Test ran for 1.51s cpu, 1.51s wall Check ran for 0.00s cpu, 0.00s wall ********************************************************************** File "src/sage/rings/qqbar.py", line 524, in sage.rings.qqbar Warning, slow doctest: z2 = QQbar.polynomial_root(p4, ival) Test ran for 1.07s cpu, 1.07s wall Check ran for 0.00s cpu, 0.00s wall ********************************************************************** File "src/sage/rings/qqbar.py", line 808, in sage.rings.qqbar.AlgebraicField_common._factor_multivariate_polynomial Warning, slow doctest: F = QQbar._factor_multivariate_polynomial(p) Test ran for 2.38s cpu, 2.42s wall Check ran for 0.00s cpu, 0.00s wall ... ``` #### pexpect accounting Two different methods are used to account for the CPU time, one for linux, and one for everything else. This GAP command uses pexpect and should chew up a good bit of CPU time: ``` sage: from sage.doctest.util import Timer sage: t = Timer() sage: _ = t.start(); t.stop() # no CPU time used between start/stop {'cputime': 0.0, 'walltime': 7.605552673339844e-05} sage: _ = t.start(); gap.eval('a:=List([0..10000],i->WordAlp("a",i));; IsSortedList(a);'); t.stop() # lots of CPU time used 'true' {'cputime': 30.089999999999996, 'walltime': 36.292088747024536} ``` URL: #36226 Reported by: Michael Orlitzky Reviewer(s): Gonzalo Tornaría, John H. Palmieri, Michael Orlitzky, Tobias Diez
2 parents 7726cd9 + efa14c3 commit 7e4f7bd

File tree

12 files changed

+340
-102
lines changed

12 files changed

+340
-102
lines changed

pkgs/sagemath-repl/MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ include sage/misc/sagedoc.py
77
include sage/misc/sage_input.py
88
include sage/misc/sage_eval.py
99

10+
# expect_objects from sage.interfaces.quit is needed to compute the
11+
# CPU time used by each doctest. We include sage.interfaces.cleaner
12+
# because it is conditionally imported by sage.interfaces.quit.
13+
include sage/interfaces/quit.py
14+
include sage/interfaces/cleaner.py
15+
1016
include VERSION.txt
1117

1218
global-exclude all__*.py

pkgs/sagemath-repl/pyproject.toml.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ py-modules = [
4242
]
4343
packages = [
4444
"sage.doctest",
45+
"sage.interfaces",
4546
"sage.repl",
4647
"sage.repl.display",
4748
"sage.repl.ipython_kernel",

src/bin/sage

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ usage_advanced() {
428428
echo " --initial -- only show the first failure per block"
429429
echo " --debug -- drop into PDB after an unexpected error"
430430
echo " --failed -- only test files that failed last test"
431-
echo " --warn-long [timeout] -- warning if doctest is slow"
431+
echo " --warn-long [timeout] -- warn if tests take too much CPU time"
432432
echo " --only-errors -- only output failures, not successes"
433433
echo " --gc=GC -- control garbarge collection (ALWAYS:"
434434
echo " collect garbage before every test; NEVER:"

src/doc/en/developer/doctesting.rst

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -745,10 +745,10 @@ In order to run the long tests as well, do the following::
745745
cpu time: 25.2 seconds
746746
cumulative wall time: 34.7 seconds
747747

748-
To find tests that take longer than the allowed time use the
749-
``--warn-long`` flag. Without any options it will cause tests to
750-
print a warning if they take longer than 1.0 second. Note that this is
751-
a warning, not an error::
748+
To find tests that take longer than a specified amount of CPU time,
749+
use the ``--warn-long`` flag. Without any options, it will cause a
750+
warning to be printed if any tests take longer than one
751+
cpu-second. Note that this is a warning, not an error::
752752

753753
[roed@localhost sage]$ ./sage -t --warn-long src/sage/rings/factorint.pyx
754754
Running doctests with ID 2012-07-14-03-27-03-2c952ac1.
@@ -758,22 +758,22 @@ a warning, not an error::
758758
File "src/sage/rings/factorint.pyx", line 125, in sage.rings.factorint.base_exponent
759759
Failed example:
760760
base_exponent(-4)
761-
Test ran for 4.09 s
761+
Test ran for 4.09 cpu seconds
762762
**********************************************************************
763763
File "src/sage/rings/factorint.pyx", line 153, in sage.rings.factorint.factor_aurifeuillian
764764
Failed example:
765765
fa(2^6+1)
766-
Test ran for 2.22 s
766+
Test ran for 2.22 cpu seconds
767767
**********************************************************************
768768
File "src/sage/rings/factorint.pyx", line 155, in sage.rings.factorint.factor_aurifeuillian
769769
Failed example:
770770
fa(2^58+1)
771-
Test ran for 2.22 s
771+
Test ran for 2.22 cpu seconds
772772
**********************************************************************
773773
File "src/sage/rings/factorint.pyx", line 163, in sage.rings.factorint.factor_aurifeuillian
774774
Failed example:
775775
fa(2^4+1)
776-
Test ran for 2.25 s
776+
Test ran for 2.25 cpu seconds
777777
**********************************************************************
778778
----------------------------------------------------------------------
779779
All tests passed!
@@ -792,12 +792,12 @@ You can also pass in an explicit amount of time::
792792
File "tests.py", line 240, in sage.rings.tests.test_random_elements
793793
Failed example:
794794
sage.rings.tests.test_random_elements(trials=1000) # long time (5 seconds)
795-
Test ran for 13.36 s
795+
Test ran for 13.36 cpu seconds
796796
**********************************************************************
797797
File "tests.py", line 283, in sage.rings.tests.test_random_arith
798798
Failed example:
799799
sage.rings.tests.test_random_arith(trials=1000) # long time (5 seconds?)
800-
Test ran for 12.42 s
800+
Test ran for 12.42 cpu seconds
801801
**********************************************************************
802802
----------------------------------------------------------------------
803803
All tests passed!

src/sage/doctest/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def _make_parser():
6464
help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)")
6565
parser.add_argument("--warn-long", dest="warn_long", nargs='?',
6666
type=float, default=-1.0, const=1.0, metavar="SECONDS",
67-
help="warn if tests take more time than SECONDS")
67+
help="warn if tests take more CPU time than SECONDS")
6868
# By default, include all tests marked 'sagemath_doc_html' -- see
6969
# https://github.com/sagemath/sage/issues/25345 and
7070
# https://github.com/sagemath/sage/issues/26110:

src/sage/doctest/control.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -588,36 +588,38 @@ def __del__(self):
588588

589589
def _init_warn_long(self):
590590
"""
591-
Pick a suitable default for the ``--warn-long`` option if not specified.
591+
Pick a suitable default for the ``--warn-long`` option if not
592+
specified.
592593
593594
It is desirable to have all tests (even ``# long`` ones)
594595
finish in less than about 5 seconds. Longer tests typically
595596
don't add coverage, they just make testing slow.
596597
597-
The default used here is 60 seconds on a modern computer. It
598-
should eventually be lowered to 5 seconds, but its best to
599-
boil the frog slowly.
598+
The default used here is 5 seconds, unless `--long` was used,
599+
in which case it is 30 seconds.
600600
601-
The stored timings are used to adjust this limit according to
602-
the machine running the tests.
601+
TESTS:
603602
604-
EXAMPLES::
603+
Ensure that the user's command-line options are not changed::
605604
606-
sage: from sage.doctest.control import DocTestDefaults, DocTestController
605+
sage: from sage.doctest.control import (DocTestDefaults,
606+
....: DocTestController)
607607
sage: DC = DocTestController(DocTestDefaults(), [])
608608
sage: DC.options.warn_long = 5.0
609609
sage: DC._init_warn_long()
610-
sage: DC.options.warn_long # existing command-line options are not changed
610+
sage: DC.options.warn_long
611611
5.00000000000000
612612
"""
613613
# default is -1.0
614614
if self.options.warn_long >= 0: # Specified on the command line
615615
return
616-
try:
617-
self.options.warn_long = 60.0 * self.second_on_modern_computer()
618-
except RuntimeError as err:
619-
if not sage.doctest.DOCTEST_MODE:
620-
print(err) # No usable timing information
616+
617+
# The developer's guide says that even a "long time" test
618+
# should ideally complete in under five seconds, so we're
619+
# being rather generous here.
620+
self.options.warn_long = 5.0
621+
if self.options.long:
622+
self.options.warn_long = 30.0
621623

622624
def second_on_modern_computer(self):
623625
"""
@@ -637,6 +639,9 @@ def second_on_modern_computer(self):
637639
sage: DC = DocTestController(DocTestDefaults(), [])
638640
sage: DC.second_on_modern_computer() # not tested
639641
"""
642+
from sage.misc.superseded import deprecation
643+
deprecation(32981, "this method is no longer used by the sage library and will eventually be removed")
644+
640645
if len(self.stats) == 0:
641646
raise RuntimeError('no stored timings available')
642647
success = []
@@ -1152,7 +1157,7 @@ def run_doctests(self):
11521157
sage: DC.run_doctests()
11531158
Doctesting 1 file.
11541159
sage -t .../sage/rings/homset.py
1155-
[... tests, ... s]
1160+
[... tests, ...s wall]
11561161
----------------------------------------------------------------------
11571162
All tests passed!
11581163
----------------------------------------------------------------------
@@ -1229,7 +1234,7 @@ def cleanup(self, final=True):
12291234
Running doctests with ID ...
12301235
Doctesting 1 file.
12311236
sage -t .../rings/all.py
1232-
[... tests, ... s]
1237+
[... tests, ...s wall]
12331238
----------------------------------------------------------------------
12341239
All tests passed!
12351240
----------------------------------------------------------------------
@@ -1433,7 +1438,7 @@ def run(self):
14331438
Running doctests with ID ...
14341439
Doctesting 1 file.
14351440
sage -t .../sage/sets/non_negative_integers.py
1436-
[... tests, ... s]
1441+
[... tests, ...s wall]
14371442
----------------------------------------------------------------------
14381443
All tests passed!
14391444
----------------------------------------------------------------------
@@ -1457,7 +1462,7 @@ def run(self):
14571462
Features to be detected: ...
14581463
Doctesting 1 file.
14591464
sage -t ....py
1460-
[0 tests, ... s]
1465+
[0 tests, ...s wall]
14611466
----------------------------------------------------------------------
14621467
All tests passed!
14631468
----------------------------------------------------------------------
@@ -1483,7 +1488,7 @@ def run(self):
14831488
Features to be detected: ...
14841489
Doctesting 1 file.
14851490
sage -t ....py
1486-
[4 tests, ... s]
1491+
[4 tests, ...s wall]
14871492
----------------------------------------------------------------------
14881493
All tests passed!
14891494
----------------------------------------------------------------------
@@ -1501,7 +1506,7 @@ def run(self):
15011506
Features to be detected: ...
15021507
Doctesting 1 file.
15031508
sage -t ....py
1504-
[4 tests, ... s]
1509+
[4 tests, ...s wall]
15051510
----------------------------------------------------------------------
15061511
All tests passed!
15071512
----------------------------------------------------------------------
@@ -1519,7 +1524,7 @@ def run(self):
15191524
Features to be detected: ...
15201525
Doctesting 1 file.
15211526
sage -t ....py
1522-
[4 tests, ... s]
1527+
[4 tests, ...s wall]
15231528
----------------------------------------------------------------------
15241529
All tests passed!
15251530
----------------------------------------------------------------------
@@ -1627,7 +1632,7 @@ def run_doctests(module, options=None):
16271632
Running doctests with ID ...
16281633
Doctesting 1 file.
16291634
sage -t .../sage/rings/all.py
1630-
[... tests, ... s]
1635+
[... tests, ...s wall]
16311636
----------------------------------------------------------------------
16321637
All tests passed!
16331638
----------------------------------------------------------------------

0 commit comments

Comments
 (0)