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) 295.0 ns 283.6 ns 194.5 ns 520.4 ns
attr get 44.6 ns 45.1 ns 39.5 ns 45.2 ns
attr set 136.6 ns 138.5 ns 52.1 ns 142.0 ns
eq (==, same) 314.3 ns 393.7 ns 134.0 ns 295.6 ns
hash (frozen) 457.2 ns 456.5 ns 96.4 ns 109.2 ns
repr 574.5 ns 581.6 ns 324.0 ns 1.08 us
str 581.8 ns 591.7 ns 323.8 ns 937.6 ns
clone (shallow) 507.0 ns 540.9 ns 647.7 ns 599.2 ns
clone (deep) 954.5 ns 963.8 ns 2.31 us 1.73 us
to dict/json 409.5 ns 393.8 ns 631.0 ns 526.9 ns
to json str 2.27 us 2.28 us 1.51 us 609.8 ns
from dict/json 990.3 ns 1.03 us 197.9 ns 669.8 ns
from json str 3.58 us 3.53 us 535.4 ns
Symbolic sym_* (no analog)
sym_clone 527.7 ns 566.0 ns
sym_rebind 911.7 ns 880.0 ns
sym_seal / unseal 91.6 ns 91.6 ns
sym_eq 83.1 ns 83.0 ns
sym_ne 114.9 ns 113.7 ns
sym_lt 1.22 us 1.24 us
sym_gt 1.45 us 1.45 us
sym_hash 390.6 ns 394.9 ns
sym_missing 575.0 ns 578.3 ns
sym_nondefault 2.06 us 2.07 us
sym_keys 232.8 ns 233.3 ns
sym_values 231.8 ns 232.4 ns
sym_items 257.8 ns 256.7 ns
sym_hasattr 169.5 ns 167.0 ns
sym_getattr 185.2 ns 186.3 ns
sym_get 444.5 ns 446.3 ns
sym_has 277.4 ns 283.6 ns
sym_contains 508.1 ns 516.0 ns
sym_jsonify 1.03 us 1.30 us
sym_descendants 1.10 us 1.11 us
sym_attr_field 172.2 ns 175.2 ns
format(compact) 407.9 ns 405.3 ns
query (pg.query) 6.07 us 6.17 us
Properties sym_* (no analog)
sym_partial 315.5 ns 308.6 ns
sym_abstract 516.3 ns 515.5 ns
sym_puresymbolic 184.5 ns 184.7 ns
sym_sealed 97.8 ns 97.3 ns
topo_root 199.0 ns
sym_origin 97.6 ns 97.0 ns
sym_init_args 1.15 us 1.22 us
topo_path 91.1 ns
topo_parent 92.7 ns
enable_topo 51.1 ns 52.0 ns
accessor_writable 98.4 ns 98.6 ns
allow_partial 98.2 ns 97.7 ns
is_deterministic 1.29 us 1.29 us

Section B — key operations across scales

construct

Shape topo=False topo=True dataclass pydantic
3 fields 289.5 ns 285.7 ns 195.7 ns 518.2 ns
4 fields 329.6 ns 319.8 ns 246.8 ns 570.2 ns
8 fields 500.4 ns 499.1 ns 531.5 ns 877.1 ns
16 fields 840.0 ns 847.4 ns 1.26 us 1.44 us
8 fields + 4 kwargs 1.13 us
container children 742.7 ns 804.1 ns 233.7 ns 775.4 ns
symbolic children 1.24 us 2.79 us 299.3 ns 821.1 ns

attr get

Shape topo=False topo=True dataclass pydantic
3 fields 39.3 ns 39.1 ns 33.9 ns 39.5 ns
4 fields 39.1 ns 39.1 ns 34.3 ns 40.1 ns
8 fields 38.9 ns 39.2 ns 34.1 ns 39.9 ns
16 fields 39.5 ns 39.3 ns 34.0 ns 40.1 ns
8 fields + 4 kwargs 39.5 ns 40.3 ns 34.4 ns 39.9 ns
container children 39.1 ns 39.4 ns 34.1 ns 39.9 ns
symbolic children 39.5 ns 39.4 ns 34.2 ns 40.1 ns

eq

Shape topo=False topo=True dataclass pydantic
3 fields 297.7 ns 380.6 ns 117.5 ns 280.0 ns
4 fields 311.6 ns 433.9 ns 142.1 ns 289.8 ns
8 fields 326.7 ns 584.2 ns 239.0 ns 309.1 ns
16 fields 382.3 ns 932.0 ns 429.1 ns 364.1 ns
8 fields + 4 kwargs 333.2 ns 593.9 ns 238.0 ns 318.7 ns
container children 522.9 ns 518.4 ns 169.4 ns 325.0 ns
symbolic children 921.2 ns 971.6 ns 221.1 ns 598.4 ns

clone

Shape topo=False topo=True dataclass pydantic
3 fields 511.1 ns 525.0 ns 610.7 ns 592.2 ns
4 fields 525.4 ns 540.9 ns 665.4 ns 600.7 ns
8 fields 549.5 ns 605.8 ns 675.1 ns 638.6 ns
16 fields 591.4 ns 712.8 ns 765.7 ns 715.2 ns
8 fields + 4 kwargs 567.8 ns 623.6 ns 703.3 ns 632.5 ns
container children 555.4 ns 563.9 ns 662.4 ns 622.9 ns
symbolic children 545.2 ns 3.75 us 637.8 ns 600.3 ns

clone/deep

Shape topo=False topo=True dataclass pydantic
3 fields 905.4 ns 906.4 ns 2.19 us 1.65 us
4 fields 994.9 ns 985.2 ns 2.49 us 1.83 us
8 fields 1.34 us 1.33 us 3.50 us 2.85 us
16 fields 1.95 us 1.94 us 5.27 us 4.70 us
8 fields + 4 kwargs 1.32 us 1.32 us 3.39 us 3.05 us
container children 4.20 us 4.23 us 4.40 us 3.87 us
symbolic children 2.49 us 3.82 us 4.29 us 3.38 us

to json

Shape topo=False topo=True dataclass pydantic
3 fields 399.1 ns 384.3 ns 631.7 ns 525.5 ns
4 fields 425.6 ns 425.8 ns 733.3 ns 559.3 ns
8 fields 528.5 ns 622.3 ns 1.18 us 720.2 ns
16 fields 773.8 ns 1.02 us 2.05 us 1.04 us
8 fields + 4 kwargs 530.3 ns 619.3 ns 1.18 us 740.7 ns
container children 672.4 ns 9.69 us 1.66 us 796.8 ns
symbolic children 2.25 us 634.9 ns 1.19 us 686.5 ns

from json

Shape topo=False topo=True dataclass pydantic
3 fields 978.3 ns 1.01 us 200.9 ns 666.7 ns
4 fields 1.17 us 1.13 us 247.6 ns 701.3 ns
8 fields 1.58 us 1.56 us 529.8 ns 967.8 ns
16 fields 2.49 us 2.46 us 1.28 us 1.41 us
8 fields + 4 kwargs 1.15 us
container children 1.77 us 1.84 us 234.3 ns 897.7 ns
symbolic children 2.04 us 3.70 us 299.7 ns 963.5 ns