Title: Use dict keys-version stamps and entry-index hints in LOAD_ATTR/STORE_ATTR specializations by youknowone · Pull Request #8350 · RustPython/RustPython · GitHub
Open Graph Title: Use dict keys-version stamps and entry-index hints in LOAD_ATTR/STORE_ATTR specializations by youknowone · Pull Request #8350 · RustPython/RustPython
X Title: Use dict keys-version stamps and entry-index hints in LOAD_ATTR/STORE_ATTR specializations by youknowone · Pull Request #8350 · RustPython/RustPython
Description: Even after LOAD_ATTR specializes, the specialized handlers still paid for a full hash+probe dict lookup on every execution — LoadAttrWithHint ignored its hint, LoadAttrMethodWithValues probed the instance dict as its shadow check, and StoreAttr* re-probed on every store. This PR replaces those probes with two cheap guards. Changes Dict keys-version stamp (dict_inner.rs) Dict gets a keys_version: AtomicU32. Stamps are allocated from a global counter and never reused, so equal nonzero stamps guarantee an unchanged key set. Any key-set change (new key, deletion, pop, clear, drain_entries) resets the stamp while holding the write lock, before the mutation; value-only updates keep it. New get_with_hint (lookup returning the entry index) and insert_with_hint (identity-checked value replace by entry index, falling back to a full insert). LOAD_ATTR_METHOD_WITH_VALUES / LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES — the shadow check caches the instance dict's stamp in the instruction's pointer-cache slot. While the stamp matches, the per-call dict probe is skipped entirely (an integer compare, like CPython's keys-version check). The stamp is taken before the absence probe so a concurrent key-set change can never leave a stale skip. Dict subclasses never get a stamp and always probe. LOAD_ATTR_WITH_HINT — the specializer now stores the entry index in the cache. A hit is a single identity check on the hinted entry's key — no hashing, no probe sequence. On miss the handler re-probes and refreshes the hint in place. STORE_ATTR_WITH_HINT / STORE_ATTR_INSTANCE_VALUE — same treatment via set_item_with_hint: value replace through the cached entry index, self-refreshing on miss. Results (Apple M5 Max, release, vs CPython 3.14.5) benchmark before after CPython c.x read overhead vs local read 25.4 ns ~16 ns (−37%) 2.2 ns self.value r/w + c.add(i) loop 183 ns/iter ~163 ns/iter (−11%) 24.4 ns Profiles confirm the Dict::get hash-probe frames (~7–10% of samples) are gone from both loops; the remaining gap is dominated by call overhead, int boxing, and dispatch (tracked separately). Testing cargo test workspace (the pre-existing rustpython-capi pyo3 build failure is unrelated) -m test for test_dict, test_descr, test_class, test_weakref, test_gc, test_property, test_super, test_builtin, test_types, test_copy, test_pickle, test_ordered_dict, test_userdict, test_collections — all SUCCESS New regression tests in extra_tests/snippets/vm_specialization.py (shadowing after warmup, value-only updates, hint invalidation across key churn / __dict__ replacement / clear()), also validated against CPython 3.14 Multithreaded stress test of concurrent shadowing/deletion and key-set churn against the stamp and hint paths Fixes youknowone#42 🤖 Generated with Claude Code Summary by CodeRabbit Performance Improved instance attribute access and dict lookups using refreshed hint-based fast paths that account for per-dictionary keyset/layout changes. Enhanced hinted attribute store/update so cached attribute resolution remains valid with fewer slow-path fallbacks. Bug Fixes Strengthened cache invalidation on keyset mutations (including clears/drains and related updates) to avoid stale results. Improved correctness when attributes/methods are shadowed and when dicts have “holes” after deletions. Tests Expanded VM specialization regression coverage for method/value shadowing, hint survival across attribute/dict churn, shared layout behavior, and hole handling.
Open Graph Description: Even after LOAD_ATTR specializes, the specialized handlers still paid for a full hash+probe dict lookup on every execution — LoadAttrWithHint ignored its hint, LoadAttrMethodWithValues probed the i...
X Description: Even after LOAD_ATTR specializes, the specialized handlers still paid for a full hash+probe dict lookup on every execution — LoadAttrWithHint ignored its hint, LoadAttrMethodWithValues probed the i...
Opengraph URL: https://github.com/RustPython/RustPython/pull/8350
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:69a37fca-536d-d99f-1180-ecd5bae2d844 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | C7BE:13B657:D34E9B:121AFD4:6A628E80 |
| html-safe-nonce | eb323edb8730391530a1bb093e28661dd702ed8fc3b2ba82790f63306aa98416 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDN0JFOjEzQjY1NzpEMzRFOUI6MTIxQUZENDo2QTYyOEU4MCIsInZpc2l0b3JfaWQiOiI2NDUyNDgzMzM2NTUzMjcwOTEyIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | b4743836917c20cf309e52084a14ba6de5fa8a861da27cff1a27bef9a2a216bd |
| hovercard-subject-tag | pull_request:4112961018 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/RustPython/RustPython/pull/8350/files |
| twitter:image | https://avatars.githubusercontent.com/u/69878?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/69878?s=400&v=4 |
| og:image:alt | Even after LOAD_ATTR specializes, the specialized handlers still paid for a full hash+probe dict lookup on every execution — LoadAttrWithHint ignored its hint, LoadAttrMethodWithValues probed the i... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | ac2209d8cb6970a1bd08cb0a3f821d7166ac544c2ca1c87819c335bb84310ecd |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/RustPython/RustPython git https://github.com/RustPython/RustPython.git |
| octolytics-dimension-user_id | 39710557 |
| octolytics-dimension-user_login | RustPython |
| octolytics-dimension-repository_id | 135201145 |
| octolytics-dimension-repository_nwo | RustPython/RustPython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 135201145 |
| octolytics-dimension-repository_network_root_nwo | RustPython/RustPython |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 2fca8cd598678aaa64706088ad13b4e19177e108 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width