Skip to content

Optimize C extension hot paths#353

Merged
jensens merged 5 commits intozopefoundation:masterfrom
bluedynamics:optimize-c-lookups
Feb 27, 2026
Merged

Optimize C extension hot paths#353
jensens merged 5 commits intozopefoundation:masterfrom
bluedynamics:optimize-c-lookups

Conversation

@jensens
Copy link
Member

@jensens jensens commented Feb 17, 2026

Summary

Four targeted optimizations to the C extension hot paths, reducing
overhead in adapter lookups and interface calls.

Changes

  1. _verify(): in-place generation comparison — Compare registry
    generation counters directly against the stored snapshot instead of
    allocating a temporary tuple via _generations_tuple() on every
    cache-valid lookup. Enables early exit on first mismatch.

  2. _getcache(): PyUnicode_GET_LENGTH instead of PyObject_IsTrue
    Direct struct field access instead of generic truth protocol dispatch.

  3. _lookup/_lookupAll/_subscriptions: PyTuple_CheckExact fast path
    Skip PySequence_Tuple allocation when required is already a tuple
    (the common case from Python callers).

  4. IB__call__: intern _CALL_CUSTOM_ADAPT string + Py_TYPE macro
    Pre-interned static string with PyDict_GetItem instead of
    PyDict_GetItemString (which creates a temporary Python string each
    call). Also replaces direct ob_type access with Py_TYPE() for
    free-threaded Python compatibility.

Benchmark (Python 3.14.0, Linux, best of 3 runs)

Benchmark Before After Change
Cached adapter lookups (lb.lookup(...)) 112 ns 108 ns -4%
IFoo(foo) (call + adapt) 381 ns 348 ns -9%
providedBy(foo) 58 ns 57 ns 0% (not targeted)

@jensens jensens marked this pull request as ready for review February 17, 2026 00:23
Eliminates PyTuple_New + N x PyTuple_SET_ITEM + Py_DECREF on every
cache-valid lookup call.  Also enables early exit on first mismatch.
Direct struct field access avoids the generic truth protocol dispatch
(type slot lookup -> sq_length or nb_bool) for the name parameter.
… a tuple

Use PyTuple_CheckExact fast path to avoid allocating a copy when
the required argument is already a tuple (the common case from
Python callers).
Use a pre-interned static string with PyDict_GetItem instead of
PyDict_GetItemString with a C literal (which creates a temporary
Python string on each call).  Also replace direct ob_type struct
access with the Py_TYPE() macro for free-threaded compatibility.
@jensens jensens requested a review from tseaver February 25, 2026 22:20
Copy link
Member

@tseaver tseaver left a comment

Choose a reason for hiding this comment

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

Nice work!

@jensens jensens enabled auto-merge February 26, 2026 16:09
@jensens
Copy link
Member Author

jensens commented Feb 26, 2026

https://status.coveralls.io/ is down and blocks the CI

@tseaver
Copy link
Member

tseaver commented Feb 26, 2026

@jensens I just subscribed to the issue at https://status.coveralls.io/

@jensens jensens merged commit 58cf4b8 into zopefoundation:master Feb 27, 2026
173 of 197 checks passed
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