nakamura244 blog

所属団体とは関係なく、個人的なblog

xhprofとphp-quick-profilerを使って改善した話

対象のシステム

  • PHP
  • Fulephp使ってる
  • PHP-fpm使ってる
  • Nginx使ってる

はじめに

  • PHPを使っててPHP重いなーとかサーバのメモリ使いすぎだよなーとかいろんな不満を抱えていました
  • PHP自体はそこまで古くないのになーとかFWのversionも上げてるしなーとかざっくり思って見たり
  • でもPHPが本当に原因なのかなーっと疑問を感じながらずっと過ごしてた
  • チームの他のエンジニアがpmapを使ってプロセス (群) のメモリマッピングを表示してくれて、anonという匿名メモリー?へのメモリ割り当てが多く存在する事が分かった。
    • これPHP-fpmの再起動直後と数日間運用してからもう一度計測したりして見比べるとやっぱりPHP側で色々とメモリ使ってるんだろうなーっという感じ
    • Newrelicとかでの指摘は合ってたのかと思ったり

んーこれは少々ばかりDeep Driveして向き合わねばならん

まずは調査

Newrelicを使ってるんですが、契約のplanとNewrelic側の対応FWでfulephpが非対応になってて、詳細が分からずという状態だったので別の解析ツールが必要だったので色々ググった

ただ、一つのツールだけではちょっと分かりづらく、結局下記の2つのツールを活用することがよさげと思って試した

その他

調査結果~考案

自分のローカルマシンで調査しました

一応色々数値自体は隠しておきます。🙇🏽

php-quick-profiler

f:id:tsuyoshi_nakamura:20170214111003p:plain

  • どの画面でprofilingしてもMemory Usedが高い😨
  • アクセスをさばく時に毎度通るロジックの部分が怪しいと分かった

xhprofのグラフから

  • 一番ボトルネックになってる箇所を見つけた。下記グラフの赤くなってる箇所 f:id:tsuyoshi_nakamura:20170214111014p:plain

    • でもここFWのcore部分で手つけづらい…😨
    • その手前の赤マルで記した部分がアクセスの度に必ず通過するメソッドで黄色くなって指摘されてた
      • ここに改善の余地があるのではないかと考えた🤔

xhprofの表から

f:id:tsuyoshi_nakamura:20170214111023p:plain

EWall%でソートして表示しています

項目

  • Calls… 呼びされて回数
  • Incl Wall Time … その関数全体の処理時間
  • IWall% … 全体の実行時間に対する割合
  • Excl Wall Time … その関数から呼ばれた関数の実行時間を除外した、関数の純粋な処理時間
  • EWall% … 全体の実行時間に対する割合(関数の純粋な処理時間 version)

赤で囲った部分はExcl Wallの%で、わりと高いと指摘されてる。且つ手の入れやすい部分。アクセスがある度に動くロジックの部分でもある。

xhprofのグラフの赤マルで囲ったfunctionだった🙄

こいつを改善できれば良いんじゃないかと思った🤔

それより上位はPHPのネイティブ関数の部分だったり、そのPHPのネイティブ関数をFWのcoreクラスで使ってたりでちょっと深い部分。

ここを改修となると影響範囲が大きいのでちょっと一旦パス。

以上を踏まえて主な対策方針

  • アクセスされる度に動く共通のロジックの部分を見直した方が効果が高いのでやろう
  • いきなりFWのCore部分に手をいれるとそれなりのリスクがあるので、Coreから切り離せる部分がまずは良いだろう

対策した内容

  1. アクセスがある度に毎回動くクラスAがfuelphpAutoloaderに設定されていて、クラスAはCoreクラスをextendしていたが、extendを止めた
    • xhprofの表の赤で囲ったfunctionです
    • なぜかと言うと
    • 親クラスのメソッドを全く使っていなかったので必要無いだろうと
    • fuelphpの仕様でAutoloaderで設定されてるクラスを辿り、_initメソッドが存在した場合はそれを動作させるみたい。つまり、クラスAの親クラスの_initメソッドが毎回動いていた
      • 無駄な処理で且つ、中身を見ると結構重そうな処理だった…😰
  2. preg_match関数じゃなくても良い部分はstrposに置き換えた

    • 下記の公式に書いてるようにstrposで置き換え可能な場合は置き換えた方が早くなるよって
    • PHP: preg_match - Manual

      Tip Do not use preg_match() if you only want to check if one string is contained in another string. Use strpos() instead as it will be faster.

  3. cookieを活用してpreg_matchの判定する場面を減らした

    • どうしてもpreg_matchを使わなければ行けない部分はあって、cookieを活用することで効率的に処理するようにした

対策してみた結果

Newrelic

f:id:tsuyoshi_nakamura:20170214113344p:plain

😀

Latency

f:id:tsuyoshi_nakamura:20170214113356p:plain

😀

CPUの数値

f:id:tsuyoshi_nakamura:20170214113432p:plain

😀

LoadAverage

f:id:tsuyoshi_nakamura:20170214113425p:plain

😀

もちろん

xhprof,php-quick-profilerの数値も改善が見られました😀

まとめ

  • 施策を3つほどやりましたが、結局はアクセスがある度に毎回動くクラスAのチューニングが一番効果が高かった🙌
  • xhprofで指摘された処理の中で一番遅く時間が掛かってるものをチューニングしたのが良かった(FWのcoreを除外して) 😀
    • あえてcore部分を一度除外したことで影響範囲も限定されるし、リードタイム短くリリースまでいける❗❗何気にここ良かったと思ってる😀
  • やっぱり時間を取って絶対改善するという心意気重要💪🏽
  • 思ったよりもいろんな数値の改善が見られたのでインスタンスのタイプ一つ下げれるなぁーと思った
    • このシステムが動いているサーバは4台あるので削減できる金額も結構でかく見えると思われる💰
  • やった事はそんなに難しい事ではないので早くやればよかったという結果論😅
  • どんどん改善していきましょー🚀