Skip to content

Performance report — pg.Object vs dataclass / pydantic

Active core: rust (native pygx_core when rust). Median ns/op (testing/benchmarks/_bench.py auto-scaling timer); lower is better; = no equivalent. Numbers are machine-specific (Apple Silicon here) — read ratios, not absolutes.

Regenerate: PYGX_CORE=rust .venv-coredev/bin/python testing/benchmarks/perf_report_bench.py

Notes: 8 fields + 4 kwargs adds 4 undeclared kwargs at construction. pygx Objects (topo=False and topo=True) and dataclass are typed/closed and reject undeclared kwargs by design, so construct / from-json show ; only pydantic (extra="allow") absorbs them. (Read-only ops for that shape run on the 8 declared fields.) symbolic children nests a 2-field object as a field. to/from-json is not identical work across columns: pygx emits a _type tag for round-trippable typed reconstruction (and dispatches on it when loading), whereas dataclass asdict / pydantic model_dump emit bare dicts — extra work attributed to pygx. Class-level utilities (register/load/save/...) and on_sym_* hooks are excluded (not per-instance ops).

Section A — all public operations (3 scalar fields)

Operation topo=False topo=True dataclass pydantic
Lifecycle (dataclass / pydantic analog)
construct (kwargs) 292.4 ns 282.7 ns 191.8 ns 522.3 ns
attr get 44.6 ns 44.5 ns 39.7 ns 45.4 ns
attr set 139.7 ns 138.6 ns 52.1 ns 142.6 ns
eq (==, same) 312.4 ns 391.3 ns 130.2 ns 288.3 ns
hash (frozen) 459.0 ns 445.3 ns 101.5 ns 113.2 ns
repr 558.8 ns 555.4 ns 306.3 ns 1.03 us
str 564.2 ns 560.5 ns 303.9 ns 909.9 ns
clone (shallow) 490.8 ns 515.7 ns 624.9 ns 592.0 ns
clone (deep) 914.6 ns 881.6 ns 2.27 us 1.62 us
to dict/json 388.6 ns 387.9 ns 631.0 ns 531.8 ns
to json str 2.22 us 2.19 us 1.51 us 596.8 ns
from dict/json 977.4 ns 981.9 ns 195.6 ns 656.2 ns
from json str 3.43 us 3.46 us 532.5 ns
Symbolic sym_* (no analog)
sym_clone 511.6 ns 528.2 ns
sym_rebind 887.2 ns 869.9 ns
sym_seal / unseal 91.3 ns 90.7 ns
sym_eq 80.7 ns 81.2 ns
sym_ne 110.6 ns 110.2 ns
sym_lt 1.20 us 1.22 us
sym_gt 1.43 us 1.46 us
sym_hash 403.1 ns 399.0 ns
sym_missing 566.2 ns 566.3 ns
sym_nondefault 2.14 us 2.13 us
sym_keys 226.7 ns 228.4 ns
sym_values 226.3 ns 226.3 ns
sym_items 248.6 ns 248.3 ns
sym_hasattr 168.3 ns 167.6 ns
sym_getattr 187.4 ns 186.8 ns
sym_get 445.5 ns 454.5 ns
sym_has 272.7 ns 275.8 ns
sym_contains 502.1 ns 494.8 ns
sym_jsonify 1.03 us 1.30 us
sym_descendants 1.10 us 1.11 us
sym_attr_field 172.5 ns 171.9 ns
format(compact) 403.0 ns 404.2 ns
query (pg.query) 5.99 us 6.07 us
Properties sym_* (no analog)
sym_partial 297.1 ns 298.7 ns
sym_abstract 495.3 ns 504.2 ns
sym_puresymbolic 178.8 ns 180.4 ns
sym_sealed 98.0 ns 97.9 ns
topo_root 197.5 ns
sym_origin 97.9 ns 97.6 ns
sym_init_args 1.14 us 1.19 us
topo_path 90.2 ns
topo_parent 90.7 ns
enable_topo 49.7 ns 49.7 ns
accessor_writable 97.5 ns 97.8 ns
allow_partial 97.8 ns 97.6 ns
is_deterministic 1.31 us 1.31 us

Section B — key operations across scales

construct

