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

Rails from "good" to fast - Colognerb

Rails from "good" to fast - Colognerb

Rails from "good" to fast. How to get the view rendering from ~600ms to ~60ms.
https://github.com/timoschilling/YetAnotherBlog
https://github.com/apotonick/cells
https://github.com/apotonick/representable

Timo Schilling

February 17, 2016
Tweet

More Decks by Timo Schilling

Other Decks in Programming

Transcript

  1. Rails from
    “good” to fast
    How to get the view rendering
    from ~600ms to ~60ms

    View Slide

  2. What’s the slowest
    part in Rails?
    • Request Handling (ActionDispatch)
    • NO!
    • Database Queries (ActiveRecord)
    • NO, just a bit!
    • Rendering (ActionView, HAML, JBuilder)
    • YES!

    View Slide

  3. Let’s build a Blog
    github.com/timoschilling/YetAnotherBlog
    Why ~5ms?

    View Slide

  4. Asset Overhead
    Prod
    Dev
    1 Asset
    Dev
    100 Assets

    View Slide

  5. Fix Asset Overhead
    How?
    Dev
    1 Asset
    Dev
    100 Assets

    View Slide

  6. Even Faster?

    View Slide

  7. The Code

    View Slide

  8. Render a Index with
    100 Items

    View Slide

  9. Side Note:

    Every Query is a lie!
    0.3ms + 0.4ms + 4.4ms +0.6ms = 5.7ms
    5.7ms != 6.1ms

    View Slide

  10. View Slide

  11. Why 5.7ms != 6.1ms?
    ActiveRecord show only the query time!
    Not the real amount of each operation!

    View Slide

  12. How to get a real value?
    Before:
    (0.3ms) SELECT COUNT(*)…

    Post Load (0.4ms) …

    User Load (0.4ms) …

    Comment Load (4.4ms) …

    User Load (0.6ms) …


    Completed in 568ms

    (Views: 561.9ms |

    ActiveRecord: 6.1ms)
    After:
    (0.3ms) SELECT COUNT(*)…

    Post Load (0.4ms) …

    User Load (0.4ms) …

    Comment Load (4.4ms) …

    User Load (0.6ms) …


    DB Queries: 78.85ms


    Completed in 568ms

    (Views: 489,1ms |

    ActiveRecord: 6.1ms)

    View Slide

  13. Hint!
    If you use Trailblazer:

    You will get this kind of measurement
    for free, in the future.

    View Slide

  14. How to make it
    faster?
    Cache!?

    View Slide

  15. Russian Doll Caching
    from
    570ms
    to
    677ms
    ???

    View Slide

  16. Russian Doll Caching
    It helps only if you have slow views!
    This view has the same render time as a view
    without `sleep` with Russian Doll Caching

    View Slide

  17. good old
    Rails.cache.fetch
    It’s fast!

    But now I need to manage the cache!

    :(
    85ms

    View Slide

  18. tl;dr
    Rails caching sucks!

    View Slide

  19. Again:
    How to make it faster?
    Don’t use ActionView!
    But how?
    Use Cells!

    View Slide

  20. Cells?
    A gem for ViewModel’s,

    written by this awesome guy:

    View Slide

  21. Demo

    View Slide

  22. Status:

    ~650ms -> ~150ms

    View Slide

  23. Where is the Time?
    • 150ms Request
    • DB: 70ms
    • ActiveRecord: 6.1ms
    • ActionView: 78.1ms
    • Cells: 71.4ms

    View Slide

  24. Next Step:
    remove index (action)view
    (demo)

    View Slide

  25. Where is the Time?
    • 145ms Request
    • DB: 70ms
    • ActiveRecord: 6.1ms
    • ActionView: 74.1ms
    • Cells: 71.4ms

    View Slide

  26. Next Step:
    remove layout (action)view
    (demo)

    View Slide

  27. Where is the Time?
    • 145ms Request
    • DB: 70ms
    • ActiveRecord: 6.1ms
    • Cells: 71.4ms

    View Slide

  28. How does tempting
    works?

    View Slide

  29. Why need that ~70ms?
    • 1 Layout
    • Index List
    • 100 Post
    • + 1 User
    • + 1 Comment
    • + 1 User
    • = 404 Renderings
    • 404 Renderings
    • each rendering
    needs 100 method
    call (theoretical
    assumption)

    View Slide

  30. 70ms vs. 4ms
    Where the time goes by?
    Tilt!

    View Slide

  31. What does Tilt do?
    • one time
    • compile the template
    • build a method
    • cache this method
    • per rendering
    • bind the method to the context
    • call the method
    • unbind the method from context
    • one time
    • compile the template
    • build a method
    • cache this method
    • per rendering
    • bind the method to the context
    • call the method
    • unbind the method from context

    View Slide

  32. Solution:
    Hand build tilt replacement

    View Slide

  33. Where is the Time?
    • 91ms Request
    • DB: 70ms
    • ActiveRecord: 6.1ms
    • Cells: 17.4ms

    View Slide

  34. ActiveSupport::Notifications
    • Nice
    • But slow
    • One event costs ~0.05ms

    View Slide

  35. Path helpers
    • post_path(model) # => f***ing slow
    • post_path(id: model.id) # => slow
    • “/posts/#{id}” # => fast
    • brings up to 4ms (50%)

    View Slide

  36. Cell::Mailer

    View Slide

  37. Real World App

    View Slide

  38. Jbuilder

    View Slide

  39. Faster? How?
    A gem JSON/XML Object Mapper,

    written by this awesome guy:
    Representable

    View Slide

  40. Representable

    View Slide

  41. Jbuilder

    vs.

    Representable
    from ~230ms to ~80ms

    View Slide

  42. Form’s
    SimpleForm

    View Slide

  43. Faster? How?
    A form builder gem,

    written by this awesome guy:
    Formular

    View Slide

  44. Formular
    SimpleForm 11ms
    vs.
    Formular 0.6ms

    View Slide

  45. Disclamer
    I’m an apotonick fanboy!

    View Slide

  46. The End

    View Slide

  47. Tanks!

    View Slide

  48. Questions?

    View Slide

  49. Links
    • github.com/timoschilling/YetAnotherBlog
    • github.com/apotonick/cells
    • github.com/apotonick/representable

    View Slide