Skip to content

perf(visitor): reuse arguments - faster schema validation - #297

Merged
Cito merged 1 commit into
graphql-python:mainfrom
dexhunter:perf/parallel-visitor-arguments
Nov 5, 2026
Merged

Cito merged 1 commit into
graphql-python:mainfrom
dexhunter:perf/parallel-visitor-arguments

Conversation

@dexhunter

Copy link
Copy Markdown
Contributor

TL;DR

  • Reduce repeated work in ParallelVisitor enter and leave callbacks.
  • Reuse callback arguments and visitor indices; skip action checks when callbacks return None.
  • The repository's empty parallel visitor benchmark takes 195.20 ms → 94.48 ms, using 51.6% less time.
  • Tests: PYTHONPATH=src python -m pytest tests -q passed 3,613 tests; 18 skipped.

Refs #296

What & Why

Each node visit rebuilt arguments and index pairs for every callback.
Build the argument tuple once per node callback and store indices with the cached handlers.
Check action values only when a callback returns a value.
The same handlers receive the same arguments in the same order.

Validation

Situation Before After Change
GitHub schema, 25 empty parallel visitors 195.20 ms 94.48 ms 51.6% less time
GitHub schema validation 46.78 ms 44.11 ms 5.7% less time
Introspection query validation 2.552 ms 2.530 ms No measurable change (0.9% less)
Invalid query validation 21.45 ms 21.34 ms No measurable change (0.5% less)
GitHub schema, one empty visitor 14.28 ms 14.42 ms No measurable change (1.0% more)
Large query pickle decoding 7.165 ms 6.961 ms No measurable change (2.9% less)

These are median times from at least 50 rounds on CPython 3.12.13, Linux x86-64, and an AMD EPYC 9R14.
Both versions ran sequentially on the same isolated core.
Differences below 3% are treated as measurement variation.
The largest gain comes from empty callbacks; practical validation gains depend on the query and rules.
Single-visitor traversal and pickle decoding do not use ParallelVisitor.

Run on the base commit and this change:

PYTHONPATH=src python -m pytest tests/benchmarks/test_visit.py tests/benchmarks/test_validate_gql.py tests/benchmarks/test_validate_invalid_gql.py tests/benchmarks/test_validate_sdl.py tests/benchmarks/test_serialization.py::test_pickle_large_query_decode --benchmark-enable --benchmark-min-rounds=50
python -m ruff check src/graphql/language/visitor.py
python -m ruff format --check src/graphql/language/visitor.py
python -m mypy src/graphql/language/visitor.py

Ruff and mypy passed for the changed module.

Why this is safe and easy to maintain

The small change stays in one method and uses ordinary tuples and loops.
It preserves output, edits, skip state, break state, and exception handling.
The existing tests/language/test_visitor.py tests cover traversal arguments, replacements, removals, skips, stops, and parallel visitors.

Scope

This changes Python callback dispatch without adding features, dependencies, or public API changes.

AI assistance

Codex gpt-6.1-sol generated candidates tracked with Weco Observe.
Codex also helped run automated checks and prepare this description.

Weco share link: https://dashboard.weco.ai/share/26ij8orE48QjRUY4w8ufQ9eLDNM9zuin

Reuse callback arguments and cache visitor indices.
Skip action checks for None results while preserving traversal behavior.

Co-Authored-By: Aiden <aiden@weco.ai>
@dexhunter
dexhunter requested a review from Cito as a code owner November 5, 2026 11:38
@codspeed

codspeed Bot commented Nov 5, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 35%

1 warning

⚠️ 3 benchmarks with a significant change ran in a different software environment

Their base and head results were measured with a different OS or toolchain, such as compiler, runtime or library versions, so the changes might come from this difference rather than from the code. Open an affected benchmark to see what changed.

See affected benchmarks

⚡ 3 improved benchmarks
✅ 17 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ test_visit_all_ast_nodes_in_parallel 3.7 s 2 s +90.22%
⚡ test_validate_sdl_document 811 ms 694.3 ms +16.8%
⚡ test_validate_introspection_query 33.1 ms 29.9 ms +10.73%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing dexhunter:perf/parallel-visitor-arguments (8b30931) with main (f9bdf7c)

Open in CodSpeed

@Cito Cito left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! Randomized ParallelVisitor runs gave results identical to main, and I can confirm the speedup: with this and #299 applied, about 48% less time on the empty parallel visitor benchmark and 10% on SDL validation. This will be part of 3.3.2.

@Cito
Cito merged commit 9e5cfb6 into graphql-python:main Nov 5, 2026
11 checks passed
Cito pushed a commit that referenced this pull request Nov 5, 2026
#299)

Thanks! Results match main for queries with and without arguments, including async errors, and with this and #297 applied, introspection execution takes about 13% less time. This will be part of 3.3.2.
@Cito Cito changed the title perf(visitor): reuse arguments -- schema validation 5.7% less time perf(visitor): reuse arguments - faster schema validation Nov 5, 2026
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.

2 participants