1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/defs/id.def
Aaron Patterson 9faef3113f
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>
2019-07-31 16:21:25 -07:00

189 lines
4.3 KiB
Ruby

# -*- mode: ruby; coding: us-ascii -*-
firstline, predefined = __LINE__+1, %[\
max
min
freeze
nil?
inspect
intern
object_id
const_missing
method_missing MethodMissing
method_added
singleton_method_added
method_removed
singleton_method_removed
method_undefined
singleton_method_undefined
length
size
gets
succ
each
proc
lambda
send
__send__
__attached__
initialize
initialize_copy
initialize_clone
initialize_dup
to_int
to_ary
to_str
to_sym
to_hash
to_proc
to_io
to_a
to_s
to_i
to_f
to_r
bt
bt_locations
call
mesg
exception
not NOT
and AND
or OR
_ UScore
"/*NULL*/" NULL
empty?
eql?
respond_to? Respond_to
respond_to_missing? Respond_to_missing
<IFUNC>
<CFUNC>
core#set_method_alias
core#set_variable_alias
core#undef_method
core#define_method
core#define_singleton_method
core#set_postexe
core#hash_merge_ptr
core#hash_merge_kwd
core#raise
- debug#created_info
$_ LASTLINE
$~ BACKREF
$! ERROR_INFO
]
# VM ID OP Parser Token
token_ops = %[\
Dot2 .. DOT2
Dot3 ... DOT3
BDot2 .. BDOT2
BDot3 ... BDOT3
UPlus +@ UPLUS
UMinus -@ UMINUS
Pow ** POW
Cmp <=> CMP
PLUS +
MINUS -
MULT *
DIV /
MOD %
LTLT << LSHFT
GTGT >> RSHFT
LT <
LE <= LEQ
GT >
GE >= GEQ
Eq == EQ
Eqq === EQQ
Neq != NEQ
Not !
And &
Or |
Backquote `
EqTilde =~ MATCH
NeqTilde !~ NMATCH
AREF []
ASET []=
COLON2 ::
ANDOP &&
OROP ||
ANDDOT &.
METHREF .:
PIPE |>
]
class KeywordError < RuntimeError
def self.raise(mesg, line)
super(self, mesg, ["#{__FILE__}:#{line}", *caller])
end
end
predefined_ids = {}
preserved_ids = []
local_ids = []
instance_ids = []
global_ids = []
const_ids = []
class_ids = []
attrset_ids = []
token_op_ids = []
names = {}
predefined.split(/^/).each_with_index do |line, num|
next if /^#/ =~ line
line.sub!(/\s+#.*/, '')
name, token = line.split
next unless name
token ||= name
if /#/ =~ token
token = "_#{token.gsub(/\W+/, '_')}"
else
token = token.sub(/\?/, 'P').sub(/\A[a-z]/) {$&.upcase}
token.sub!(/\A\$/, "_G_")
token.sub!(/\A@@/, "_C_")
token.sub!(/\A@/, "_I_")
token.gsub!(/\W+/, "")
end
if name == '-'
preserved_ids << token
next
end
if prev = names[name]
KeywordError.raise("#{name} is already registered at line #{prev+firstline}", firstline+num)
end
if prev = predefined_ids[token]
KeywordError.raise("#{token} is already used for #{prev} at line #{names[prev]+firstline}", firstline+num)
end
names[name] = num
case name
when /\A[A-Z]\w*\z/; const_ids
when /\A(?!\d)\w+\z/; local_ids
when /\A\$(?:\d+|(?!\d)\w+|\W)\z/; global_ids
when /\A@@(?!\d)\w+\z/; class_ids
when /\A@(?!\d)\w+\z/; instance_ids
when /\A((?!\d)\w+)=\z/; attrset_ids
else preserved_ids
end << token
predefined_ids[token] = name
end
token_ops.split(/^/).each do |line|
next if /^#/ =~ line
line.sub!(/\s+#.*/, '')
id, op, token = line.split
next unless id and op
token ||= (id unless /\A\W\z/ =~ op)
token_op_ids << [id, op, token]
end
{
"LOCAL" => local_ids,
"INSTANCE" => instance_ids,
"GLOBAL" => global_ids,
"CONST" => const_ids,
"CLASS" => class_ids,
"ATTRSET" => attrset_ids,
:preserved => preserved_ids,
:predefined => predefined_ids,
:token_op => token_op_ids,
}