-
-
Notifications
You must be signed in to change notification settings - Fork 97
Comparing changes
Open a pull request
base repository: ormar-orm/ormar
base: master
head repository: ormar-orm/ormar
compare: perf/relationproxy-getattribute
- 14 commits
- 28 files changed
- 2 contributors
Commits on May 4, 2026
-
optimizations to improve performance, add optional ormar-utils packag…
…e written in rust
Configuration menu - View commit details
-
Copy full SHA for c051af7 - Browse repository at this point
Copy the full SHA c051af7View commit details -
Configuration menu - View commit details
-
Copy full SHA for 3e4b0bc - Browse repository at this point
Copy the full SHA 3e4b0bcView commit details -
Configuration menu - View commit details
-
Copy full SHA for 234e836 - Browse repository at this point
Copy the full SHA 234e836View commit details -
Configuration menu - View commit details
-
Copy full SHA for f2ce426 - Browse repository at this point
Copy the full SHA f2ce426View commit details -
Configuration menu - View commit details
-
Copy full SHA for 5f607a9 - Browse repository at this point
Copy the full SHA 5f607a9View commit details -
Configuration menu - View commit details
-
Copy full SHA for a76d170 - Browse repository at this point
Copy the full SHA a76d170View commit details -
Configuration menu - View commit details
-
Copy full SHA for f2efa09 - Browse repository at this point
Copy the full SHA f2efa09View commit details -
Optimize hot paths with caching and Rust reverse alias map
Profile-driven optimizations targeting the most expensive ormar functions. Key changes: - Cache alias<->field_name mappings per model class, using Rust build_reverse_alias_map for O(1) lookups (was O(n) linear scan, called 406K times in profiling) - Cache (col_name, field_name) pairs to avoid repeated SA column iteration in own_table_columns and extract_prefixed_table_columns - Use set instead of list for selected_columns membership checks - Cache get_name(lower=True), extract_db_own_fields, ormar_fields_set, and ForeignKey constructors dict - Use frozenset for RelationProxy method check End-to-end benchmark improvements: - iterate: 24-30% faster - first: 26-36% faster - get_all: 18-19% faster - saving: 17-25% faster - select_related: 12-17% faster Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 103ec5f - Browse repository at this point
Copy the full SHA 103ec5fView commit details -
Configuration menu - View commit details
-
Copy full SHA for 3dce4a2 - Browse repository at this point
Copy the full SHA 3dce4a2View commit details -
Configuration menu - View commit details
-
Copy full SHA for 1379d65 - Browse repository at this point
Copy the full SHA 1379d65View commit details -
chore: regenerate lock and reorder imports after rebase
Post-rebase tidying: poetry lock bumped to 2.3.3 plus mkdocstrings patch bump, and ruff reordered the third-party `ormar_rust_utils` import in queryset/utils.py.
Configuration menu - View commit details
-
Copy full SHA for edfebd0 - Browse repository at this point
Copy the full SHA edfebd0View commit details
Commits on May 5, 2026
-
perf: cache & specialize _process_kwargs hot path (#1649)
Profile-driven refactor of NewBaseModel._process_kwargs, which fires on every Model.__init__ (user construction and row hydration). Removes redundant per-init work and skips no-op conversion calls for fields that are neither JSON nor bytes. Changes: - Cache _pydantic_field_names, _extra_is_ignore, _allowed_kwarg_names on the class (lazy-populated on first init); installed via metaclass add_cached_properties alongside the existing _json_fields/_bytes_fields caches. - Replace nested _convert_to_bytes(_convert_json(...)) wrapping with an explicit dispatch loop. Common path (regular ormar field, no JSON, no bytes) avoids the function-call overhead entirely. - Inline _remove_extra_parameters_if_they_should_be_ignored behind the cached _extra_is_ignore bool; remove the method (no external callers). - Remove now-unused _convert_to_bytes / _convert_json methods and the orphaned _convert_json entry in quick_access_views. Behavior unchanged: same ModelError messaging on unknown fields, same JSON encoding, same base64 bytes handling. 628 tests pass at 100 % coverage. Benchmark deltas (median, pytest-benchmark): - test_initializing_models[250] -34.9% - test_initializing_models[500] -8.8% - test_iterate[500] / test_iterate[1000] -7.9% / -7.4% - test_get_all_with_related_models[40] -3.2% - I/O-bound get_one / first / get_or_none within noise cProfile (all_with_related): _process_kwargs tottime 0.365s -> 0.238s (-35%). The line-397 dict-comp is no longer a separate hotspot. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 8632965 - Browse repository at this point
Copy the full SHA 8632965View commit details -
perf: replace RelationProxy.__getattribute__ with explicit count/clear
Profile-driven removal of the per-attribute-access Python override on RelationProxy. The previous __getattribute__ existed solely to redirect two list-method names ("count", "clear") to the QuerysetProxy versions; every other attribute access paid the cost of a Python-level __getattribute__ call only to fall through to super. cProfile (all_with_related, 40 000 row hydrations): __getattribute__ was 0.354 s tottime / 6.2 % of scenario time, with 420 000 calls. After this change it is no longer in the top 25 by tottime — attribute access goes through the C-level lookup path. Replacement: define count() and clear() as async methods directly on RelationProxy. They shadow list.count / list.clear by virtue of MRO and delegate to queryset_proxy after self._initialize_queryset(). The observable async semantics are unchanged: callers always did ``await proxy.count(...)`` / ``await proxy.clear(...)``; the previous override returned a bound async method, the new methods are themselves async — both produce the same coroutine. Behavior unchanged: same signatures (distinct=True / keep_reversed=True defaults), same delegation path, same QuerysetProxy initialization trigger. 628 tests pass at 100 % coverage. Benchmark deltas (median, pytest-benchmark, --warmup=on, 10+ rounds): - test_get_all_with_related_models[10] -17.2 % - test_get_all_with_related_models[20] -15.6 % - test_get_all_with_related_models[40] -7.7 % - test_get_all[250] -13.1 % - test_get_all[500] -9.9 % - test_get_all[1000] -6.4 % - test_iterate[*] within noise - single-row get_one / first I/O-dominated, ±15 % noise Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Configuration menu - View commit details
-
Copy full SHA for 411cc6a - Browse repository at this point
Copy the full SHA 411cc6aView commit details -
perf: precompute field.relation_type and slim RelationsManager init (#…
…1651) Three micro-optimizations on the per-Model.__init__ relation bookkeeping path: 1. Precompute ``relation_type`` once in ``BaseField.__init__`` (priority- ordered branch on ``is_multi`` / ``is_through`` / ``virtual`` / ``is_relation``). ``RelationsManager._add_relation`` reads ``field.relation_type`` directly instead of calling ``_get_relation_type(field)`` per relation per Model.__init__. Saves ~100k method calls per all_with_related scenario. ``_get_relation_type`` deleted. ``RelationType`` is lazy-imported via a module-level ``_relation_type_cls()`` helper to break the circular ``ormar.relations`` → ``relation_manager`` → ``utils`` → ``foreign_key`` → ``base`` chain. 2. Drop ``RelationsManager._related_fields`` and ``_related_names`` attributes. ``__contains__`` now checks ``self._relations`` (already keyed by field name). Removes a per-init list-comp and two ``STORE_ATTR`` ops. 3. ``RelationsManager._add_relation`` reads ``field.relation_type`` directly; an ``assert`` narrows ``Optional[RelationType]`` for mypy and documents the invariant (only relation/through fields reach this path). Behavior unchanged. 628 tests pass at 100 % coverage. Benchmark deltas (median, pytest-benchmark): - test_initializing_models[250] -7.8 % - test_get_all[1000] -7.1 % - test_initializing_models_with_related_models[10] -5.7 % - test_initializing_models[500] -1.7 % - I/O-dominated single-row queries within noise cProfile (all_with_related): scenario 5.481 s -> 5.328 s (-2.8 %). ``_get_relation_type`` no longer in the trace. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 692acc5 - Browse repository at this point
Copy the full SHA 692acc5View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff master...perf/relationproxy-getattribute