mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

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>
189 lines
4.3 KiB
Ruby
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,
|
|
}
|