From: mame@... Date: 2019-08-27T11:33:27+00:00 Subject: [ruby-core:94606] [Ruby master Feature#15902] Add a specialized instruction for `.nil?` Issue #15902 has been updated by mame (Yusuke Endoh). ned (Ned Hadzo) wrote: > This was reverted, right? https://github.com/ruby/ruby/commit/a0980f2446c0db735b8ffeb37e241370c458a626 It has been re-introduced. The Mojave issue was also fixed. ---------------------------------------- Feature #15902: Add a specialized instruction for `.nil?` https://bugs.ruby-lang.org/issues/15902#change-81083 * Author: tenderlovemaking (Aaron Patterson) * Status: Closed * Priority: Normal * Assignee: * Target version: ---------------------------------------- I'd like to add a specialized instruction for `.nil?`. We have specialized instructions for `.length` and `.empty?`, and surprisingly our application also calls `.nil?` a lot: ``` [aaron@TC ~/g/github (gc-boot-stats)]$ git grep '.empty?' | wc -l 2553 [aaron@TC ~/g/github (gc-boot-stats)]$ git grep '.length' | wc -l 3975 [aaron@TC ~/g/github (gc-boot-stats)]$ git grep '.nil?' | wc -l 3117 ``` I'm not sure how hot any of the `.nil?` callsites are, but I think this instruction will speed up most of them. I tried two benchmark runners: ## Benchmark/ips ``` 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 ``` ### Results 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 patch: ``` [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 ``` According to benchmark/ips, it's about 27% faster when the object is nil or a regular object. When it's an object that implements .nil?, I think it might be slower but it's hard to tell. ## Benchmark-driver I added a benchmark driver file: ``` yaml 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 ``` ### Results (tested against master @ c9b74f9fd95113df903fc34cc1d6ec3fb3160c85 ) ``` [aaron@TC ~/g/ruby (specialized-nilp)]$ make benchmark ARGS=benchmark/nil_p.yml ./revision.h unchanged /Users/aaron/.rbenv/shims/ruby --disable=gems -rrubygems -I./benchmark/lib ./benchmark/benchmark-driver/exe/benchmark-driver \ --executables="compare-ruby::/Users/aaron/.rbenv/shims/ruby --disable=gems -I.ext/common --disable-gem" \ --executables="built-ruby::./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems --disable-gem" \ benchmark/nil_p.yml Calculating ------------------------------------- compare-ruby built-ruby xnil.nil? 68.825M 405.121M i/s - 10.000M times in 0.145296s 0.024684s notnil.nil? 66.357M 267.874M i/s - 10.000M times in 0.150700s 0.037331s niller.nil? 110.273M 123.089M i/s - 10.000M times in 0.090684s 0.081242s Comparison: xnil.nil? built-ruby: 405120725.0 i/s compare-ruby: 68825019.2 i/s - 5.89x slower notnil.nil? built-ruby: 267873885.2 i/s compare-ruby: 66357000.6 i/s - 4.04x slower niller.nil? built-ruby: 123089042.6 i/s compare-ruby: 110273035.8 i/s - 1.12x slower [aaron@TC ~/g/ruby (specialized-nilp)]$ make benchmark ARGS=benchmark/nil_p.yml ./revision.h unchanged /Users/aaron/.rbenv/shims/ruby --disable=gems -rrubygems -I./benchmark/lib ./benchmark/benchmark-driver/exe/benchmark-driver \ --executables="compare-ruby::/Users/aaron/.rbenv/shims/ruby --disable=gems -I.ext/common --disable-gem" \ --executables="built-ruby::./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems --disable-gem" \ benchmark/nil_p.yml Calculating ------------------------------------- compare-ruby built-ruby xnil.nil? 45.083M 360.998M i/s - 10.000M times in 0.221811s 0.027701s notnil.nil? 69.558M 271.054M i/s - 10.000M times in 0.143765s 0.036893s niller.nil? 115.423M 79.667M i/s - 10.000M times in 0.086638s 0.125523s Comparison: xnil.nil? built-ruby: 360997801.1 i/s compare-ruby: 45083426.9 i/s - 8.01x slower notnil.nil? built-ruby: 271054130.3 i/s compare-ruby: 69557959.1 i/s - 3.90x slower niller.nil? compare-ruby: 115422793.6 i/s built-ruby: 79666674.5 i/s - 1.45x slower ``` I think there is too much noise for the third case. I'm not happy about making `rb_false` non-static, but I'm not sure how else to do this patch. What do you think? ---Files-------------------------------- 0001-Add-a-specialized-instruction-for-.nil-calls.patch (7.13 KB) -- https://bugs.ruby-lang.org/ Unsubscribe: