Skip to content

Commit 04e4ba5

Browse files
committed
perf: inline has_checksumming_support check to avoid classmethod call overhead
Replace ProtocolVersion.has_checksumming_support(protocol_version) calls in encode_message and decode_message with inline integer comparisons using pre-computed module-level constants. This avoids the classmethod dispatch overhead on every encode/decode call. Benchmark: classmethod call: 61.3 ns inline compare: 25.5 ns saving: 35.8 ns/call (2.4x)
1 parent 2b4e88b commit 04e4ba5

2 files changed

Lines changed: 65 additions & 2 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright ScyllaDB, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Micro-benchmark: inline checksumming check vs classmethod call.
17+
18+
Measures the overhead of ProtocolVersion.has_checksumming_support()
19+
classmethod call versus an inline integer comparison on the
20+
encode/decode hot path.
21+
22+
Run:
23+
python benchmarks/bench_checksumming_inline.py
24+
"""
25+
26+
import sys
27+
import timeit
28+
29+
from cassandra import ProtocolVersion
30+
from cassandra.protocol import _CHECKSUMMING_MIN_VERSION, _CHECKSUMMING_MAX_VERSION
31+
32+
33+
def bench():
34+
protocol_version = ProtocolVersion.V4
35+
36+
def via_classmethod():
37+
return ProtocolVersion.has_checksumming_support(protocol_version)
38+
39+
def via_inline():
40+
return _CHECKSUMMING_MIN_VERSION <= protocol_version < _CHECKSUMMING_MAX_VERSION
41+
42+
n = 5_000_000
43+
t_classmethod = timeit.timeit(via_classmethod, number=n)
44+
t_inline = timeit.timeit(via_inline, number=n)
45+
46+
saving_ns = (t_classmethod - t_inline) / n * 1e9
47+
speedup = t_classmethod / t_inline if t_inline > 0 else float('inf')
48+
49+
print(f"=== has_checksumming_support ({n:,} iters) ===")
50+
print(f" classmethod call: {t_classmethod / n * 1e9:.1f} ns")
51+
print(f" inline compare: {t_inline / n * 1e9:.1f} ns")
52+
print(f" saving: {saving_ns:.1f} ns/call ({speedup:.1f}x)")
53+
54+
55+
if __name__ == "__main__":
56+
print(f"Python {sys.version}")
57+
bench()

cassandra/protocol.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ class InternalError(Exception):
6969

7070
_UNSET_VALUE = object()
7171

72+
# Inline constants for has_checksumming_support check, avoiding
73+
# ProtocolVersion.has_checksumming_support() classmethod call overhead
74+
# (~94 ns per call) on the encode/decode hot path.
75+
_CHECKSUMMING_MIN_VERSION = ProtocolVersion.V5
76+
_CHECKSUMMING_MAX_VERSION = ProtocolVersion.DSE_V1
77+
7278

7379
def register_class(cls):
7480
_message_types_by_opcode[cls.opcode] = cls
@@ -1100,7 +1106,7 @@ def encode_message(cls, msg, stream_id, protocol_version, compressor, allow_beta
11001106
buff = io.BytesIO()
11011107

11021108
# With checksumming, the compression is done at the segment frame encoding
1103-
if (compressor and not ProtocolVersion.has_checksumming_support(protocol_version)):
1109+
if (compressor and not (_CHECKSUMMING_MIN_VERSION <= protocol_version < _CHECKSUMMING_MAX_VERSION)):
11041110
if msg.custom_payload:
11051111
write_bytesmap(buff, msg.custom_payload)
11061112
msg.send_body(buff, protocol_version)
@@ -1149,7 +1155,7 @@ def decode_message(cls, protocol_version, protocol_features, user_type_map, stre
11491155
:param decompressor: optional decompression function to inflate the body
11501156
:return: a message decoded from the body and frame attributes
11511157
"""
1152-
if (not ProtocolVersion.has_checksumming_support(protocol_version) and
1158+
if (not (_CHECKSUMMING_MIN_VERSION <= protocol_version < _CHECKSUMMING_MAX_VERSION) and
11531159
flags & COMPRESSED_FLAG):
11541160
if decompressor is None:
11551161
raise RuntimeError("No de-compressor available for compressed frame!")

0 commit comments

Comments
 (0)