aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2019-06-04 16:29:08 -0700
committerAaron Patterson <tenderlove@ruby-lang.org>2019-07-31 16:21:25 -0700
commit9faef3113fb4331524b81ba73005ba13fa0ef6c6 (patch)
treead589031c32b94c96737445617931a6a389c456b
parent117241b3c73f8f7f1bcfee34e9d627c41bb19cc9 (diff)
downloadruby-9faef3113fb4331524b81ba73005ba13fa0ef6c6.tar.gz
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 <kivikakk@github.com>
-rw-r--r--benchmark/nil_p.yml9
-rw-r--r--compile.c1
-rw-r--r--defs/id.def1
-rw-r--r--insns.def14
-rw-r--r--object.c2
-rw-r--r--vm.c1
-rw-r--r--vm_core.h1
-rw-r--r--vm_insnhelper.c20
8 files changed, 48 insertions, 1 deletions
diff --git a/benchmark/nil_p.yml b/benchmark/nil_p.yml
new file mode 100644
index 0000000000..79ba4f2177
--- /dev/null
+++ b/benchmark/nil_p.yml
@@ -0,0 +1,9 @@
+prelude: |
+ class Niller; def nil?; true; end; end
+ xnil, notnil = nil, Object.new
+ niller = Niller.new
+benchmark:
+ - xnil.nil?
+ - notnil.nil?
+ - niller.nil?
+loop_count: 10000000
diff --git a/compile.c b/compile.c
index 774dadfc79..b6d3cb140f 100644
--- a/compile.c
+++ b/compile.c
@@ -3255,6 +3255,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
case idLength: SP_INSN(length); return COMPILE_OK;
case idSize: SP_INSN(size); return COMPILE_OK;
case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
+ case idNilP: SP_INSN(nil_p); return COMPILE_OK;
case idSucc: SP_INSN(succ); return COMPILE_OK;
case idNot: SP_INSN(not); return COMPILE_OK;
}
diff --git a/defs/id.def b/defs/id.def
index 66dfdf915a..25677ffdc2 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -3,6 +3,7 @@ firstline, predefined = __LINE__+1, %[\
max
min
freeze
+ nil?
inspect
intern
object_id
diff --git a/insns.def b/insns.def
index c971026cf6..7c93af6e4d 100644
--- a/insns.def
+++ b/insns.def
@@ -808,6 +808,20 @@ opt_str_freeze
}
}
+/* optimized nil? */
+DEFINE_INSN
+opt_nil_p
+(CALL_INFO ci, CALL_CACHE cc)
+(VALUE recv)
+(VALUE val)
+{
+ val = vm_opt_nil_p(ci, cc, recv);
+
+ if (val == Qundef) {
+ CALL_SIMPLE_METHOD();
+ }
+}
+
DEFINE_INSN
opt_str_uminus
(VALUE str, CALL_INFO ci, CALL_CACHE cc)
diff --git a/object.c b/object.c
index 3aa7cd20d2..f31875f8c0 100644
--- a/object.c
+++ b/object.c
@@ -1687,7 +1687,7 @@ rb_true(VALUE obj)
*/
-static VALUE
+VALUE
rb_false(VALUE obj)
{
return Qfalse;
diff --git a/vm.c b/vm.c
index ca5df22315..0759ef2a01 100644
--- a/vm.c
+++ b/vm.c
@@ -1656,6 +1656,7 @@ vm_init_redefined_flag(void)
OP(Call, CALL), (C(Proc));
OP(And, AND), (C(Integer));
OP(Or, OR), (C(Integer));
+ OP(NilP, NIL_P), (C(NilClass));
#undef C
#undef OP
}
diff --git a/vm_core.h b/vm_core.h
index 0120f21831..d5e18ba489 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -550,6 +550,7 @@ enum ruby_basic_operators {
BOP_LENGTH,
BOP_SIZE,
BOP_EMPTY_P,
+ BOP_NIL_P,
BOP_SUCC,
BOP_GT,
BOP_GE,
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 57f6d91e88..1875d35de1 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -4228,6 +4228,26 @@ vm_opt_empty_p(VALUE recv)
}
}
+VALUE rb_false(VALUE obj);
+
+static VALUE
+vm_opt_nil_p(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
+{
+ if (recv == Qnil) {
+ if (BASIC_OP_UNREDEFINED_P(BOP_NIL_P, NIL_REDEFINED_OP_FLAG)) {
+ return Qtrue;
+ } else {
+ return Qundef;
+ }
+ } else {
+ if (vm_method_cfunc_is(ci, cc, recv, rb_false)) {
+ return Qfalse;
+ } else {
+ return Qundef;
+ }
+ }
+}
+
static VALUE
fix_succ(VALUE x)
{