Skip to content

Commit 200150b

Browse files
committed
Merge bitcoin/bitcoin#33313: test/refactor: use test deque to avoid quadratic iteration
75e6984 test/refactor: use test deque to avoid quadratic iteration (Lőrinc) Pull request description: Extracted from bitcoin/bitcoin#33141 (comment). ----- In Python, [list `pop(0)` is linear](https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-queues), so consuming all items in the test results in quadratic iteration. Switching to `collections.deque` with `popleft()` expresses FIFO intent and avoids the O(n^2) path. Behavior is unchanged - for a few hundred items the perf impact is likely negligible. ACKs for top commit: maflcko: lgtm ACK 75e6984 theStack: re-ACK 75e6984 enirox001: reACK 75e6984 w0xlt: reACK bitcoin/bitcoin@75e6984 Tree-SHA512: 290f6aeeb33d8b12b7acbbfede7ce0bef1c831a7ab9efc9c3a08c049986572e289cdece0844db908cf198395f574575ce4073c268033bf6dbaadc3828c96c1d8
2 parents 7e08445 + 75e6984 commit 200150b

File tree

1 file changed

+6
-7
lines changed

1 file changed

+6
-7
lines changed

test/functional/test_runner.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,8 @@ def main():
454454
print("Re-compile with the -DBUILD_DAEMON=ON build option")
455455
sys.exit(1)
456456

457-
# Build list of tests
458-
test_list = []
457+
# Build tests
458+
test_list = deque()
459459
if tests:
460460
# Individual tests have been specified. Run specified tests that exist
461461
# in the ALL_SCRIPTS list. Accept names with or without a .py extension.
@@ -474,7 +474,7 @@ def main():
474474
script = script + ".py" if ".py" not in script else script
475475
matching_scripts = [s for s in ALL_SCRIPTS if s.startswith(script)]
476476
if matching_scripts:
477-
test_list.extend(matching_scripts)
477+
test_list += matching_scripts
478478
else:
479479
print("{}WARNING!{} Test '{}' not found in full test list.".format(BOLD[1], BOLD[0], test))
480480
elif args.extended:
@@ -509,7 +509,7 @@ def remove_tests(exclude_list):
509509
remove_tests([test for test in test_list if test.split('.py')[0] == exclude_test.split('.py')[0]])
510510

511511
if args.filter:
512-
test_list = list(filter(re.compile(args.filter).search, test_list))
512+
test_list = deque(filter(re.compile(args.filter).search, test_list))
513513

514514
if not test_list:
515515
print("No valid test scripts specified. Check that your test is in one "
@@ -726,7 +726,7 @@ def done(self):
726726
def get_next(self):
727727
while len(self.jobs) < self.num_jobs and self.test_list:
728728
# Add tests
729-
test = self.test_list.pop(0)
729+
test = self.test_list.popleft()
730730
portseed = len(self.test_list)
731731
portseed_arg = ["--portseed={}".format(portseed)]
732732
log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
@@ -754,8 +754,7 @@ def proc_wait(task):
754754
]
755755
fut = self.executor.submit(proc_wait, task)
756756
self.jobs[fut] = test
757-
if not self.jobs:
758-
raise IndexError('pop from empty list')
757+
assert self.jobs # Must not be empty here
759758

760759
# Print remaining running jobs when all jobs have been started.
761760
if not self.test_list:

0 commit comments

Comments
 (0)