Skip to content

Commit 069d0a6

Browse files
tenderloveAshe Connor
andcommitted
Add a specialized instruction for .nil? calls
This commit adds a specialized instruction for called to `.nil?`. It is about 27% faster than master in the case where the object is nil or not nil. In the case where an object implements `nil?`, I think it may be slightly slower. Here is a benchmark: ```ruby require "benchmark/ips" class Niller def nil?; true; end end not_nil = Object.new xnil = nil niller = Niller.new Benchmark.ips do |x| x.report("nil?") { xnil.nil? } x.report("not nil") { not_nil.nil? } x.report("niller") { niller.nil? } end ``` On Ruby master: ``` [aaron@TC ~/g/ruby (master)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 429.195k i/100ms not nil 437.889k i/100ms niller 437.935k i/100ms Calculating ------------------------------------- nil? 20.166M (± 8.1%) i/s - 100.002M in 5.002794s not nil 20.046M (± 7.6%) i/s - 99.839M in 5.020086s niller 22.467M (± 6.1%) i/s - 112.111M in 5.013817s [aaron@TC ~/g/ruby (master)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 449.660k i/100ms not nil 433.836k i/100ms niller 443.073k i/100ms Calculating ------------------------------------- nil? 19.997M (± 8.8%) i/s - 99.375M in 5.020458s not nil 20.529M (± 7.0%) i/s - 102.385M in 5.020689s niller 21.796M (± 8.0%) i/s - 108.110M in 5.002300s [aaron@TC ~/g/ruby (master)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 402.119k i/100ms not nil 438.968k i/100ms niller 398.226k i/100ms Calculating ------------------------------------- nil? 20.050M (±12.2%) i/s - 98.519M in 5.008817s not nil 20.614M (± 8.0%) i/s - 102.280M in 5.004531s niller 22.223M (± 8.8%) i/s - 110.309M in 5.013106s ``` On this branch: ``` [aaron@TC ~/g/ruby (specialized-nilp)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 468.371k i/100ms not nil 456.517k i/100ms niller 454.981k i/100ms Calculating ------------------------------------- nil? 27.849M (± 7.8%) i/s - 138.169M in 5.001730s not nil 26.417M (± 8.7%) i/s - 131.020M in 5.011674s niller 21.561M (± 7.5%) i/s - 107.376M in 5.018113s [aaron@TC ~/g/ruby (specialized-nilp)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 477.259k i/100ms not nil 428.712k i/100ms niller 446.109k i/100ms Calculating ------------------------------------- nil? 28.071M (± 7.3%) i/s - 139.837M in 5.016590s not nil 25.789M (±12.9%) i/s - 126.470M in 5.011144s niller 20.002M (±12.2%) i/s - 98.144M in 5.001737s [aaron@TC ~/g/ruby (specialized-nilp)]$ ./ruby compil.rb Warming up -------------------------------------- nil? 467.676k i/100ms not nil 445.791k i/100ms niller 415.024k i/100ms Calculating ------------------------------------- nil? 26.907M (± 8.0%) i/s - 133.755M in 5.013915s not nil 25.319M (± 7.9%) i/s - 125.713M in 5.007758s niller 19.569M (±11.8%) i/s - 96.286M in 5.008533s ``` Co-Authored-By: Ashe Connor <[email protected]>
1 parent c9b74f9 commit 069d0a6

File tree

8 files changed

+48
-1
lines changed

8 files changed

+48
-1
lines changed

benchmark/nil_p.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
prelude: |
2+
class Niller; def nil?; true; end; end
3+
xnil, notnil = nil, Object.new
4+
niller = Niller.new
5+
benchmark:
6+
- xnil.nil?
7+
- notnil.nil?
8+
- niller.nil?
9+
loop_count: 10000000

compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
32513251
case idLength: SP_INSN(length); return COMPILE_OK;
32523252
case idSize: SP_INSN(size); return COMPILE_OK;
32533253
case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
3254+
case idNilP: SP_INSN(nil_p); return COMPILE_OK;
32543255
case idSucc: SP_INSN(succ); return COMPILE_OK;
32553256
case idNot: SP_INSN(not); return COMPILE_OK;
32563257
}

defs/id.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ firstline, predefined = __LINE__+1, %[\
33
max
44
min
55
freeze
6+
nil?
67
inspect
78
intern
89
object_id

insns.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,20 @@ opt_str_freeze
808808
}
809809
}
810810

811+
/* optimized nil? */
812+
DEFINE_INSN
813+
opt_nil_p
814+
(CALL_INFO ci, CALL_CACHE cc)
815+
(VALUE recv)
816+
(VALUE val)
817+
{
818+
val = vm_opt_nil_p(ci, cc, recv);
819+
820+
if (val == Qundef) {
821+
CALL_SIMPLE_METHOD();
822+
}
823+
}
824+
811825
DEFINE_INSN
812826
opt_str_uminus
813827
(VALUE str, CALL_INFO ci, CALL_CACHE cc)

object.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1664,7 +1664,7 @@ rb_true(VALUE obj)
16641664
*/
16651665

16661666

1667-
static VALUE
1667+
VALUE
16681668
rb_false(VALUE obj)
16691669
{
16701670
return Qfalse;

vm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,7 @@ vm_init_redefined_flag(void)
16571657
OP(Call, CALL), (C(Proc));
16581658
OP(And, AND), (C(Integer));
16591659
OP(Or, OR), (C(Integer));
1660+
OP(NilP, NIL_P), (C(NilClass));
16601661
#undef C
16611662
#undef OP
16621663
}

vm_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ enum ruby_basic_operators {
547547
BOP_LENGTH,
548548
BOP_SIZE,
549549
BOP_EMPTY_P,
550+
BOP_NIL_P,
550551
BOP_SUCC,
551552
BOP_GT,
552553
BOP_GE,

vm_insnhelper.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4239,6 +4239,26 @@ vm_opt_empty_p(VALUE recv)
42394239
}
42404240
}
42414241

4242+
VALUE rb_false(VALUE obj);
4243+
4244+
static VALUE
4245+
vm_opt_nil_p(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
4246+
{
4247+
if (recv == Qnil) {
4248+
if (BASIC_OP_UNREDEFINED_P(BOP_NIL_P, NIL_REDEFINED_OP_FLAG)) {
4249+
return Qtrue;
4250+
} else {
4251+
return Qundef;
4252+
}
4253+
} else {
4254+
if (vm_method_cfunc_is(ci, cc, recv, rb_false)) {
4255+
return Qfalse;
4256+
} else {
4257+
return Qundef;
4258+
}
4259+
}
4260+
}
4261+
42424262
static VALUE
42434263
fix_succ(VALUE x)
42444264
{

0 commit comments

Comments
 (0)