Shape topo=False topo=True dataclass pydantic
3 fields 289.6 ns 290.0 ns 201.6 ns 534.5 ns
4 fields 337.7 ns 335.0 ns 248.0 ns 593.1 ns
8 fields 512.7 ns 508.4 ns 528.9 ns 899.5 ns
16 fields 849.8 ns 860.7 ns 1.27 us 1.49 us
8 fields + 4 kwargs 1.22 us
container children 756.3 ns 821.9 ns 244.9 ns 785.0 ns
symbolic children 1.22 us 2.97 us 320.0 ns 853.7 ns

attr get

Shape topo=False topo=True dataclass pydantic
3 fields 39.0 ns 39.1 ns 34.9 ns 40.3 ns
4 fields 40.0 ns 40.3 ns 35.0 ns 41.1 ns
8 fields 39.4 ns 39.9 ns 34.8 ns 40.5 ns
16 fields 40.6 ns 40.0 ns 34.1 ns 41.5 ns
8 fields + 4 kwargs 40.8 ns 40.7 ns 35.4 ns 40.8 ns
container children 40.2 ns 39.6 ns 35.3 ns 40.8 ns
symbolic children 40.1 ns 41.0 ns 35.8 ns 42.1 ns

eq

Shape topo=False topo=True dataclass pydantic
3 fields 302.0 ns 384.7 ns 119.8 ns 283.9 ns
4 fields 315.2 ns 431.5 ns 141.0 ns 292.4 ns
8 fields 342.9 ns 597.0 ns 232.3 ns 316.1 ns
16 fields 387.0 ns 938.3 ns 429.7 ns 363.5 ns
8 fields + 4 kwargs 349.0 ns 607.1 ns 240.8 ns 323.5 ns
container children 536.1 ns 539.0 ns 176.9 ns 328.5 ns
symbolic children 949.2 ns 975.3 ns 220.3 ns 607.2 ns

clone

Shape topo=False topo=True dataclass pydantic
3 fields 488.6 ns 520.0 ns 634.2 ns 595.5 ns
4 fields 507.0 ns 548.8 ns 664.5 ns 602.7 ns
8 fields 542.5 ns 616.0 ns 679.9 ns 645.5 ns
16 fields 591.2 ns 724.4 ns 802.8 ns 748.5 ns
8 fields + 4 kwargs 552.6 ns 619.7 ns 711.6 ns 641.5 ns
container children 545.5 ns 571.2 ns 686.2 ns 647.6 ns
symbolic children 533.1 ns 3.78 us 676.3 ns 617.2 ns

clone/deep

Shape topo=False topo=True dataclass pydantic
3 fields 888.9 ns 884.7 ns 2.20 us 1.64 us
4 fields 971.4 ns 971.0 ns 2.51 us 1.88 us
8 fields 1.31 us 1.31 us 3.52 us 2.84 us
16 fields 1.98 us 1.97 us 5.47 us 4.77 us
8 fields + 4 kwargs 1.37 us 1.35 us 3.50 us 3.20 us
container children 4.24 us 4.30 us 4.58 us 3.92 us
symbolic children 2.46 us 3.88 us 4.55 us 3.43 us

to json

Shape topo=False topo=True dataclass pydantic
3 fields 391.9 ns 394.4 ns 647.3 ns 530.0 ns
4 fields 415.7 ns 429.8 ns 752.1 ns 571.3 ns
8 fields 538.9 ns 638.3 ns 1.17 us 730.3 ns
16 fields 770.3 ns 1.05 us 2.08 us 1.07 us
8 fields + 4 kwargs 541.4 ns 639.7 ns 1.19 us 768.5 ns
container children 668.8 ns 10.17 us 1.74 us 827.6 ns
symbolic children 2.30 us 647.2 ns 1.22 us 708.0 ns

from json

Shape topo=False topo=True dataclass pydantic
3 fields 978.4 ns 969.8 ns 207.7 ns 648.2 ns
4 fields 1.12 us 1.13 us 256.1 ns 709.5 ns
8 fields 1.61 us 1.59 us 537.5 ns 987.4 ns
16 fields 2.48 us 2.49 us 1.30 us 1.43 us
8 fields + 4 kwargs 1.18 us
container children 1.77 us 1.86 us 244.9 ns 924.6 ns
symbolic children 1.98 us 3.66 us 310.7 ns 968.2 ns