Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Stop reallocating the positional-param-types array every call - #48

Open
maedi wants to merge 1 commit into
fix/untyped-args-keyword-param-detectionfrom
perf/hoist-positional-check-array-literal
Open

Stop reallocating the positional-param-types array every call#48
maedi wants to merge 1 commit into
fix/untyped-args-keyword-param-detectionfrom
perf/hoist-positional-check-array-literal

Conversation

@maedi

@maedi maedi commented Sep 1, 2026

Copy link
Copy Markdown
Member

Stacked on #46

This builds on #46 (fix/untyped-args-keyword-param-detection) — that PR introduces the %i[pos_req pos_opt].include?(param_proxy.type) check in untyped_args in the first place, replacing a different, buggy check. Targeting that branch rather than main so the diff here is just this change.

Summary

Two related per-call allocations in Redefiner, present in both typed_methods and untyped_args:

  1. %i[pos_req pos_opt].include?(param_proxy.type) — array literals aren't cached the way frozen string literals are under # frozen_string_literal: true (that magic comment only affects strings), so this allocated a brand new Array on every single param, every single call. Hoisted to a frozen POSITIONAL_PARAM_TYPES constant.

  2. untyped_args returned [args, kwargs] for the caller to reassign into its own args/kwargs — but untyped_args already mutates those same Array/Hash objects in place (args[position] = value, kwargs[name] = value), so the caller's args/kwargs already reflected every change once the method returned. The return value — and the caller's args, kwargs = Low::Redefiner.untyped_args(...) reassignment — was pure redundant allocation for something already true.

Verification

Measured with GC.disable + ObjectSpace diffing (nothing freed mid-measurement, every allocation attributable) on a call to a method with 2 keyword type-expression params:

Path Before After
typed_methods (type_checking: true, the default) 8 6
untyped_args (type_checking: false) 13 10

Since the array-literal saving is per-param, this scales with how many typed params a method has — a bigger win for larger method signatures than this 2-param example shows.

Full suite: 147 examples, same 10 pre-existing failures as main (Ruby-version-dependent formatting differences in hash/error-message output, unrelated to this change).

Two related per-call allocations in Redefiner, present in both
typed_methods and untyped_args:

1. `%i[pos_req pos_opt].include?(param_proxy.type)` -- array literals
   aren't cached the way frozen string literals are, so this allocated
   a brand new Array on every single param, every single call. Hoisted
   to a frozen POSITIONAL_PARAM_TYPES constant.

2. untyped_args returned `[args, kwargs]` for the caller to reassign
   into its own args/kwargs -- but untyped_args already mutates those
   same Array/Hash objects in place, so the caller's args/kwargs
   already reflected the changes. The return value (and the caller's
   `args, kwargs = ...` reassignment) was pure redundant allocation.

Measured with GC.disable + ObjectSpace diffing on a 2-keyword-param
method call:
  typed_methods (type_checking: true, the default):    8 -> 6 allocations
  untyped_args  (type_checking: false):                13 -> 10 allocations

Scales with param count, since the array-literal saving is per-param.

Full suite: 147 examples, same 10 pre-existing failures as main
(Ruby-version-dependent formatting differences, unrelated).

Branched from/depends on #46 (fix/untyped-args-keyword-param-detection)
-- that PR introduces the %i[pos_req pos_opt].include?(param_proxy.type)
check in untyped_args in the first place (replacing a different, buggy
check), so this builds on it rather than main directly.
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