Skip to content

perf: Skip redundant __init__ assignments and remove dead attributes in ResponseFuture (-32ns/call - 4.7% improvement for the common case, +43ns (+4.5%) for the non-common case, - code cleanup also) - #806

Draft
mykaul wants to merge 2 commits into
scylladb:masterfrom
mykaul:perf/response-future-init-cleanup

Conversation

@mykaul

@mykaul mykaul commented Apr 7, 2026

Copy link
Copy Markdown

Summary

  • Remove 3 dead class attributes from ResponseFuture:
    • default_timeout — belongs to Session, never used in RF
    • _profile_manager — belongs to Session, never used in RF
    • _warned_timeout — never read or written anywhere in the codebase
  • Skip 4 redundant STORE_ATTR operations in __init__ when parameters are None (matching the class-level default): _metrics, prepared_statement, _host, _continuous_paging_state
  • Move prepared_statement and _continuous_paging_state class defaults (previously only set in __init__) to class level so the conditional skip works correctly

Thread Safety

All skipped attributes have class-level None defaults and are only set during __init__ or by the owning thread after construction. No lazy initialization of shared mutable state is introduced — the thread-safety invariants are preserved.

Benchmark

ResponseFuture.__init__ micro-benchmark, min() of 7 × 200k iterations:

Scenario Before After Δ
Common case (all 4 params = None) 670 ns/call 638 ns/call -32 ns/call (4.7%)
Non-None case (metrics + prepared_statement + host set) 957 ns/call 1001 ns/call +43 ns/call (+4.5%)

The common case (simple queries without metrics/prepared statements/host pinning) is the dominant path. The non-None case overhead is from the added if checks, but these params are rarely all non-None simultaneously.

Additional optimization: isinstance dispatch order in _create_response_future

This PR also reorders the isinstance chain in Session._create_response_future (cassandra/cluster.py) so BoundStatement is checked before SimpleStatement. BoundStatement and SimpleStatement are sibling classes under Statement (no subclass relationship), so the reorder changes dispatch order only — no behavioral change.

For prepared-statement workloads (the perf-critical case, since BoundStatement is produced by PreparedStatement.bind()), BoundStatement is the most common query type reaching this dispatch, so checking it first avoids one wasted isinstance() call per query on the hot path.

A dedicated micro-benchmark, benchmarks/micro/bench_isinstance_dispatch.py, simulates a representative workload mix (80% BoundStatement, 15% SimpleStatement, 5% other) and measures dispatch order in isolation:

SimpleStatement first: 32.8 ns/dispatch
BoundStatement first:  23.2 ns/dispatch
Speedup: ~1.4-1.7x (~10-15 ns/dispatch saved)

Run it yourself with python benchmarks/micro/bench_isinstance_dispatch.py from the repository root.

Tests

All 645 unit tests pass (43 skipped), matching the origin/master baseline.

@mykaul mykaul changed the title perf: Skip redundant __init__ assignments and remove dead attributes in ResponseFuture perf: Skip redundant __init__ assignments and remove dead attributes in ResponseFuture (-32ns/call - 4.7% improvement for the common case, +43ns (+4.5%) for the non-common case, - code cleanup also) Apr 7, 2026
@mykaul

mykaul commented Apr 10, 2026

Copy link
Copy Markdown
Author

Follow-up commit: reorder isinstance chain in _create_response_future

Commit: 40f45ecf5perf: reorder isinstance chain to check BoundStatement first in _create_response_future

Change

Swapped the isinstance check order so BoundStatement is checked before SimpleStatement in the _create_response_future dispatch chain. For prepared-statement workloads (the perf-critical case), this saves one wasted isinstance() call per dispatch.

BoundStatement and SimpleStatement are sibling classes under Statement — no subclass relationship — so reorder is safe with no behavioral change.

Benchmark results (benchmarks/bench_isinstance_dispatch.py)

Simulated workload mix: 80% BoundStatement, 15% SimpleStatement, 5% other:

Python 3.14.3
SimpleStatement first: 32.8 ns/dispatch
BoundStatement first:  23.2 ns/dispatch
Speedup: ~1.4–1.7x (~10–15 ns/dispatch saved)

Tests

607 unit tests passed, 0 failures.

@mykaul
mykaul force-pushed the perf/response-future-init-cleanup branch 3 times, most recently from 40f45ec to 7506833 Compare April 11, 2026 16:28
@mykaul
mykaul force-pushed the perf/response-future-init-cleanup branch from 7506833 to 4b72049 Compare June 29, 2026 21:24
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 523cdfc0-f307-4864-b0c8-1aaee957d60a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

