Skip to content

Make sliding_window startup linear in the window size - #655

Open
galafis wants to merge 1 commit into
pytoolz:masterfrom
galafis:perf-sliding-window-startup
Open

galafis wants to merge 1 commit into
pytoolz:masterfrom
galafis:perf-sliding-window-startup

Conversation

@galafis

@galafis galafis commented Oct 24, 2026

Copy link
Copy Markdown

sliding_window currently advances each tee from the initial position by its offset. Constructing a window of size n therefore replays 0 + 1 + ... + (n - 1) cached items before returning. This becomes expensive for large rolling windows, even when only the first window is requested.

Copy each preceding offset and advance the copy once instead. The returned zip still operates directly on tee iterators, retaining the iteration-speed benefit discussed in #321. Use the second output of tee: Python 3.9 can reuse the input iterator as its first output.

Tests cover consumption before and after the first yields, infinite inputs, short inputs, a large window, integer-like sizes, zero-size non-consumption, and inputs that are already tee iterators. A startup benchmark is added alongside the existing sliding-window benchmark.

Measured on Windows / CPython 3.12.14, median of 11 one-call repeats for next(sliding_window(n, range(n))):

n Before After Speedup
500 1.002 ms 0.142 ms 7.1x
2000 17.668 ms 0.576 ms 30.7x
8000 267.482 ms 2.267 ms 118.0x

Consuming 100,000 values with windows of 1, 3 and 16 stayed within about 4% of the original timings. At n=8000, tracemalloc peak increased from 1,024,232 to 1,088,384 bytes (about 6%), including construction and the first result. Both implementations retain O(n) iterator/cache space.

Validation on both CPython 3.9.25 and 3.12.14: 272 tests/doctests passed, 2 skipped; both sliding-window benchmarks passed. The source file passes the CI pycodestyle rule set, changed lines pass pycodestyle, and git diff --check is clean.

@Muguett-DBY Muguett-DBY left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checked 3631498. The offset copies remove the repeated cached-item replay while preserving the direct tee iterators in the returned zip.

On Windows / CPython 3.12.14, the six sliding-window test functions and all three docstring examples pass with the exact function extracted in memory. Slice-based oracles agree for 8,415 finite-input cases and 5,599 already-tee cases, including an independent tee witness. Construction, exhaustion and source-error timelines also match the base implementation.

I checked the startup work separately, retaining native tee objects: for n=1024, the base replays 523,776 items through islice/deque, while this version makes 1,023 offset-advance calls. This confirms the removed quadratic replay; it is not a wall-time or memory benchmark.

The second-output choice is justified by CPython 3.9's implementation, which can return its copyable input as the first output. My native 3.12 runtime doesn't exercise that reuse, so I haven't independently rerun the Python 3.9 case. The complete pytest suite, other platforms/interpreters and packaging checks were not run here.

No issue found in this change. AI assistance: OpenAI Codex helped inspect the source and execute these checks; no production incident is claimed.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants