$30 off During Our Annual Pro Sale. View Details »

GraphQL Ruby をちょっとだけ速くした / Make graphql-ruby faster a bit

GraphQL Ruby をちょっとだけ速くした / Make graphql-ruby faster a bit

Kaigi on Rails new LT 発表資料

https://kaigionrails.doorkeeper.jp/events/109773

Fumiaki MATSUSHIMA

July 25, 2020
Tweet

More Decks by Fumiaki MATSUSHIMA

Other Decks in Programming

Transcript

  1. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    GraphQL Ruby を
    ちょっとだけ速くした
    @mtsmfm
    Fumiaki Matsushima
    Kaigi on Rails new #kaigionrails

    View Slide

  2. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    ➔ Web Dev at Quipper
    ➔ Dead by Daylight 仲間募集
    ➔ 西日暮里.rb 主催
    ➔ GraphQL Tokyo 主催
    @mtsmfm.inspect

    View Slide

  3. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    https://nishinipporirb.doorkeeper.jp/events/108780

    View Slide

  4. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    https://www.meetup.com/ja-JP/GraphQL-Tokyo/

    View Slide

  5. #kaigionrails GraphQL Ruby をちょっとだけ速くした 5
    GraphQL とは
    - POST /graphql {“query”: “{ articles { title }”}
    したら
    {“data”: {“articles”: [{“title”: “Hi”}]}}
    を返すやつ

    View Slide

  6. #kaigionrails GraphQL Ruby をちょっとだけ速くした 6
    社内でいくつか GraphQL を導入
    - あれ
    - これ
    - それ

    View Slide

  7. #kaigionrails GraphQL Ruby をちょっとだけ速くした 7
    ある日鳴る SLO アラート
    - そのサービスは 90 % 300 ms
    - 社内サービスなので顧客に約束しているわけではない
    が放置は NG
    - 遅い GraphQL クエリがあった

    View Slide

  8. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    - これでも 200ms 弱くらい
    遅いクエリ
    8
    ※リソース名などは実際と異なります

    View Slide

  9. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    - これすら 70ms 弱
    遅いクエリ
    9

    View Slide

  10. #kaigionrails GraphQL Ruby をちょっとだけ速くした10
    遅いクエリ
    - DB?

    View Slide

  11. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    - DB? ❌
    - -> 起動時に YAML を読んで、そこから返している
    11
    遅いクエリ

    View Slide

  12. #kaigionrails GraphQL Ruby をちょっとだけ速くした12
    遅いクエリ
    - 件数?

    View Slide

  13. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    - 件数?
    - -> articles が 20、relatedArticles が 50 ずつくらい
    - 20 * 50 = 1000
    - 多いしクエリの改善の余地がありそう
    - 実際はクエリをなんとかした
    - 好奇心: 多くはあるが数百ms もいくか?
    13
    遅いクエリ

    View Slide

  14. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    プロファイリング
    14
    - GraphQL Ruby が怪しい気がする
    - とりあえず ruby-prof

    View Slide

  15. #kaigionrails GraphQL Ruby をちょっとだけ速くした15
    https://ruby-prof.github.io/

    View Slide

  16. #kaigionrails GraphQL Ruby をちょっとだけ速くした16
    ようするにこう

    View Slide

  17. #kaigionrails GraphQL Ruby をちょっとだけ速くした17
    CallStackPrinter

    View Slide

  18. #kaigionrails GraphQL Ruby をちょっとだけ速くした18
    %self total self wait child calls name location
    5.96 0.659 0.044 0.000 0.615 19241 *#resolve_value
    /usr/local/bundle/gems/graphql-1.11.1/lib/graphql/execution/execute.rb:220
    5.46 0.156 0.041 0.000 0.115 44528 Concurrent::Collection::MriMapBackend#compute_if_absent bench.rb:14
    4.97 0.252 0.037 0.000 0.215 24284 *GraphQL::Schema::LazyHandlingMethods#after_lazy
    /usr/local/bundle/gems/graphql-1.11.1/lib/graphql/schema.rb:104
    3.96 0.079 0.029 0.000 0.049 44532 Thread::Mutex#synchronize
    3.95 0.036 0.029 0.000 0.007 44528 Concurrent::Collection::NonConcurrentMapBackend#_get
    /usr/local/bundle/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb:19
    3.61 0.727 0.027 0.000 0.700 6456 *Hash#each
    FlatPrinter

    View Slide

  19. #kaigionrails GraphQL Ruby をちょっとだけ速くした19
    %self total self wait child calls name location
    5.96 0.659 0.044 0.000 0.615 19241 *#resolve_value
    /usr/local/bundle/gems/graphql-1.11.1/lib/graphql/execution/execute.rb:220
    5.46 0.156 0.041 0.000 0.115 44528 Concurrent::Collection::MriMapBackend#compute_if_absent bench.rb:14
    4.97 0.252 0.037 0.000 0.215 24284 *GraphQL::Schema::LazyHandlingMethods#after_lazy
    /usr/local/bundle/gems/graphql-1.11.1/lib/graphql/schema.rb:104
    3.96 0.079 0.029 0.000 0.049 44532 Thread::Mutex#synchronize
    3.95 0.036 0.029 0.000 0.007 44528 Concurrent::Collection::NonConcurrentMapBackend#_get
    /usr/local/bundle/gems/concurrent-ruby-1.1.6/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb:19
    3.61 0.727 0.027 0.000 0.700 6456 *Hash#each
    FlatPrinter

    View Slide

  20. #kaigionrails GraphQL Ruby をちょっとだけ速くした20
    Concurrent::Collection::
    MriMapBackend#compute_if_absent
    - map.compute_if_absent(key) { calc(key) }
    - ようは hash[:a] ||= ‘a’ みたいな

    View Slide

  21. #kaigionrails GraphQL Ruby をちょっとだけ速くした21
    Concurrent::Collection::
    MriMapBackend#compute_if_absent
    - GraphQL Ruby 内では遅延評価するべきかど
    うかの判定に使っている (?)
    - graphql-batch など resolver が Promise なとき用?
    - 毎度 nil 返ってる

    View Slide

  22. #kaigionrails GraphQL Ruby をちょっとだけ速くした22
    Concurrent::Collection::
    MriMapBackend#compute_if_absent
    https://github.com/ruby-concurrency/concurrent-ruby/blob/f749b81cb6c6291640c0004b57e60dbc2b59
    a72b/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb#L21-L27

    View Slide

  23. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    Key があっても格納された値が
    Falsy なときに毎回 lock とってた
    23
    https://github.com/ruby-concurrency/concurrent-ruby/blob/f749b81cb6c629164
    0c0004b57e60dbc2b59a72b/lib/concurrent-ruby/concurrent/collection/map/mri_
    map_backend.rb#L22

    View Slide

  24. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    なおした
    24
    https://github.com/ruby-concurrency/concurrent-ruby/pull/879

    View Slide

  25. #kaigionrails GraphQL Ruby をちょっとだけ速くした
    GraphQL Ruby Concurrent Ruby を
    ちょっとだけ速くした
    @mtsmfm
    Fumiaki Matsushima
    Kaigi on Rails new #kaigionrails

    View Slide

  26. #kaigionrails GraphQL Ruby をちょっとだけ速くした26
    Warming up --------------------------------------
    without patch 1.000 i/100ms
    with patch 1.000 i/100ms
    Calculating -------------------------------------
    without patch 6.782 (± 0.0%) i/s - 34.000 in 5.030646s
    with patch 8.633 (±11.6%) i/s - 44.000 in 5.140707s
    Comparison:
    with patch: 8.6 i/s
    without patch: 6.8 i/s - 1.27x (± 0.00) slower
    例のクエリが 1.27 倍速くなった

    View Slide

  27. #kaigionrails GraphQL Ruby をちょっとだけ速くした27
    ご清聴ありがとうございました
    - 配列を返す API で、子要素も配列になっていると要素数は M
    * N になる (自明)
    - GraphQL Ruby は 20 * 50 要素 4 フィールドでも 66 ms くら
    い処理にかかる
    - Concurrent Ruby の master を使うと 54 ms くらいになる
    - Concurrent::Map#compute_if_absent で nil 返すのを3倍弱速くしたため
    - 他にも改善の余地はありそう
    - 簡易ベンチマークスクリプトを書いた
    - https://github.com/mtsmfm/graphql-ruby-benchmark-example

    View Slide