Stop reallocating the positional-param-types array every call - #48
Open
maedi wants to merge 1 commit into
Open
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 inuntyped_argsin the first place, replacing a different, buggy check. Targeting that branch rather thanmainso the diff here is just this change.Summary
Two related per-call allocations in
Redefiner, present in bothtyped_methodsanduntyped_args:%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 newArrayon every single param, every single call. Hoisted to a frozenPOSITIONAL_PARAM_TYPESconstant.untyped_argsreturned[args, kwargs]for the caller to reassign into its ownargs/kwargs— butuntyped_argsalready mutates those sameArray/Hashobjects in place (args[position] = value,kwargs[name] = value), so the caller'sargs/kwargsalready reflected every change once the method returned. The return value — and the caller'sargs, kwargs = Low::Redefiner.untyped_args(...)reassignment — was pure redundant allocation for something already true.Verification
Measured with
GC.disable+ObjectSpacediffing (nothing freed mid-measurement, every allocation attributable) on a call to a method with 2 keyword type-expression params:typed_methods(type_checking: true, the default)untyped_args(type_checking: false)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).