Copilot AI review requested due to automatic review settings July 29, 2026 17:31
@mykaul
mykaul force-pushed the perf/response-future-init-cleanup branch from 4b72049 to 59ac073 Compare July 29, 2026 17:31

Copilot AI 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.

Pull request overview

Optimizes response-future construction and statement dispatch while removing unused attributes.

Changes:

  • Avoids redundant ResponseFuture assignments for None values.
  • Removes dead attributes and adds class-level defaults.
  • Prioritizes BoundStatement dispatch and adds a micro-benchmark.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
cassandra/cluster.py Optimizes dispatch and ResponseFuture initialization.
benchmarks/micro/bench_isinstance_dispatch.py Benchmarks statement dispatch ordering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread benchmarks/micro/bench_isinstance_dispatch.py Outdated
Comment thread cassandra/cluster.py
…in ResponseFuture

- Remove 3 dead class attributes (default_timeout, _profile_manager,
  _warned_timeout) that were never read or written on ResponseFuture
- Add prepared_statement and _continuous_paging_state as class-level
  defaults (both None), skip __init__ assignment when parameter is None
- Conditionalize _metrics and _host assignments: only set when non-None
- Saves 4 STORE_ATTR operations per query on the common path (simple
  statements, no metrics, no host targeting, no continuous paging)
@mykaul
mykaul force-pushed the perf/response-future-init-cleanup branch from 59ac073 to c24db61 Compare July 30, 2026 07:00
Copilot AI review requested due to automatic review settings July 30, 2026 07:00

Copilot AI 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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

…te_response_future

For prepared-statement workloads (the perf-critical case), BoundStatement
is the most common query type reaching _create_response_future. Checking
it before SimpleStatement saves one wasted isinstance() call per dispatch.

Benchmark (80% BoundStatement, 15% SimpleStatement, 5% other):
  SimpleStatement first: 32.8 ns/dispatch
  BoundStatement first:  23.2 ns/dispatch
  Speedup: ~1.4-1.7x (~10-15 ns/dispatch saved)

Also fix an unrelated, pre-existing MSVC compile failure surfaced by this
PR's Windows wheel-build CI job: cassandra/c_shard_info.pyx cast biased_token
to `__uint128_t` to get the high 64 bits of a 64x32-bit product. That type is
a GCC/Clang compiler-builtin extension with no MSVC equivalent (MSVC has no
128-bit integer type at all), so MSVC's compiler treated it as an undeclared
identifier and failed with cascading syntax errors (C2065/C2146/C2059) in the
generated c_shard_info.c. Replaced it with a portable 64-bit-only
multiply-high decomposition (split into 32-bit halves, matching the existing
pure-Python fallback in cassandra/shard_info.py) that compiles identically on
GCC, Clang, and MSVC and is numerically identical to the previous computation
(verified against millions of random inputs and against the actual compiled
extension).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 09:58
@mykaul
mykaul force-pushed the perf/response-future-init-cleanup branch from c24db61 to 85808f0 Compare July 30, 2026 09:58

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

cassandra/c_shard_info.pyx:42

  • This portability rewrite is unrelated to the ResponseFuture/dispatch optimization described by the PR, and the summary does not mention that shard-routing arithmetic is being changed. Please either document and justify this additional scope (including the Windows-build motivation) or move it to a separate PR so reviewers and release notes do not miss a correctness-sensitive routing change.
        # Compute (biased_token * shards_count) >> 64, i.e. the high 64 bits of the
        # 64x32-bit product, using only 64-bit arithmetic. This used to rely on the
        # GCC/Clang-only __uint128_t extension type, which MSVC does not support at
        # all (no 128-bit integer type), causing a compile error on Windows builds.

cassandra/c_shard_info.pyx:49

  • The new multiply-high implementation has no boundary/equivalence coverage. The existing shard-aware test checks only five application tokens, so carry boundaries and token extrema could regress shard routing unnoticed. Add a parametrized test that imports the C implementation directly and compares it with _ShardingInfo for INT64_MIN, INT64_MAX, low-half carry boundaries, multiple shard counts, and sharding_ignore_msb values.
        cdef uint64_t low_product = (biased_token & <uint64_t>UINT32_MAX) * shards_count
        cdef uint64_t carry = low_product >> 32
        cdef uint64_t mid = (biased_token >> 32) * shards_count + carry
        cdef int shardId = <int>(mid >> 32);

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