Slide 1

Slide 1 text

© Link and Motivation Group たった50行直しただけで CSV一括登録機能が2h速くなった話 リンクアンドモチベーション Developer Productivityユニット プラットフォーム推進グループ 谷 有輝

Slide 2

Slide 2 text

2 © Link and Motivation Group 谷 有輝(@taniiiiim) 株式会社リンクアンドモチベーション Developer Productivityユニット プラットフォーム推進グループ ● 2019年新卒入社 ● バックエンドメインのフルスタックエンジニア ● モチベーションクラウドシリーズ内外のシステム 連携開発に従事 ● 趣味はパルクール🏃‍ ♂️ 自己紹介

Slide 3

Slide 3 text

モチベーションクラウド 診断 変革 ※ 2022年度 実績 10,060 社 312 万人

Slide 4

Slide 4 text

4 © Link and Motivation Group Ruby on Railsでの開発中に遭遇した性能問題の事例共有 実際にパフォーマンス問題を解消した方法の事例共有 ※本発表で共有する機能・コードは実際のものとは異なります。 ※本発表で共有する解消方法が全てではございません。あくまで一例としてご参考いただけたらと思います。 今日お話しする内容

Slide 5

Slide 5 text

5 © Link and Motivation Group Ruby on Railsでの開発中に遭遇した性能問題の事例共有 実際にパフォーマンス問題を解消した方法の事例共有 ※本発表で共有する機能・コードは実際のものとは異なります。 ※本発表で共有する解消方法が全てではございません。あくまで一例としてご参考いただけたらと思います。 今日お話しする内容 基本的なことをちょっと意識するだけで パフォーマンス改善できる!

Slide 6

Slide 6 text

6 © Link and Motivation Group アジェンダ 1. 起きたこと 2. 対応内容 3. 最後に

Slide 7

Slide 7 text

© Link and Motivation Group 7 起きたこと

Slide 8

Slide 8 text

8 © Link and Motivation Group 性能問題があった処理について ユーザー情報 CSVでユーザーを一括登録する処理

Slide 9

Slide 9 text

9 © Link and Motivation Group 登録CSVファイル 名前 性別 年齢 役職 持部 太郎 男性 35歳 課長 輪怐 次郎 男性 40歳 部長 紫苑 花子 女性 30歳 係長 ユーザーをラベルつきで登録

Slide 10

Slide 10 text

10 © Link and Motivation Group 実施した性能試験 目標 1h ユーザー50000人×ラベル10000件で性能試験

Slide 11

Slide 11 text

11 © Link and Motivation Group 目標 1h 結果 3h15min 実施した性能試験 ユーザー50000人×ラベル10000件で性能試験

Slide 12

Slide 12 text

12 © Link and Motivation Group 実施した性能試験 ユーザー:50000人 ラベル:10000件 のデータ要件でCSV一括登録を実施 目標:1h 結果:3h15min これはまずい!

Slide 13

Slide 13 text

© Link and Motivation Group 13 対応内容

Slide 14

Slide 14 text

14 © Link and Motivation Group 性能改善結果 Before 3h15min

Slide 15

Slide 15 text

15 © Link and Motivation Group 性能改善結果 After 1h5min Before 3h15min

Slide 16

Slide 16 text

16 © Link and Motivation Group 性能改善結果 ユーザー:50000人 ラベル:10000件 のデータ要件でCSV一括登録を実施 目標:1h Before:3h15min After:1h5min 2hの短縮に成功!

Slide 17

Slide 17 text

17 © Link and Motivation Group 性能劣化を引き起こしていた実装 1. 不要にループをしている 2. 不要に大きなインスタンス生成をしている

Slide 18

Slide 18 text

18 © Link and Motivation Group ①不要にループをしている # Before labels.map do |label| users.find do |user| label.user == user end end ループ数 実装詳細 Before (label数) * (user数) labelとuserの二重ループ

Slide 19

Slide 19 text

19 © Link and Motivation Group ①不要にループをしている # Before labels.map do |label| users.find do |user| label.user == user end end # After labels.map do |label| label.user end ループ数 実装詳細 Before (label数) * (user数) labelとlabelの二重ループ After (label数) labelのループのみ

Slide 20

Slide 20 text

20 © Link and Motivation Group ①不要にループをしている # Before user_label = user_labels.find do |user_label| labels.include?(user_label.label) end # After label = labels.find do |label| user_label_by_label_id[label.id] end user_label = user_label_by_label_id[label.id] ループ数 実装詳細 Before (label数)^2 * (user 数) user_labelとlabelの二重ループ After (label数) labelのループ ループ数が大幅に減って性能改善 before: 500000000回 after: 10000回

Slide 21

Slide 21 text

21 © Link and Motivation Group # Before name = "持部 太郎" users = User.all user_by_name = users.index_by(&:name) user_id = user_by_name[name].id ②不要に大きなインスタンス生成をしている users user_by_name Before ActiveRecord::Relation valueがActiveRecordのHash

Slide 22

Slide 22 text

22 © Link and Motivation Group # Before name = "持部 太郎" users = User.all user_by_name = users.index_by(&:name) user_id = user_by_name[name].id ②不要に大きなインスタンス生成をしている users user_by_name Before ActiveRecord::Relation valueがActiveRecordのHash 不必要にActiveRecord::Relation の インスタンスを生成すると 無駄な処理コストがかかる

Slide 23

Slide 23 text

23 © Link and Motivation Group # Before name = "持部 太郎" users = User.all user_by_name = users.index_by(&:name) user_id = user_by_name[name].id ②不要に大きなインスタンス生成をしている # After name = "持部 太郎" users = User.all.pluck(:id, :name) user_by_name = users.index_by(&:name) user_id = user_by_name[name][0] users user_by_name Before ActiveRecord::Relation valueがActiveRecordのHash After Array valueがArrayのHash

Slide 24

Slide 24 text

24 © Link and Motivation Group # Before name = "持部 太郎" users = User.all user_by_name = users.index_by(&:name) user_id = user_by_name[name].id ②不要に大きなインスタンス生成をしている # After name = "持部 太郎" users = User.all.pluck(:id, :name) user_by_name = users.index_by(&:name) user_id = user_by_name[name][0] users user_by_name Before ActiveRecord::Relation valueがActiveRecordのHash After Array valueがArrayのHash 生成されるインスタンスが小さくなり 処理コストが削減

Slide 25

Slide 25 text

© Link and Motivation Group 25 最後に

Slide 26

Slide 26 text

26 © Link and Motivation Group 明日からできる性能改善 1. 不要にループをしない 2. 不要に大きなインスタンス生成をしない 総括

Slide 27

Slide 27 text

27 © Link and Motivation Group 明日からでもできる性能改善4選 1. 二重ループを見たら、一個でも減らす 2. 「集合に含まれているか?」の判定にSet#includeを使う 3. ActiveRecord(::Relation)のインスタンスではなく必要なカラムの値のみを利用し て変数に格納する 4. 必要なカラムだけselectする 総括 基本的なことをちょっと意識するだけで パフォーマンス改善できる!

Slide 28

Slide 28 text

28 © Link and Motivation Group お知らせ ● エンジニアリングマネージャー ● プロダクトマネージャー ● テックリード ● サーバーサイドエンジニア ● フロントエンドエンジニア ● SRE ● データエンジニア ● CRM ● UXデザイナー 週1でテックブログ更新しています! まずはカジュアルにお話しましょう! ご連絡お待ちしております! 全職種、積極採用中です!