Rb_yield $B$N9bB.HG(B

e$B%o%J%Y$H?=$7$^$9!#e(B

rb_yield
e$B$rO"B3$G8F$S=P$9$H$-$K!"@Q$s$@%U%l!<%`$r;H$$$^$o$94X?t$r=q$-$^$7$?!#e(B
e$B;n83E*$Ke(B Fixnum#times e$B$K$@$1AH$_9~$s$G$$$^$9$,!"$[$+$K$be(B
Array#each, Range#each e$B$J$I$G;H$($k$H;W$$$^$9!#e(B

ruby -e ‘GC.disable;t=Time.now;(10**7).times {};p Time.now - t’
e$B$3$s$J46$8$N4JC1$J7WB,$G$Oe(B 4.9e$BIC$+$ie(B 3.5
e$BIC$[$I$K=L$^$j$^$7$?!#e(B
e$B0l1~<j85$G$Oe(B make test e$B$He(B make test-all
e$B$NF0:n$r3NG’$7$F$"$j$^$9!#e(B

Index: eval.c

— eval.c (revision 16218)
+++ eval.c (working copy)
@@ -960,6 +960,24 @@
return v;
}

+rb_block_t *vm_yield_fast_setup(rb_thread_t th, int argc); / vm.c */
+void vm_yield_fast_finish(rb_thread_t *th, rb_block_t block); / vm.c
*/
+
+VALUE
+rb_yield_fast(VALUE (*i_proc)(ANYARGS), VALUE val)
+{

  • rb_thread_t *th = GET_THREAD();
  • rb_block_t *block = vm_yield_fast_setup(th, 1);
  • i_proc(val, (void*)block);
  • vm_yield_fast_finish(th, block);
    +}

+VALUE
+rb_yield_fast_call(VALUE val, void *block)
+{

  • return vm_yield_fast(GET_THREAD(), (rb_block_t *)block, 1, &val);
    +}

static VALUE
loop_i()
{
Index: numeric.c

— numeric.c (revision 16218)
+++ numeric.c (working copy)
@@ -3016,17 +3016,23 @@
*/

static VALUE
+int_dotimes_i(VALUE num, void *block)
+{

  • long i, end;
  • end = FIX2LONG(num);
  • for (i=0; i<end; i++) {
  • rb_yield_fast_call(LONG2FIX(i), block);
  • }
    +}

+static VALUE
int_dotimes(VALUE num)
{
RETURN_ENUMERATOR(num, 0, 0);

 if (FIXNUM_P(num)) {
  • long i, end;
  • end = FIX2LONG(num);
  • for (i=0; i<end; i++) {
  •  rb_yield(LONG2FIX(i));
    
  • }
  • rb_yield_fast(int_dotimes_i, num);
    }
    else {
    VALUE i = INT2FIX(0);
    Index: vm.c
    ===================================================================
    — vm.c (revision 16218)
    +++ vm.c (working copy)
    @@ -541,11 +541,10 @@

/* C -> Ruby: block */

-static VALUE
-invoke_block(rb_thread_t *th, rb_block_t *block, VALUE self,
+static inline void
+setup_block(rb_thread_t *th, rb_block_t *block, VALUE self,
int argc, VALUE *argv, rb_block_t *blockptr)
{

  • VALUE val;
    if (BUILTIN_TYPE(block->iseq) != T_NODE) {
    rb_iseq_t *iseq = block->iseq;
    rb_control_frame_t *cfp = th->cfp;
    @@ -557,8 +556,14 @@

CHECK_STACK_OVERFLOW(cfp, argc + iseq->stack_max);

  • for (i=0; i<argc; i++) {
  •  cfp->sp[i] = argv[i];
    
  • if (argv) {

  •  for (i=0; i<argc; i++) {
    
  • cfp->sp[i] = argv[i];

  •  }
    
  • } else {

  •  for (i=0; i<argc; i++) {
    
  • cfp->sp[i] = Qnil;

  •  }
    

    }

    opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
    @@ -568,7 +573,15 @@
    self, GC_GUARDED_PTR(block->dfp),
    iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
    iseq->local_size - arg_size);

  • }
    +}

+static inline VALUE
+invoke_block_fast(rb_thread_t *th, rb_block_t *block, VALUE self,

  •   int argc, VALUE *argv, rb_block_t *blockptr)
    

+{

  • VALUE val;
  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
    val = vm_eval_body(th);
    }
    else {
    @@ -577,19 +590,85 @@
    return val;
    }

-VALUE
-vm_yield(rb_thread_t *th, int argc, VALUE *argv)
+static VALUE
+invoke_block(rb_thread_t *th, rb_block_t *block, VALUE self,

  •   int argc, VALUE *argv, rb_block_t *blockptr)
    

{

  • rb_block_t *block = GC_GUARDED_PTR_REF(th->cfp->lfp[0]);
  • VALUE val;
  • setup_block(th, block, self, argc, argv, blockptr);
  • return invoke_block_fast(th, block, self, argc, argv, blockptr);
    +}

+static inline rb_block_t *
+vm_get_block(rb_control_frame_t *cfp)
+{

  • rb_block_t *block = GC_GUARDED_PTR_REF(cfp->lfp[0]);
  • if (block == 0) {
    vm_localjump_error(“no block given”, Qnil, 0);
    }
  • return block;
    +}

+VALUE
+vm_yield(rb_thread_t *th, int argc, VALUE *argv)
+{

  • rb_block_t *block = vm_get_block(th->cfp);
    return invoke_block(th, block, block->self, argc, argv, 0);
    }

+rb_block_t *
+vm_yield_fast_setup(rb_thread_t *th, int argc)
+{

  • rb_block_t *block = vm_get_block(th->cfp);
  • setup_block(th, block, block->self, argc, 0, 0);
  • return block;
    +}

VALUE
+vm_yield_fast(rb_thread_t *th, rb_block_t *block, int argc, VALUE
*argv)
+{

  • int i;
  • int opt_pc;
  • VALUE retval;
  • VALUE pre_dfp;
  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • CHECK_STACK_OVERFLOW(th->cfp, argc + block->iseq->stack_max);
  • for (i=0; i<argc; i++) {
  •  th->cfp->sp[i] = argv[i];
    
  • }
  • opt_pc = vm_yield_setup_args(th, block->iseq, argc, th->cfp->sp, 0,
  •      block_proc_is_lambda(block->proc));
    
  • for (i=0; iiseq->arg_size; i++) {
  •  th->cfp[2].sp[i] = th->cfp->sp[i];
    
  • }
  • th->cfp->pc = block->iseq->iseq_encoded + opt_pc;
  • th->cfp->dfp[0] = GC_GUARDED_PTR(block->dfp);
  • }
  • retval = invoke_block_fast(th, block, block->self, argc, argv, 0);
  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • for (i=0; iiseq->local_size; i++) {
  •  th->cfp->sp[i] = Qnil;
    
  • }
  • th->cfp–;
  • th->cfp->pc–;
  • th->cfp–;
  • }
  • return retval;
    +}

+void
+vm_yield_fast_finish(rb_thread_t *th, rb_block_t *block)
+{

  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • th->cfp++;
  • th->cfp->pc++;
  • th->cfp++;
  • }
    +}

+VALUE
vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc,
VALUE self, int argc, VALUE *argv, rb_block_t *blockptr)
{

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:34560] rb_yield e$B$N9bB.HGe(B”
on Thu, 1 May 2008 19:27:02 +0900, wanabe [email protected]
writes:

|rb_yield e$B$rO"B3$G8F$S=P$9$H$-$K!“@Q$s$@%U%l!<%`$r;H$$$^$o$94X?t$r=q$-$^$7$?!#e(B
|e$B;n83E*$Ke(B Fixnum#times e$B$K$@$1AH$_9~$s$G$$$^$9$,!”$[$+$K$be(B
|Array#each, Range#each e$B$J$I$G;H$($k$H;W$$$^$9!#e(B
|
|ruby -e ‘GC.disable;t=Time.now;(10**7).times {};p Time.now - t’
|e$B$3$s$J46$8$N4JC1$J7WB,$G$Oe(B 4.9e$BIC$+$ie(B 3.5 e$BIC$[$I$K=L$^$j$^$7$?!#e(B
|e$B0l1~<j85$G$Oe(B make test e$B$He(B make test-all e$B$NF0:n$r3NG’$7$F$"$j$^$9!#e(B

e$BHs>o$K6=L#?<$$$N$G$9$,!“$5$5$@$/$s$O%V%m%C%/$K4X$7$FJL$N9M$(e(B
e$B$,$”$k$+$b$7$l$^$;$s!#H`$,;?@.$7$?$i<h$j9~$_$^$7$g$&!#e(B

e$B!!$5$5$@$G$9!#e(B

Yukihiro M. wrote:

|rb_yield e$B$rO"B3$G8F$S=P$9$H$-$K!"@Q$s$@%U%l!<%`$r;H$$$^$o$94X?t$r=q$-$^$7$?!#e(B
|e$B;n83E*$Ke(B Fixnum#times e$B$K$@$1AH$_9~$s$G$$$^$9$,!"$[$+$K$be(B
|Array#each, Range#each e$B$J$I$G;H$($k$H;W$$$^$9!#e(B
|
|ruby -e ‘GC.disable;t=Time.now;(10**7).times {};p Time.now - t’
|e$B$3$s$J46$8$N4JC1$J7WB,$G$Oe(B 4.9e$BIC$+$ie(B 3.5 e$BIC$[$I$K=L$^$j$^$7$?!#e(B
|e$B0l1~<j85$G$Oe(B make test e$B$He(B make test-all e$B$NF0:n$r3NG’$7$F$"$j$^$9!#e(B

e$BHs>o$K6=L#?<$$$N$G$9$,!"$5$5$@$/$s$O%V%m%C%/$K4X$7$FJL$N9M$(e(B
e$B$,$"$k$+$b$7$l$^$;$s!#H`$,;?@.$7$?$i<h$j9~$_$^$7$g$&!#e(B

e$B!!@N!"$3$&$$$&5!G=$rF~$l$F$$$^$7$?!#$,!“e(Bblock inlining (BI)
e$B$N$[$&$,e(B
e$B$$$$$@$m$&!”$H;W$C$F;W$$@Z$C$F:o$C$?$s$G$9$,!“e(BBI
e$B$r<BAu$9$k$N$O?’!9e(B
e$B$HFq$7$$$N$G$A$g$C$H8=<BE*$8$c$J$$$J$!!”$H;W$$D>$7$F$-$^$7$?!Je(BBIe$B$N;De(B
e$B3<$O;D$C$F$$$^$9$,!"B?J,$^$H$b$KF0$-$^$;$s!K!#e(B

e$B!!$H$$$&$o$1$G!"$d$C$Q$j$3$&$$$&;EAH$$,NI$$$N$G$O$J$$$+!"$H$$$&0U8+e(B
e$B$G$9!#J#;(EY$O>e$,$k$N$G$9$,!"$=$l$G$H$/$K$^$D$b$H$5$s$,LdBj$J$$$N$Ge(B
e$B$7$?$i!"%=!<%9$r8+$F$+$i!"%3%
%C%H$5$;$F$$$?$@$-$^$9!#e(B

e$B!!5$$K$J$C$?E@$H$7$F$O!“e(Bcfp e$B$rD>@\e(B ++
e$B$J$I$GA`:n$9$k$N$O$^$:$$$+$Je(B
e$B!J%^%/%m$r;H$C$F$/$@$5$$!K$H$+!”$=$N$"$?$j$G$9!#$"$H!“e(BRuby
e$B$K$O7y$Je(B
e$BNc30$,Bt;3$”$k$N$G!"$=$N$"$?$j$r3NG’$5$;$F$/$@$5$$!#e(B

e$B!!$A$J$_$K!"%Y%s%A%^!<%/$Oe(B make benchmark
e$B$G3NG’$G$-$^$9!#$4MxMQ$/$@e(B
e$B$5$$!#e(B

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:34560] rb_yield e$B$N9bB.HGe(B”
on Thu, 1 May 2008 19:27:02 +0900, wanabe [email protected]
writes:
|
|e$B%o%J%Y$H?=$7$^$9!#e(B
|
|rb_yield e$B$rO"B3$G8F$S=P$9$H$-$K!“@Q$s$@%U%l!<%`$r;H$$$^$o$94X?t$r=q$-$^$7$?!#e(B
|e$B;n83E*$Ke(B Fixnum#times e$B$K$@$1AH$_9~$s$G$$$^$9$,!”$[$+$K$be(B
|Array#each, Range#each e$B$J$I$G;H$($k$H;W$$$^$9!#e(B
|
|ruby -e ‘GC.disable;t=Time.now;(10**7).times {};p Time.now - t’
|e$B$3$s$J46$8$N4JC1$J7WB,$G$Oe(B 4.9e$BIC$+$ie(B 3.5 e$BIC$[$I$K=L$^$j$^$7$?!#e(B
|e$B0l1~<j85$G$Oe(B make test e$B$He(B make test-all e$B$NF0:n$r3NG’$7$F$"$j$^$9!#e(B

e$B$3$l$r<h$j9~$s$G$$h$&$H:n6H$7$F$$^$7$?!#7kO@$+$i$$$&$He(B

  • e$BA4A3B.$/$J$i$J$+$C$?e(B
  • e$B$7$+$be(Btest-alle$B$GMn$A$ke(B

e$B$H$$$&$3$H$G!“A4A3%@%a$G$9!#$3$l$,!“e(B5e$B7n$+$i:#$^$G$Ke(Bcoree$B$KF~$Ce(B
e$B$?2~A1$N$?$aM-8z$J:GE,2=$G$J$/$J$C$?$N$+!”$=$l$H$b;d$N:n$C$?e(B
e$B%Q%C%A$KITHw$,$”$k$N$+$O$^$@J,$+$j$^$;$s!#$^$?!“e(Btest-alle$B$GMne(B
e$B$A$k860x$b$^$@J,$+$j$^$;$s!#<+J,$N%@%a$52C8:$,<B46$G$-$FHa$7e(B
e$B$$$N$G$9$,!”$;$C$+$/:n6H$7$?$N$G!"8=>u$N%Q%C%A$rEj9F$7$F$*$-e(B
e$B$^$9!#e(B

diff --git a/array.c b/array.c
index e7eb280…a91fcb1 100644
— a/array.c
+++ b/array.c
@@ -19,6 +19,9 @@ VALUE rb_cArray;

static ID id_cmp;

+VALUE rb_fast_yield_loop(VALUE (*i_proc)(ANYARGS), VALUE val);
+VALUE rb_fast_yield(VALUE val, void *block);
+
#define ARY_DEFAULT_SIZE 16
#define ARY_MAX_SIZE (LONG_MAX / sizeof(VALUE))

@@ -1116,6 +1119,17 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
return ary;
}

+static VALUE
+each_i(VALUE ary, void *block)
+{

  • long i;
  • for (i=0; i<RARRAY_LEN(ary); i++) {
  • rb_fast_yield(RARRAY_PTR(ary)[i], block);
  • }
  • return Qnil;
    +}

/*

  • call-seq:
  • array.each {|item| block }   ->   array
    

@@ -1134,12 +1148,8 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
VALUE
rb_ary_each(VALUE ary)
{

  • long i;
  • RETURN_ENUMERATOR(ary, 0, 0);
  • for (i=0; i<RARRAY_LEN(ary); i++) {
  • rb_yield(RARRAY_PTR(ary)[i]);
  • }
  • rb_fast_yield_loop(each_i, ary);
    return ary;
    }

diff --git a/eval.c b/eval.c
index b39da54…fd08d0a 100644
— a/eval.c
+++ b/eval.c
@@ -950,6 +950,32 @@ rb_obj_extend(int argc, VALUE *argv, VALUE obj)

  • in each module to the receiver.
    */

+const rb_block_t *vm_yield_fast_setup(rb_thread_t th, int argc); /
vm.c */
+void vm_yield_fast_finish(rb_thread_t *th, const rb_block_t block); /
vm.c */
+VALUE vm_yield_fast(rb_thread_t *th, rb_block_t *block, int argc, VALUE
argv); / vm.c */
+
+VALUE
+rb_fast_yield_loop(VALUE (*i_proc)(ANYARGS), VALUE val)
+{

  • rb_thread_t *th = GET_THREAD();
  • const rb_block_t *block = vm_yield_fast_setup(th, 1);
  • val = i_proc(val, (void*)block);
  • vm_yield_fast_finish(th, block);
  • return val;
    +}

+VALUE
+rb_fast_yield(VALUE val, void *block)
+{

  • return vm_yield_fast(GET_THREAD(), (rb_block_t *)block, 1, &val);
    +}

+VALUE
+rb_fast_yield_argv(int argc, VALUE *argv, void *block)
+{

  • return vm_yield_fast(GET_THREAD(), (rb_block_t *)block, argc,
    argv);
    +}

static VALUE
top_include(int argc, VALUE *argv, VALUE self)
{
diff --git a/numeric.c b/numeric.c
index 745a217…fbd6498 100644
— a/numeric.c
+++ b/numeric.c
@@ -80,6 +80,9 @@ round(double x)
}
#endif

+VALUE rb_fast_yield_loop(VALUE (*i_proc)(ANYARGS), VALUE val);
+VALUE rb_fast_yield(VALUE val, void *block);
+
static ID id_coerce, id_to_i, id_eq;

VALUE rb_cNumeric;
@@ -2992,17 +2995,24 @@ int_downto(VALUE from, VALUE to)
*/

static VALUE
+int_dotimes_i(VALUE num, void *block)
+{

  • long i, end;
  • end = FIX2LONG(num);
  • for (i=0; i<end; i++) {
  • rb_fast_yield(LONG2FIX(i), block);
  • }
  • return Qnil;
    +}

+static VALUE
int_dotimes(VALUE num)
{
RETURN_ENUMERATOR(num, 0, 0);

 if (FIXNUM_P(num)) {
  • long i, end;
  • end = FIX2LONG(num);
  • for (i=0; i<end; i++) {
  •  rb_yield(LONG2FIX(i));
    
  • }
  • rb_fast_yield_loop(int_dotimes_i, num);
    }
    else {
    VALUE i = INT2FIX(0);
    diff --git a/vm.c b/vm.c
    index f06ba0e…6942462 100644
    — a/vm.c
    +++ b/vm.c
    @@ -437,10 +437,10 @@ vm_make_proc(rb_thread_t *th,

/* C → Ruby: block */

-static inline VALUE
-invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,
+static inline void
+setup_block(rb_thread_t *th, const rb_block_t *block,
VALUE self, int argc, const VALUE *argv,

  •    const rb_block_t *blockptr, const NODE *cref)
    
  •    const rb_block_t *blockptr)
    

{
if (BUILTIN_TYPE(block->iseq) != T_NODE) {
const rb_iseq_t *iseq = block->iseq;
@@ -453,8 +453,14 @@ invoke_block_from_c(rb_thread_t *th, const
rb_block_t *block,

CHECK_STACK_OVERFLOW(cfp, argc + iseq->stack_max);

  • for (i=0; i<argc; i++) {
  •  cfp->sp[i] = argv[i];
    
  • if (argv) {

  •  for (i=0; i<argc; i++) {
    
  • cfp->sp[i] = argv[i];

  •  }
    
  • } else {

  •  for (i=0; i<argc; i++) {
    
  • cfp->sp[i] = Qnil;

  •  }
    

    }

    opt_pc = vm_yield_setup_args(th, iseq, argc, cfp->sp, blockptr,
    @@ -464,11 +470,18 @@ invoke_block_from_c(rb_thread_t *th, const
    rb_block_t *block,
    self, GC_GUARDED_PTR(block->dfp),
    iseq->iseq_encoded + opt_pc, cfp->sp + arg_size, block->lfp,
    iseq->local_size - arg_size);

  • }
    +}

+static inline VALUE
+invoke_block_fast(rb_thread_t *th, const rb_block_t *block,

  •  VALUE self, int argc, const VALUE *argv,
    
  •  const rb_block_t *blockptr, const NODE *cref)
    

+{

  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
    if (cref) {
    th->cfp->dfp[-1] = (VALUE)cref;
    }
  • return vm_eval_body(th);
    }
    else {
    @@ -476,6 +489,16 @@ invoke_block_from_c(rb_thread_t *th, const
    rb_block_t *block,
    }
    }

+static inline VALUE
+invoke_block_from_c(rb_thread_t *th, const rb_block_t *block,

  •    VALUE self, int argc, const VALUE *argv,
    
  •    const rb_block_t *blockptr, const NODE *cref)
    

+{

  • setup_block(th, block, self, argc, argv, blockptr);
  • return invoke_block_fast(th, block, self, argc, argv, blockptr,
    cref);
    +}

static inline const rb_block_t *
check_block(rb_thread_t *th)
{
@@ -502,6 +525,56 @@ vm_yield(rb_thread_t *th, int argc, const VALUE
*argv)
return invoke_block_from_c(th, blockptr, blockptr->self, argc,
argv, 0, 0);
}

+const rb_block_t *
+vm_yield_fast_setup(rb_thread_t *th, int argc)
+{

  • const rb_block_t *block = check_block(th);
  • setup_block(th, block, block->self, argc, 0, 0);
  • return block;
    +}

+VALUE
+vm_yield_fast(rb_thread_t *th, rb_block_t *block, int argc, VALUE
*argv)
+{

  • int i;
  • int opt_pc;
  • VALUE retval;
  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • CHECK_STACK_OVERFLOW(th->cfp, argc + block->iseq->stack_max);
  • for (i=0; i<argc; i++) {
  •  th->cfp->sp[i] = argv[i];
    
  • }
  • opt_pc = vm_yield_setup_args(th, block->iseq, argc, th->cfp->sp, 0,
  •         block_proc_is_lambda(block->proc));
    
  • for (i=0; iiseq->arg_size; i++) {
  •  th->cfp[2].sp[i] = th->cfp->sp[i];
    
  • }
  • th->cfp->pc = block->iseq->iseq_encoded + opt_pc;
  • th->cfp->dfp[0] = GC_GUARDED_PTR(block->dfp);
  • }
  • retval = invoke_block_fast(th, block, block->self, argc, argv, 0,
    0);
  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • for (i=0; iiseq->local_size; i++) {
  •  th->cfp->sp[i] = Qnil;
    
  • }
  • th->cfp–;
  • th->cfp->pc–;
  • th->cfp–;
  • }
  • return retval;
    +}

+void
+vm_yield_fast_finish(rb_thread_t *th, const rb_block_t *block)
+{

  • if (BUILTIN_TYPE(block->iseq) != T_NODE) {
  • th->cfp++;
  • th->cfp->pc++;
  • th->cfp++;
  • }
    +}

VALUE
vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self,
int argc, const VALUE *argv, rb_block_t * blockptr)
diff --git a/vm_eval.c b/vm_eval.c
index 1de5371…1c318e6 100644
— a/vm_eval.c
+++ b/vm_eval.c
@@ -535,15 +535,25 @@ rb_yield_splat(VALUE values)
return v;
}

+VALUE rb_fast_yield_loop(VALUE (*i_proc)(ANYARGS), VALUE val);
+VALUE rb_fast_yield_argv(int argc, VALUE *argv, void *block);
+
static VALUE
-loop_i(void)
+loop_ii(VALUE dummy, void *block)
{
for (;:wink: {

  • rb_yield_0(0, 0);
  • rb_fast_yield_argv(0, 0, block);
    }
    return Qnil;
    }

+static VALUE
+loop_i(void)
+{

  • rb_fast_yield_loop(loop_ii, Qnil);
  • return Qnil;
    +}

/*

  • call-seq:
  • loop {|| block }