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.

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