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

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 をちょっとだけ速くした ➔ Web Dev at Quipper ➔

    Dead by Daylight 仲間募集 ➔ 西日暮里.rb 主催 ➔ GraphQL Tokyo 主催 @mtsmfm.inspect
  2. #kaigionrails GraphQL Ruby をちょっとだけ速くした 5 GraphQL とは - POST /graphql

    {“query”: “{ articles { title }”} したら {“data”: {“articles”: [{“title”: “Hi”}]}} を返すやつ
  3. #kaigionrails GraphQL Ruby をちょっとだけ速くした 7 ある日鳴る SLO アラート - そのサービスは

    90 % 300 ms - 社内サービスなので顧客に約束しているわけではない が放置は NG - 遅い GraphQL クエリがあった
  4. #kaigionrails GraphQL Ruby をちょっとだけ速くした - DB? ❌ - -> 起動時に

    YAML を読んで、そこから返している 11 遅いクエリ
  5. #kaigionrails GraphQL Ruby をちょっとだけ速くした - 件数? - -> articles が

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

    name location 5.96 0.659 0.044 0.000 0.615 19241 *<Module::GraphQL::Execution::Execute::ExecutionFunctions>#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
  7. #kaigionrails GraphQL Ruby をちょっとだけ速くした19 %self total self wait child calls

    name location 5.96 0.659 0.044 0.000 0.615 19241 *<Module::GraphQL::Execution::Execute::ExecutionFunctions>#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
  8. #kaigionrails GraphQL Ruby をちょっとだけ速くした21 Concurrent::Collection:: MriMapBackend#compute_if_absent - GraphQL Ruby 内では遅延評価するべきかど

    うかの判定に使っている (?) - graphql-batch など resolver が Promise なとき用? - 毎度 nil 返ってる
  9. #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
  10. #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 倍速くなった
  11. #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