Originally presented at Baruco 2014. Updated for RubyConf Portugal 2014.
Video here: https://www.youtube.com/watch?v=fGFM_UrSp70.
View Slide
Levels of OptimizationDesignSourceBuildCompileRuntime
Levels of OptimizationDesignSourceBuildCompileRuntimeArchitecture and algorithms (e.g. n + 1 queries)Writing fast RubySetting build flags (e.g. ./configure)mrbc, jrubyc, rbx compileThanks Matz & Koichi (e.g. RUBY_GC_MALLOC_LIMIT)
Benchmarkrequire ‘benchmark'n = 50Benchmark.bm do |x|x.report { n.times { fast } }x.report { n.times { slow } }end
Benchmarkrequire ‘benchmark'n = 50_000Benchmark.bm do |x|x.report { n.times { fast } }x.report { n.times { slow } }end
Benchmark IPSrequire 'benchmark/ips'Benchmark.ips do |x|x.report('fast') { fast }x.report('slow') { slow }end
GoalsSourceSignificantEasyHappyOptimize at the code levelAt least 12% improvementCode should be easier to readHigh quality Ruby
Proc#call versus yielddef slow(&block)block.callenddef fastyieldend
Resultsslow 950950.6 (±14.0%) i/sfast 5508226.3 (±15.5%) i/sOver 5X faster!
Proc#call versus yielddef slowProc.new.callenddef fastyieldend
Block versus Symbol#to_proc(1..100).map { |i| i.to_s }(1..100).map(&:to_s)
Resultsslow 47524.3 (±7.6%) i/sfast 56823.2 (±7.2%) i/s20% faster!
Enumerable#map and Array#flattenversus Enumerable#flat_mapenum.map do# do somethingend.flatten(1)enum.flat_map do# do somethingend
Resultsslow 12348.2 (±9.0%) i/sfast 56647.8 (±7.2%) i/sOver 4.5X faster!
Enumerable#reverse and Enumerable#each versus Enumerable#reverse_eachenum.reverse.each do# do somethingendenum.reverse_each do# do somethingend
Resultsslow 156173.2 (±9.2%) i/sfast 182859.3 (±7.8%) i/s17% faster!
Hash#keys and Enumerable#each versus Hash#each_keyhash.keys.each do |k|# do somethingendhash.each_key do |k|# do somethingend
Resultsslow 34702.1 (±9.8%) i/sfast 46103.3 (±8.1%) i/sOver 33% faster!
Array#shuffle and Array#first versus Array#samplearray.shuffle.first array.sample
Resultsslow 324806.7 (±8.1%) i/sfast 5069719.9 (±9.5%) i/sOver 15X faster!
Hash#merge versus Hash#merge!enum.inject({}) do |h, e|h.merge(e => e)endenum.inject({}) do |h, e|h.merge!(e => e)end
Resultsslow 33572.0 (±3.3%) i/sfast 106473.0 (±3.4%) i/sOver 3X faster!
Hash#merge! versus Hash#[]=enum.each_with_object({}) do |e, h|h.merge!(e => e)endenum.each_with_object({}) do |e, h|h[e] = eend
Resultsslow 99331.3 (±2.2%) i/sfast 217944.4 (±5.2%) i/sOver 2X faster!
Hash#fetch versus Hash#fetch with block{:ruby => :conf}.fetch(:ruby, (0..9).to_a){:ruby => :conf}.fetch(:ruby) { (0..9).to_a }
Resultsslow 712384.2 (±3.8%) i/sfast 1590417.4 (±4.1%) i/sOver 2X faster!
String#gsub versus String#sub‘http://rubyconf.pt/'.gsub(‘http://', 'https://')'http://rubyconf.pt/'.sub('http://', 'https://')
Resultsslow 404148.2 (±4.6%) i/sfast 602661.1 (±3.4%) i/s50% faster!
String#gsub versus String#tr'slug from title'.gsub(' ', '_')'slug from title'.tr(' ', '_')
Resultsslow 311878.2 (±3.5%) i/sfast 1573891.1 (±4.6%) i/sOver 5X faster!
Parallel versus sequential assignmenta, b = 1, 2 a = 1b = 2
Resultsslow 5821588.3 (±6.0%) i/sfast 8010420.3 (±5.5%) i/s40% faster!
Using exceptions for control flowbeginruby.confrescue NoMethodError'conf'endif ruby.respond_to?(:conf)ruby.confelse'conf'end
Resultsslow 328886.4 (±5.0%) i/sfast 3348327.9 (±9.0%) i/sOver 10X faster!
Using throw/catch for control flowbeginruby.confrescue NoMethodError'conf'endcatch(:ruby) doif ruby.respond_to?(:conf)ruby.confelsethrow(:ruby, ‘conf’)endend
Resultsslow 252474.2 (±8.4%) i/sfast 1411916.6 (±7.2%) i/sOver 5X faster!
The Future
Special ThanksAaron PattersonRuby Rogues ParleySam SaffronAman GuptaDon KnuthYukihiro MatsumotoKoichi SasadaRubyConf Portugal