Remove 1 allocation in Enumerable#each_with_index (#11868)

* Remove 1 allocation in Enumerable#each_with_index

Previously, each call to Enumerable#each_with_index allocates 2
objects, one for the counting index, the other an imemo_ifunc passed
to `self.each` as a block.

Use `struct vm_ifunc::data` to hold the counting index directly to
remove 1 allocation.

* [DOC] Brief summary for usages of `struct vm_ifunc`
This commit is contained in:
Alan Wu 2024-10-11 10:22:44 -04:00 коммит произвёл GitHub
Родитель 372bb990ac
Коммит 11e7ab79de
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 23 добавлений и 10 удалений

14
enum.c
Просмотреть файл

@ -2984,13 +2984,12 @@ enum_member(VALUE obj, VALUE val)
}
static VALUE
each_with_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo))
each_with_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(_, index))
{
struct MEMO *m = MEMO_CAST(memo);
VALUE n = imemo_count_value(m);
struct vm_ifunc *ifunc = rb_current_ifunc();
ifunc->data = (const void *)rb_int_succ(index);
imemo_count_up(m);
return rb_yield_values(2, rb_enum_values_pack(argc, argv), n);
return rb_yield_values(2, rb_enum_values_pack(argc, argv), index);
}
/*
@ -3024,12 +3023,9 @@ each_with_index_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo))
static VALUE
enum_each_with_index(int argc, VALUE *argv, VALUE obj)
{
struct MEMO *memo;
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
memo = MEMO_NEW(0, 0, 0);
rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)memo);
rb_block_call(obj, id_each, argc, argv, each_with_index_i, INT2FIX(0));
return obj;
}

Просмотреть файл

@ -79,7 +79,12 @@ struct vm_ifunc_argc {
#endif
};
/*! IFUNC (Internal FUNCtion) */
/*! IFUNC (Internal FUNCtion)
*
* Bookkeeping for converting a C function and some closed-over data into a
* block passable to methods. Like Ruby Proc, but not directly accessible at
* Ruby level since this is an imemo. See rb_block_call() and friends.
*/
struct vm_ifunc {
VALUE flags;
VALUE *svar_lep;

Просмотреть файл

@ -77,6 +77,7 @@ VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv,
void rb_check_stack_overflow(void);
#define RB_BLOCK_NO_USE_PACKED_ARGS 2
VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, VALUE data2, long flags);
struct vm_ifunc *rb_current_ifunc(void);
#if USE_YJIT
/* vm_exec.c */

Просмотреть файл

@ -2702,6 +2702,17 @@ rb_current_realfilepath(void)
return Qnil;
}
// Assert that an internal function is running and return
// the imemo object that represents it.
struct vm_ifunc *
rb_current_ifunc(void)
{
// Search VM_FRAME_MAGIC_IFUNC to see ifunc imemos put on the iseq field.
VALUE ifunc = (VALUE)GET_EC()->cfp->iseq;
RUBY_ASSERT_ALWAYS(imemo_type_p(ifunc, imemo_ifunc));
return (struct vm_ifunc *)ifunc;
}
void
Init_vm_eval(void)
{