diff options
author | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-04 20:11:00 +0900 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-04 22:20:12 +0900 |
commit | 346aa557b31fe96760e505d30da26eb7a846bac9 (patch) | |
tree | 631a7f75231311ed7a21fcbdbf678e6246835bb6 | |
parent | 39622232c7542d062f79277a11f6b8b6b6cfd994 (diff) |
Make opt_eq and opt_neq insns leaf
# Benchmark zero?
```
require 'benchmark/ips'
Numeric.class_eval do
def ruby_zero?
self == 0
end
end
Benchmark.ips do |x|
x.report('0.zero?') { 0.ruby_zero? }
x.report('1.zero?') { 1.ruby_zero? }
x.compare!
end
```
## VM
No significant impact for VM.
### before
ruby 2.7.0dev (2019-08-04T02:56:02Z master 2d8c037e97) [x86_64-linux]
0.zero?: 21855445.5 i/s
1.zero?: 21770817.3 i/s - same-ish: difference falls within error
### after
ruby 2.7.0dev (2019-08-04T11:17:10Z opt-eq-leaf 6404bebd6a) [x86_64-linux]
1.zero?: 21958912.3 i/s
0.zero?: 21881625.9 i/s - same-ish: difference falls within error
## JIT
The performance improves about 1.23x.
### before
ruby 2.7.0dev (2019-08-04T02:56:02Z master 2d8c037e97) +JIT [x86_64-linux]
0.zero?: 36343111.6 i/s
1.zero?: 36295153.3 i/s - same-ish: difference falls within error
### after
ruby 2.7.0dev (2019-08-04T11:17:10Z opt-eq-leaf 6404bebd6a) +JIT [x86_64-linux]
0.zero?: 44740467.2 i/s
1.zero?: 44363616.1 i/s - same-ish: difference falls within error
# Benchmark str == str / str != str
```
# frozen_string_literal: true
require 'benchmark/ips'
Benchmark.ips do |x|
x.report('a == a') { 'a' == 'a' }
x.report('a == b') { 'a' == 'b' }
x.report('a != a') { 'a' != 'a' }
x.report('a != b') { 'a' != 'b' }
x.compare!
end
```
## VM
No significant impact for VM.
### before
ruby 2.7.0dev (2019-08-04T02:56:02Z master 2d8c037e97) [x86_64-linux]
a == a: 27286219.0 i/s
a != a: 24892389.5 i/s - 1.10x slower
a == b: 23623635.8 i/s - 1.16x slower
a != b: 21800958.0 i/s - 1.25x slower
### after
ruby 2.7.0dev (2019-08-04T11:17:10Z opt-eq-leaf 6404bebd6a) [x86_64-linux]
a == a: 27224016.2 i/s
a != a: 24490109.5 i/s - 1.11x slower
a == b: 23391052.4 i/s - 1.16x slower
a != b: 21811321.7 i/s - 1.25x slower
## JIT
The performance improves on JIT a little.
### before
ruby 2.7.0dev (2019-08-04T02:56:02Z master 2d8c037e97) +JIT [x86_64-linux]
a == a: 42010674.7 i/s
a != a: 38920311.2 i/s - same-ish: difference falls within error
a == b: 32574262.2 i/s - 1.29x slower
a != b: 32099790.3 i/s - 1.31x slower
### after
ruby 2.7.0dev (2019-08-04T11:17:10Z opt-eq-leaf 6404bebd6a) +JIT [x86_64-linux]
a == a: 46902738.8 i/s
a != a: 43097258.6 i/s - 1.09x slower
a == b: 35822018.4 i/s - 1.31x slower
a != b: 33377257.8 i/s - 1.41x slower
This is needed towards Bug#15589.
Closes: https://github.com/ruby/ruby/pull/2318
-rw-r--r-- | insns.def | 4 | ||||
-rw-r--r-- | internal.h | 16 | ||||
-rw-r--r-- | string.c | 20 | ||||
-rw-r--r-- | vm_insnhelper.c | 9 |
4 files changed, 25 insertions, 24 deletions
@@ -1161,7 +1161,7 @@ opt_eq /* This instruction can compare a string with non-string. This * (somewhat) coerces the non-string into a string, via a method * call. */ -// attr bool leaf = false; /* has rb_str_equal() */ +// attr bool leaf = true; { val = opt_eq_func(recv, obj, ci, cc); @@ -1177,7 +1177,7 @@ opt_neq (VALUE recv, VALUE obj) (VALUE val) /* Same discussion as opt_eq. */ -// attr bool leaf = false; /* has rb_str_equal() */ +// attr bool leaf = true; { val = vm_opt_neq(ci, cc, ci_eq, cc_eq, recv, obj); diff --git a/internal.h b/internal.h index 5ecb229413..d391597d54 100644 --- a/internal.h +++ b/internal.h @@ -2125,6 +2125,22 @@ VALUE rb_str_eql(VALUE str1, VALUE str2); VALUE rb_obj_as_string_result(VALUE str, VALUE obj); const char *ruby_escaped_char(int c); +/* expect tail call optimization */ +static inline VALUE +rb_str_eql_internal(const VALUE str1, const VALUE str2) +{ + const long len = RSTRING_LEN(str1); + const char *ptr1, *ptr2; + + if (len != RSTRING_LEN(str2)) return Qfalse; + if (!rb_str_comparable(str1, str2)) return Qfalse; + if ((ptr1 = RSTRING_PTR(str1)) == (ptr2 = RSTRING_PTR(str2))) + return Qtrue; + if (memcmp(ptr1, ptr2, len) == 0) + return Qtrue; + return Qfalse; +} + /* symbol.c */ #ifdef RUBY_ENCODING_H VALUE rb_sym_intern(const char *ptr, long len, rb_encoding *enc); @@ -3258,22 +3258,6 @@ rb_str_cmp(VALUE str1, VALUE str2) return -1; } -/* expect tail call optimization */ -static VALUE -str_eql(const VALUE str1, const VALUE str2) -{ - const long len = RSTRING_LEN(str1); - const char *ptr1, *ptr2; - - if (len != RSTRING_LEN(str2)) return Qfalse; - if (!rb_str_comparable(str1, str2)) return Qfalse; - if ((ptr1 = RSTRING_PTR(str1)) == (ptr2 = RSTRING_PTR(str2))) - return Qtrue; - if (memcmp(ptr1, ptr2, len) == 0) - return Qtrue; - return Qfalse; -} - /* * call-seq: * str == obj -> true or false @@ -3297,7 +3281,7 @@ rb_str_equal(VALUE str1, VALUE str2) } return rb_equal(str2, str1); } - return str_eql(str1, str2); + return rb_str_eql_internal(str1, str2); } /* @@ -3312,7 +3296,7 @@ rb_str_eql(VALUE str1, VALUE str2) { if (str1 == str2) return Qtrue; if (!RB_TYPE_P(str2, T_STRING)) return Qfalse; - return str_eql(str1, str2); + return rb_str_eql_internal(str1, str2); } /* diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 1875d35de1..e68ded7d15 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1506,10 +1506,11 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc) return rb_float_equal(recv, obj); } } - else if (BUILTIN_CLASS_P(recv, rb_cString)) { - if (EQ_UNREDEFINED_P(STRING)) { - return rb_str_equal(recv, obj); - } + else if (BUILTIN_CLASS_P(recv, rb_cString) && EQ_UNREDEFINED_P(STRING)) { + if (recv == obj) return Qtrue; + if (RB_TYPE_P(obj, T_STRING)) { + return rb_str_eql_internal(recv, obj); + } } fallback: |