Use rb_block_call2 for some Enumerable methods

Enumerable#all?, #any?, #one?, and #none? do not use yielded arguments
as an Array. So they can use rb_block_call2 to omit array allocatoin.

Enumerable#find does not have to immediately accept yielded arguments as
an Array. It can delay array allocation until the predicate block
returns truthy.

(TODO: Enumerable#count and #find_all seem to be optimizable as well.)
This commit is contained in:
Yusuke Endoh 2024-07-09 18:30:03 +09:00
Родитель 114e32b357
Коммит 182822683f
1 изменённых файлов: 28 добавлений и 9 удалений

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

@ -322,16 +322,32 @@ enum_count(int argc, VALUE *argv, VALUE obj)
return imemo_count_value(memo);
}
NORETURN(static void found(VALUE i, VALUE memop));
static void
found(VALUE i, VALUE memop) {
struct MEMO *memo = MEMO_CAST(memop);
MEMO_V1_SET(memo, i);
memo->u3.cnt = 1;
rb_iter_break();
}
static VALUE
find_i_fast(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
if (RTEST(rb_yield_values2(argc, argv))) {
ENUM_WANT_SVALUE();
found(i, memop);
}
return Qnil;
}
static VALUE
find_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, memop))
{
ENUM_WANT_SVALUE();
if (RTEST(enum_yield(argc, i))) {
struct MEMO *memo = MEMO_CAST(memop);
MEMO_V1_SET(memo, i);
memo->u3.cnt = 1;
rb_iter_break();
found(i, memop);
}
return Qnil;
}
@ -366,7 +382,10 @@ enum_find(int argc, VALUE *argv, VALUE obj)
if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
RETURN_ENUMERATOR(obj, argc, argv);
memo = MEMO_NEW(Qundef, 0, 0);
rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
if (rb_block_pair_yield_optimizable())
rb_block_call2(obj, id_each, 0, 0, find_i_fast, (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
else
rb_block_call2(obj, id_each, 0, 0, find_i, (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
if (memo->u3.cnt) {
return memo->v1;
}
@ -1817,7 +1836,7 @@ enum_all(int argc, VALUE *argv, VALUE obj)
{
struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
rb_block_call2(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
return memo->v1;
}
@ -1879,7 +1898,7 @@ enum_any(int argc, VALUE *argv, VALUE obj)
{
struct MEMO *memo = MEMO_ENUM_NEW(Qfalse);
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
rb_block_call2(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
return memo->v1;
}
@ -2168,7 +2187,7 @@ enum_one(int argc, VALUE *argv, VALUE obj)
VALUE result;
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
rb_block_call2(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
result = memo->v1;
if (UNDEF_P(result)) return Qfalse;
return result;
@ -2229,7 +2248,7 @@ enum_none(int argc, VALUE *argv, VALUE obj)
struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
WARN_UNUSED_BLOCK(argc);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
rb_block_call2(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo, RB_BLOCK_NO_USE_PACKED_ARGS);
return memo->v1;
}