Skip to content

Lazy-allocate @api_class and @point_in_time_copies on InheritableSetting#2740

Open
ericproulx wants to merge 1 commit into
masterfrom
perf/lazy-inheritable-setting-fields
Open

Lazy-allocate @api_class and @point_in_time_copies on InheritableSetting#2740
ericproulx wants to merge 1 commit into
masterfrom
perf/lazy-inheritable-setting-fields

Conversation

@ericproulx
Copy link
Copy Markdown
Contributor

Summary

  • InheritableSetting#initialize used to eagerly allocate @api_class = {} and @point_in_time_copies = [] per instance, even though most settings layers never touch either:
    • @api_class is only written by external code (plugins) via setting.api_class[:k] = v; nothing in lib/ writes to it.
    • @point_in_time_copies only fills up on settings that get cloned via point_in_time_copy — typically the API-class-level setting, not the per-endpoint copies that dominate boot-time allocations.
  • Switch both to memoizing readers (@x ||= …), drop the eager init, and rewrite the inherit_from propagation loop to access the ivar directly with &.each so the no-copies path doesn't allocate the Array just to iterate zero elements.

Benchmarks

Scenario Before After Delta
Grape::Util::InheritableSetting.new × 100 1600 objects / 183.6 kB 1400 objects / 164.1 kB −11%

Smaller absolute win than #2739, but stacks with it for compounded boot-time savings.

Test plan

  • bundle exec rspec — 2313 examples, 0 failures
  • bundle exec rubocop — clean on touched file
  • CI green

🤖 Generated with Claude Code

…Setting

`InheritableSetting#initialize` used to eagerly allocate both fields up
front. Neither is touched on most settings layers:

- `@api_class` is only written by external code (e.g. plugins) via
  `setting.api_class[:k] = v`; nothing in `lib/` writes to it.
- `@point_in_time_copies` only fills up on settings that get cloned via
  `point_in_time_copy` — typically the API-class-level setting, not the
  per-endpoint copies that make up the bulk of allocations at boot.

Switch both to memoizing readers (`@x ||= …`), drop the eager init, and
rewrite the `inherit_from` propagation loop to access the ivar directly
with `&.each` so the no-copies path doesn't allocate the Array just to
iterate zero elements.

Measured against master:

  Grape::Util::InheritableSetting.new × 100
    before: 1600 objects, 183.6 kB
    after:  1400 objects, 164.1 kB   (-11%)

Stacks with #2739 (lazy `@new_values`) for compounded boot-time savings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ericproulx ericproulx force-pushed the perf/lazy-inheritable-setting-fields branch from 4ed4652 to 6bea55d Compare May 23, 2026 17:40
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 23, 2026

Danger Report

No issues found.

View run

@ericproulx ericproulx requested a review from dblock May 23, 2026 17:42
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.

2 participants