クックパッド開発者ブログスマートフォン用の表示で見るクックパッド開発者ブログ2015-10-13開発環境のパフォーマンスチューニングこんにちは。クックパッドでは、ユーザーが快適にサービスを利用できるように本番環境でのパフォーマンスを向上させるための様々な工夫がなされています。開発環境に目を向けると、そこにもユーザーがいます。開発環境のパフォーマンスが向上することで、開発者が快適にサービスを開発できるようになります。今回はそういった開発環境でのパフォーマンス向上のための取り組みについてご紹介します。今回紹介するものはそれ以前に実施されたため、Ruby2.2で同じ結果になるとは限りません。今回対象とするのはcookpad.comのアプリケーションです。施策無しの状態でのパフォーマンスがどの程度なのかを調べてみました。consoleやrspecを手元で実行する際に、起動・初期化にかかる時間が20秒程度。server(実際にはforemanを使って関連するミドルウェアも同時起動したりしています)で起動する時間が30秒程度でした。ページ表示時間は、当然箇所によって異なりますが、トップページやレシピ詳細ページを起動後初回表示すると、JSなど含めて全てのロードが終わるまで30秒程度かかっていました。この数値は初回表示の場合で、二回目以降の表示ではキャッシュが効いて30秒だったものが12秒程度になります。起動処理まずは起動時間の20秒に着目しました。Bundler.requireしている時間でした。Railsの初期化処理にも8秒程度かかっていました。Railsの初期化処理をもっと細かく見ていくと、ほとんど(7秒程度)はbundleしているGemがhookしている初期化処理に費やされていました。75%程度がGemのロードに使われていました。何かロードに時間がかかるGemでもあるのかというとそうではなく、単純に量が多かったのです。約300ものGemがロードされていました。塵が積もった状態です。起動についてはRailsが提供しているSpringを使えば、一度起動してしまえば次回以降は時間がかからなくなります。Springを使うことで、ほとんど手をかけずに20秒かかっていた起動が3秒程度になりました。いえクックパッドのように古くからあるアプリケーションの場合、そもそもRailsのアップデートをしていなければ導入できません。こまめにアップデートに追随しておくことでこういった恩恵を得ることもできます。クエリ発行数の削減さて次はページ表示です。初回表示と二回目以降で大きく違うのはDBとSolrへのクエリがmemcachedなどにキャッシュされるためです。IOが大きく寄与していることになります。実際に調べてみると、キャッシュが全く無い場合、あるページではDBとSolrへのクエリがあわせて190本程度発行されていました。N+1クエリが発生していたというわけではありません。純粋に量が多いのです。レシピを表示するのにも、例えば関連キーワードでのレシピ検索結果など、様々な関連情報をロードするためです。いった情報は本番環境ではキャッシュを活用しています。誰かのアクセスでロードされればキャッシュされるため、ほぼ常にキャッシュされているような情報です。開発環境だとそもそもキャッシュにのる機会が少ないためヒット率が悪い。これは性質上避けられないので初回時のキャッシュヒット率は諦め、開発環境であることを逆手にとって、もっと幅広くキャッシュすることにしました。開発時に発行するクエリを全てキャッシュするのです。開発時の動作確認、例えばviewの調整などのために何度も表示するような場合、期待するクエリ結果が変わることはほとんどありません。更新系のクエリが発行された場合にだけキャッシュを破棄し、それ以外は、一度発行されたクエリは結果を常にキャッシュすることで大幅にIOを軽減させることができます。DBへのクエリをキャッシュするために、クックパッドの開発環境では弊社森田が開発したreuse_query_resultsというGemが使われています。Gemにはなっていませんが、Solr(Sunspot)クエリも同様にキャッシュされるようになっています。デメリットもあります。手元のアプリケーションからの更新系クエリしか検知しないので、例えばDBスキーマを頻繁に変更しながら開発する場合はむしろ邪魔になります。そこで起動時に環境変数でオンオフできるようになっています。これらにより主なIOをほぼ無くすことができました。12秒だった表示時間は10秒を切るようになります。それでも10秒・・・道は長そうです。GCを止めるIOが無いのにどこに時間がかかっているんだ?ということで細かく見ていきます。表示にかかる時間のうち、60〜70%程度がコントローラーの処理が終わった後のテンプレートエンジンがviewを構築する時間に費やされていました。テンプレートエンジンが遅いのか?と思いきや、簡単なペライチのviewだと300ms程度で終わるので、もう少し細かく見ていくことにしました。viewが多いほど遅くなっていることがわかりました。viewのrender処理が走っていたのです。その内訳を見ていくと、ほとんどは1ms以内からちょっと大きめでも10ms程度で終わっているものの、しかし一部のviewが300〜400msと非常に時間がかかっていることがわかりました。これはただ重い処理をしている箇所があるだけか?と思いきや・・・計測する度に時間がかかる箇所が変動します。計測範囲を細かくして見ていくと、あるviewのrender処理が終わったあと次のviewのrender処理が始まるまでの間に時間がかかっていることがわかりました。コードの実行の合間に実行されるものといえば・・・GCですね。そこでためしにGCを止めてみることにしました。クックパッドの本番環境ではかなり前からGCを止めて運用しています。OoBGCにすることで、renderに300〜400msかかるようなviewがなくなりました。これによって表示時間は10秒から7秒程度になりました。棚卸しするここまで高速化するための施策について書いてきましたが、視点を変えて現状を維持するための施策もご紹介します。機能はどんどん増えていくものですが、一方で既に必要ないものも存在するはずです。必要ない処理やコードはどんどん整理していくことで、オーバーヘッドの増加を食い止めることもできるはずです。その機能でしか使っていないGemがあってそれも消せるのであれば、その分Gemのロード時間にも寄与します。チームが無かったりして、どれが必要ないのかといったコンテキストが既にわからなくなっているケースもあります。掃除と同じでこまめにやる工夫が必要です。その工夫の1つとして、プロトタイピング開発に使われているChankoのunitの棚卸しを行っています。unitとは、Chankoにおける機能をひとかたまりにした単位で、assetやテストも含み、unit単位で公開範囲の切り替えを行うものです。全体公開せずにベータ版のまま開発終了してしまったが、コードがそのままになっているものや、期間限定公開で公開終了したのに残っているというものを棚卸しするのが目的です。GitHub上でissueが作られmentionするようにしています。その際アプリケーション内でinvokeされているかどうかも合わせて出力しています。機能を落としたが残りっぱなしになっているものなどがわかりやすくなります。一時的に下げているだけだったり、期間限定機能だったりする場合は、設定でこのアラートを抑制することもできます。3ヶ月限定公開なら、3ヶ月後からアラートが飛ぶようにできます。まとめ開発環境高速化のための取り組みについてご紹介しました。いかがでしたでしょうか。本番環境と比較すると、開発環境のパフォーマンスは実ユーザーが使うものではないため軽視されがちです。開発者の生産性が向上することで、結果的によりユーザーに価値が届けられるようになると考えています。起動に20秒かかっていれば、たとえテストが1秒で終わったとしても1分間に3回程度しか実行できませんが、3秒で済むなら20回テストが実行できます。12秒が7秒になれば、1時間かかっていた動作確認は35分で済むようになります。これがエンジニアの人数分だけ効果が広がるのです。現状ではキャッシュに頼った高速化なので、初回アクセスが重い問題は解決していません。表示速度もまだ快適というレベルには至っていません。今後も開発環境の高速化の取り組みは継続していこうと思っています。10:10開発環境のパフォーマンスチューニングTweet関連記事2019-06-17レシピ検索を支えるレガシーでクリティカルな大規模バッチを刷新した話こんにちは、会員事業部の新井です。インタプリタを開発している笹田です。使ったセキュリティログ検索基盤の構築サービス特性にあった検索システムの設計戦略クックパッド採用説明会「クックパッドはサービスの作り手を採用したいんです。/*}*/スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。