Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Write Simpler, Faster Rails Code

Write Simpler, Faster Rails Code

A presentation for Philly.rb (June, 2017) about writing simpler, faster Rails code.

Avatar for Ernesto Tagwerker

Ernesto Tagwerker

June 20, 2017
Tweet

More Decks by Ernesto Tagwerker

Other Decks in Programming

Transcript

  1. require 'benchmark/ips' ["foobar", nil, 89, 99.0].each do |object| puts "Testing

    with #{object.class}" Benchmark.ips do |x| x.report("unless nil") do true unless object.nil? end x.report("if object") do true if object end x.compare! end end
  2. String Testing with String Warming up -------------------------------------- unless nil 233.769k

    i/100ms if object 228.953k i/100ms Calculating ------------------------------------- unless nil 8.809M (± 4.9%) i/s - 43.949M in 5.001814s if object 10.089M (±11.7%) i/s - 49.225M in 5.008738s Comparison: if object: 10088708.4 i/s unless nil: 8808975.9 i/s - same-ish: difference falls within error
  3. NilClass Testing with NilClass Warming up -------------------------------------- unless nil 226.417k

    i/100ms if object 248.809k i/100ms Calculating ------------------------------------- unless nil 8.869M (± 7.2%) i/s - 44.151M in 5.012598s if object 10.753M (± 5.4%) i/s - 53.743M in 5.013131s Comparison: if object: 10753466.5 i/s unless nil: 8868727.3 i/s - 1.21x slower done
  4. Fixnum Testing with Fixnum Warming up -------------------------------------- unless nil 239.689k

    i/100ms if object 241.611k i/100ms Calculating ------------------------------------- unless nil 9.016M (± 4.9%) i/s - 45.062M in 5.009988s if object 10.604M (± 5.7%) i/s - 52.913M in 5.007991s Comparison: if object: 10603605.1 i/s unless nil: 9016116.6 i/s - 1.18x slower
  5. Float Testing with Float Warming up -------------------------------------- unless nil 237.572k

    i/100ms if object 238.965k i/100ms Calculating ------------------------------------- unless nil 8.881M (± 4.5%) i/s - 44.426M in 5.012954s if object 10.473M (± 6.9%) i/s - 52.094M in 5.000457s Comparison: if object: 10472909.6 i/s unless nil: 8881134.7 i/s - 1.18x slower
  6. # Slower, harder to read return true unless OBJECT.nil? #

    Faster, easier to read return true if OBJECT Example #1
  7. # Slower, harder to read ARRAY.map {|x| x * 2

    }.flatten # Faster, easier to read ARRAY.flat_map {|x| x * 2 } Example #2
  8. $ ruby benchmarks/map_flatten_vs_flat_map.rb Ruby version: 2.3.3 Warming up -------------------------------------- map-flatten

    1.926k i/100ms flat_map 2.529k i/100ms Calculating ------------------------------------- map-flatten 19.693k (± 2.3%) i/s - 100.152k in 5.088410s flat_map 25.774k (± 2.5%) i/s - 128.979k in 5.007299s Comparison: flat_map: 25774.3 i/s map-flatten: 19692.9 i/s - 1.31x slower
  9. $ bundle exec ruby benchmarks/time/parse_vs_at.rb Ruby version: 2.3.3 Warming up

    -------------------------------------- Time.parse 3.287k i/100ms Time.at 66.086k i/100ms Calculating ------------------------------------- Time.parse 34.052k (± 3.2%) i/s - 170.924k in 5.024737s Time.at 842.576k (± 2.2%) i/s - 4.230M in 5.022207s Comparison: Time.at: 842576.2 i/s Time.parse: 34051.9 i/s - 24.74x slower
  10. # Less Readable, Slightly Faster "yay" if !OBJECT.blank? # More

    Readable, Slightly Slower "yay" if OBJECT.present? Example #4
  11. String Testing with String Warming up -------------------------------------- !blank? 105.143k i/100ms

    present? 106.503k i/100ms Calculating ------------------------------------- !blank? 1.704M (± 5.5%) i/s - 8.517M in 5.015480s present? 1.623M (± 5.8%) i/s - 8.201M in 5.071236s Comparison: !blank?: 1703644.7 i/s present?: 1623253.4 i/s - same-ish: difference falls within error
  12. NilClass Testing with NilClass Warming up -------------------------------------- !blank? 214.770k i/100ms

    present? 209.608k i/100ms Calculating ------------------------------------- !blank? 7.903M (± 8.1%) i/s - 39.303M in 5.013230s present? 6.673M (±15.2%) i/s - 31.860M in 5.018127s Comparison: !blank?: 7902595.4 i/s present?: 6673257.2 i/s - same-ish: difference falls within error
  13. Fixnum Testing with Fixnum Warming up -------------------------------------- !blank? 204.972k i/100ms

    present? 212.165k i/100ms Calculating ------------------------------------- !blank? 8.525M (± 6.6%) i/s - 42.429M in 5.007297s present? 7.061M (± 4.5%) i/s - 35.432M in 5.028556s Comparison: !blank?: 8525338.3 i/s present?: 7061102.1 i/s - 1.21x slower
  14. Float Testing with Float Warming up -------------------------------------- !blank? 228.892k i/100ms

    present? 222.103k i/100ms Calculating ------------------------------------- !blank? 8.441M (± 4.4%) i/s - 42.345M in 5.026892s present? 7.228M (± 4.1%) i/s - 36.203M in 5.016829s Comparison: !blank?: 8440759.9 i/s present?: 7228480.9 i/s - 1.17x slower
  15. # Slower phone = Hash.new(number: number) phone[:number] # Faster Phone

    = Struct.new(:number) phone = Phone.new(number) phone.number Example #5
  16. $ bundle exec ruby benchmarks/struct_vs_hash.rb Ruby version: 2.3.3 Warming up

    -------------------------------------- struct 22.148k i/100ms hash 4.688k i/100ms Calculating ------------------------------------- struct 240.460k (± 2.7%) i/s - 1.218M in 5.069592s hash 47.926k (± 3.9%) i/s - 243.776k in 5.094413s Comparison: struct: 240459.9 i/s hash: 47926.1 i/s - 5.02x slower
  17. $ bundle exec rake benches:pluck_vs_map Ruby version: 2.3.3 Warming up

    -------------------------------------- map(&:id) 11.000 i/100ms pluck(:id) 80.000 i/100ms Calculating ------------------------------------- map(&:id) 124.205 (±11.3%) i/s - 616.000 in 5.026999s pluck(:id) 807.127 (± 2.0%) i/s - 4.080k in 5.057062s Comparison: pluck(:id): 807.1 i/s map(&:id): 124.2 i/s - 6.50x slower
  18. Lesson.select(:id).limit(10).map(&:id) Lesson Load (1.9ms) SELECT "lessons"."id" FROM "lessons" LIMIT $1

    [["LIMIT", 10]] vs. Lesson.limit(10).pluck(:id) (0.4ms) SELECT "lessons"."id" FROM "lessons" LIMIT $1 [["LIMIT", 10]]
  19. $ bundle exec rake benches:exists_vs_any Warming up -------------------------------------- present? 7.000

    i/100ms any? 98.000 i/100ms exists? 174.000 i/100ms Calculating ------------------------------------- present? 89.888 (±12.2%) i/s - 448.000 in 5.059417s any? 1.088k (±14.1%) i/s - 5.292k in 5.025290s exists? 1.817k (± 7.7%) i/s - 9.048k in 5.013826s Comparison: exists?: 1816.5 i/s any?: 1087.6 i/s - 1.67x slower present?: 89.9 i/s - 20.21x slower
  20. > Lesson.where(id: 2).present? Lesson Load (1.5ms) SELECT "lessons".* FROM "lessons"

    WHERE "lessons"."id" = $1 [["id", 2]] => false vs. > Lesson.where(id: 2).any? (0.4ms) SELECT COUNT(*) FROM "lessons" WHERE "lessons"."id" = $1 [["id", 2]] => false vs. > Lesson.where(id: 2).exists? Lesson Exists (0.4ms) SELECT 1 AS one FROM "lessons" WHERE "lessons"."id" = $1 LIMIT $2 [["id", 2], ["LIMIT", 1]] => false
  21. Benchmark.ips do |x| x.report("present?") do Post.where.not(title: nil).present? end x.report("any?") do

    Post.where.not(title: nil).any? end x.report("exists?") do Post.where.not(title: nil).exists? end x.compare! end
  22. # RSpec + benchmark/ips matchers describe "#all_hours_new" do let(:days) {

    Date::DAYNAMES } context "a venue is always closed" do it "performs adequately" do expect do subject.all_hours_new end.to iterate_per_second(190) end end end
  23. ombushop $ fasterer app/controllers/admin/base_controller.rb Using tr is faster than gsub

    when replacing a single character in a string with another single character. Occurred at lines: 141. ... spec/requests/steps/checkout_steps.rb Hash#fetch with second argument is slower than Hash#fetch with block. Occurred at lines: 13, 14, 20. 909 files inspected, 29 offenses detected
  24. # .fasterer.yml speedups: rescue_vs_respond_to: true module_eval: true shuffle_first_vs_sample: true for_loop_vs_each:

    true ... block_vs_symbol_to_proc: true proc_call_vs_yield: true gsub_vs_tr: true select_last_vs_reverse_detect: true getter_vs_attr_reader: true setter_vs_attr_writer: true exclude_paths: - 'db/schema.rb' Configuration