(improvement) cython - cache deserializer instances in find_deserializer and m…#739
Closed
mykaul wants to merge 1 commit into
Closed
(improvement) cython - cache deserializer instances in find_deserializer and m…#739mykaul wants to merge 1 commit into
mykaul wants to merge 1 commit into
Conversation
…ake_deserializers Cache find_deserializer() and make_deserializers() results in Cython cdef dict caches keyed on cqltype objects to avoid repeated class lookups and Deserializer object creation on every result set. Using cqltype objects (not id()) as cache keys holds strong references, preventing GC/id-reuse correctness issues with parameterized types. ## Motivation On every result set, make_deserializers(coltypes) is called from row_parser.pyx:37, which in turn calls find_deserializer() for each column type. These functions perform class name lookups and issubclass() chains, then create fresh Deserializer objects -- all redundant work when the same column types appear repeatedly (which is always the case for prepared statements). ## Benchmark results Benchmarks compare the original code (Before) against the new cached implementation (After). find_deserializer (single type lookup): | Variant | Min | Mean | Median | Ops/sec | |---|---|---|---|---| | Before (original) | 266.0 ns | 305.0 ns | 292.0 ns | 3.3 Mops/s | | After (with cache) | 44.0 ns | 49.0 ns | 47.8 ns | 20.4 Mops/s | make_deserializers (5 types): | Variant | Min | Mean | Median | Ops/sec | |---|---|---|---|---| | Before (original) | 1,976 ns | 2,438 ns | 2,435 ns | 410 Kops/s | | After (with cache) | 74.9 ns | 83.5 ns | 81.7 ns | 12,000 Kops/s | make_deserializers (10 types): | Variant | Min | Mean | Median | Ops/sec | |---|---|---|---|---| | Before (original) | 3,553 ns | 3,812 ns | 3,761 ns | 262 Kops/s | | After (with cache) | 89.7 ns | 105.1 ns | 97.6 ns | 9,511 Kops/s | ## Design notes - Caches are cdef dict (C-level, not accessible from Python) for minimal overhead - Cache keys are the cqltype objects themselves, not id(cqltype) -- holds strong references preventing GC and id() reuse - For prepared statements (the hot path), cache hit rate is effectively 100% - Cache is naturally bounded by the number of distinct cqltype objects in use ## Tests All existing unit tests pass (108 passed, 1 skipped).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ake_deserializers
Cache find_deserializer() and make_deserializers() results in Cython cdef dict caches keyed on cqltype objects to avoid repeated class lookups and Deserializer object creation on every result set.
Using cqltype objects (not id()) as cache keys holds strong references, preventing GC/id-reuse correctness issues with parameterized types.
Motivation
On every result set, make_deserializers(coltypes) is called from row_parser.pyx:37, which in turn calls find_deserializer() for each column type. These functions perform class name lookups and issubclass() chains, then create fresh Deserializer objects -- all redundant work when the same column types appear repeatedly (which is always the case for prepared statements).
Benchmark results
Benchmarks compare the original code (Before) against the new cached implementation (After).
find_deserializer (single type lookup):
make_deserializers (5 types):
make_deserializers (10 types):
Design notes
Tests
All existing unit tests pass (108 passed, 1 skipped).
Pre-review checklist
./docs/source/.Fixes:annotations to PR description.