From 182822683f86c8f8d63b05765addf5a04d112aa2 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Tue, 9 Jul 2024 18:30:03 +0900 Subject: [PATCH] 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.) --- enum.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/enum.c b/enum.c index dcb374778e..30f374ad7b 100644 --- a/enum.c +++ b/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; }