зеркало из https://github.com/github/ruby.git
Avoid array allocation for f(*empty_ary, **hash) when def f(x)
This avoids an array allocation when calling a method that does not accept a positional splat or keywords with both a positional splat and keywords. Previously, Ruby would dup the positional splat to append the keyword splat to it. Then it would flatten the dupped positional splat array to the VM stack. This flattens the given positional splat to the VM stack, then adds the keyword splat hash after the last positional splat element on the VM stack, avoiding the need to modify the positional splat array.
This commit is contained in:
Родитель
2c79a7641f
Коммит
94e7d26643
|
@ -123,8 +123,7 @@ class TestAllocation < Test::Unit::TestCase
|
|||
check_allocations(0, 0, "required(*r2k_empty_array1#{block})")
|
||||
check_allocations(0, 1, "required(*r2k_array#{block})")
|
||||
|
||||
# Currently allocates 1 array unnecessarily
|
||||
check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
|
||||
check_allocations(0, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
|
||||
RUBY
|
||||
end
|
||||
|
||||
|
@ -148,8 +147,7 @@ class TestAllocation < Test::Unit::TestCase
|
|||
check_allocations(0, 0, "optional(*r2k_empty_array1#{block})")
|
||||
check_allocations(0, 1, "optional(*r2k_array#{block})")
|
||||
|
||||
# Currently allocates 1 array unnecessarily
|
||||
check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
|
||||
check_allocations(0, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
|
||||
RUBY
|
||||
end
|
||||
|
||||
|
|
30
vm_args.c
30
vm_args.c
|
@ -672,9 +672,32 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
|||
}
|
||||
else if (!ISEQ_BODY(iseq)->param.flags.has_kwrest && !ISEQ_BODY(iseq)->param.flags.has_kw) {
|
||||
converted_keyword_hash = check_kwrestarg(converted_keyword_hash, &kw_flag);
|
||||
arg_rest_dup(args);
|
||||
rb_ary_push(args->rest, converted_keyword_hash);
|
||||
keyword_hash = Qnil;
|
||||
if (ISEQ_BODY(iseq)->param.flags.has_rest) {
|
||||
arg_rest_dup(args);
|
||||
rb_ary_push(args->rest, converted_keyword_hash);
|
||||
keyword_hash = Qnil;
|
||||
}
|
||||
else {
|
||||
// Avoid duping rest when not necessary
|
||||
// Copy rest elements and converted keyword hash directly to VM stack
|
||||
const VALUE *argv = RARRAY_CONST_PTR(args->rest);
|
||||
int j, i=args->argc, rest_len = RARRAY_LENINT(args->rest);
|
||||
if (rest_len) {
|
||||
CHECK_VM_STACK_OVERFLOW(ec->cfp, rest_len+1);
|
||||
given_argc += rest_len;
|
||||
args->argc += rest_len;
|
||||
for (j=0; rest_len > 0; rest_len--, i++, j++) {
|
||||
locals[i] = argv[j];
|
||||
}
|
||||
}
|
||||
locals[i] = converted_keyword_hash;
|
||||
given_argc--;
|
||||
args->argc++;
|
||||
args->rest = Qfalse;
|
||||
ci_flag &= ~(VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT);
|
||||
keyword_hash = Qnil;
|
||||
goto arg_splat_and_kw_splat_flattened;
|
||||
}
|
||||
}
|
||||
else {
|
||||
keyword_hash = converted_keyword_hash;
|
||||
|
@ -768,6 +791,7 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
|
|||
FL_SET_RAW(flag_keyword_hash, RHASH_PASS_AS_KEYWORDS);
|
||||
}
|
||||
|
||||
arg_splat_and_kw_splat_flattened:
|
||||
if (kw_flag && ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg) {
|
||||
rb_raise(rb_eArgError, "no keywords accepted");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче