Skip to content

Cache construction metadata - #745

Draft
hadley wants to merge 3 commits into
mainfrom
prototype-cached-constructor-metadata
Draft

hadley wants to merge 3 commits into
mainfrom
prototype-cached-constructor-metadata

Conversation

@hadley

@hadley hadley commented Jul 30, 2026

Copy link
Copy Markdown
Member

Prototype for #723.

This is stacked on the functional commits from #737 and #743 and should be rebased once those PRs land.

Classes cache construction metadata in the environment-backed class reference introduced by #743:

  • property names with setters
  • property storage names
  • parent property names
  • stored properties requiring validation
  • expected base types
  • whether a class layer adds any validation work

new_object() uses the cache to avoid rediscovering setters and storage names, and skips validation when a concrete parent is already valid and the child adds neither properties nor a validator. validate_properties() uses the cached validation plan and directly reads stored attributes for ordinary non-S4 objects, avoiding repeated generic prop() lookups.

Benchmarks

I ran bench/constructor.R back-to-back against the combined #737 + #743 baseline and this prototype on arm64 with R 4.5.3.

Case #737 + #743 Prototype Change
Depth 1 25 µs 20 µs 1.2x faster
Depth 5 120 µs 100 µs 1.2x faster
Depth 10 260 µs 210 µs 1.2x faster
Width 0 25 µs 20 µs 1.2x faster
Width 10 67 µs 48 µs 1.4x faster
Width 50 230 µs 130 µs 1.8x faster

The cached metadata increases marginal class-graph memory by approximately 17%. A production version could likely reduce that by using more compact parallel vectors or moving some of the validation plan into C.

Validation

  • air format .
  • devtools::test(): 1,289 passed
  • git diff --check

hadley added 3 commits July 30, 2026 08:16
For a property restricted to a base type, `prop_validate()` called
`class_inherits()` to check the underlying type and then called the base
class validator, which checked exactly the same thing again.

`class_inherits(x, <base class>)` is defined as
`what$class == base_class(x)`, and every base class validator is the
auto-generated `if (base_class(object) != <name>)` check produced by
`new_base_class()` (the only call sites are base.R and
base-environment.R). So once `class_inherits()` passes there is nothing
left for the validator to reject, and it can be skipped.

`class_validate()` was already a no-op for unions, `class_any`,
`class_missing`, and `NULL`, so those stay on the general path.

Also tighten `validate_properties()`: return early when a class has no
properties, and only grow `errors` when a property actually fails (it was
doing `c(errors, NULL)` on every iteration).

wide50 411us -> 224us, wide10 114us -> 76us.
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.

1 participant