mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
[3.13] gh-142145: Avoid timing measurements in quadratic behavior test (gh-143105) (#143140)
Count the number of Element attribute accesses as a proxy for work done.
With double the amount of work, a ratio of 2.0 indicates linear scaling
and 4.0 quadratic scaling. Use 3.2 as an intermediate threshold.
(cherry picked from commit 57937a8e5e)
Co-authored-by: Sam Gross <colesbury@gmail.com>
This commit is contained in:
parent
e20863f223
commit
e91c11449c
1 changed files with 28 additions and 16 deletions
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import copy
|
||||
import pickle
|
||||
import time
|
||||
import io
|
||||
from test import support
|
||||
import unittest
|
||||
|
|
@ -178,23 +177,36 @@ def testAppendChild(self):
|
|||
def testAppendChildNoQuadraticComplexity(self):
|
||||
impl = getDOMImplementation()
|
||||
|
||||
newdoc = impl.createDocument(None, "some_tag", None)
|
||||
top_element = newdoc.documentElement
|
||||
children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
|
||||
element = top_element
|
||||
def work(n):
|
||||
doc = impl.createDocument(None, "some_tag", None)
|
||||
element = doc.documentElement
|
||||
total_calls = 0
|
||||
|
||||
start = time.monotonic()
|
||||
for child in children:
|
||||
element.appendChild(child)
|
||||
element = child
|
||||
end = time.monotonic()
|
||||
# Count attribute accesses as a proxy for work done
|
||||
def getattribute_counter(self, attr):
|
||||
nonlocal total_calls
|
||||
total_calls += 1
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
# This example used to take at least 30 seconds.
|
||||
# Conservative assertion due to the wide variety of systems and
|
||||
# build configs timing based tests wind up run under.
|
||||
# A --with-address-sanitizer --with-pydebug build on a rpi5 still
|
||||
# completes this loop in <0.5 seconds.
|
||||
self.assertLess(end - start, 4)
|
||||
with support.swap_attr(Element, "__getattribute__", getattribute_counter):
|
||||
for _ in range(n):
|
||||
child = doc.createElement("child")
|
||||
element.appendChild(child)
|
||||
element = child
|
||||
return total_calls
|
||||
|
||||
# Doubling N should not ~quadruple the work.
|
||||
w1 = work(1024)
|
||||
w2 = work(2048)
|
||||
w3 = work(4096)
|
||||
|
||||
self.assertGreater(w1, 0)
|
||||
r1 = w2 / w1
|
||||
r2 = w3 / w2
|
||||
self.assertLess(
|
||||
max(r1, r2), 3.2,
|
||||
msg=f"Possible quadratic behavior: work={w1,w2,w3} ratios={r1,r2}"
|
||||
)
|
||||
|
||||
def testSetAttributeNodeWithoutOwnerDocument(self):
|
||||
# regression test for gh-142754
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue