63回ケチつけといた

PHP コード最適化 Best Practices 63+ – カタコト日記

セキュリティが便利さを生贄にすることで得られるのと同様に、高速化はコードの可読性や保守性と引きかえになる。よっぽどまずいコードでなければ。

63個もあってこれがないのかよ、っていうのはこんな感じ。

  • ファイル末尾に?>をつけるな(ref. Zend Frameworkコーディング規約)
  • ==じゃなくて===使おうぜ
  • 大きいファイルならfile()やfile_get_contents()じゃなくてfopen()とか使おうぜ
  • マジッククオートを捨てろ

printをechoにしてみたりするのは、メラよりヒャドのほうがダメージ高いとかいってゾーマに連発するようなもの。とっとと勇者にバイキルト掛けるべき。パーティ編成を見直すべき。戦略を考えるべき。どうしても無理ならレベル上げるべき。

以下ケチ

01.static にできるメソッドは static として宣言しよう。(4倍速い)

$this->method()とself::method()じゃ意味が違います。self::method()だと$thisを無視できるので理論的には軽くなるけどどうでもいい。

02. echo の方が print より速い。

インデントをやめて改行も消したらより速くなるよ。

03. echo ‘文’,’字’; (カンマ区切り)の方が、’文’.’字’ (ドット連結)より速い。

.だと文字列を連結してからecho、,だととりあえず頭からechoしていく、の違い。ドットは連結した文字列を格納するから遅い。たぶん。どうでもいい。

04. ループの最大値は、ループ「内」ではなく「前」にセットしておこう。
19. for 文の条件式には count($array) のような関数をいれない。(変数に格納)
47. ループ処理には foreach を使おう。

配列のループにはforeachを使うべき。 forの出番はまずないし、あったとしてもfor($i=0; $i<10; $i++)のように数値リテラルでループ回数指定するくらいだろうからどうでもいい。

05. 大きい配列のような変数は unset() してメモリを解放しよう。

大きいって具体的にどれくらい?

最近だと気にしなくていいのかもしれないけど、循環参照の場合はunset()するようにはしてる。

06. マジックメソッド(例: __get, __set, __autoload)は使用を避けよう。

ついでにクラス定義とかrequireとかもやめて1ファイルで完結しちゃえばもっと軽くなるよ。

07. require_once はハイコストなのです。
52. require_once より require を使おう。(opcode キャッシュがらみの理由で)

require()だと同じクラスや関数を2回定義しようとしてWarningかなんかが出る可能性があるよ。人力でチェックするのとどっちがexpensiveかは状況によるかも。

数マイクロ秒 vs. 人間の神経って図式なので、数マイクロ秒のほうが重要なら人間性を無視する方向で。

08. include や require でファイルはフルパスで指定しよう。
51. ファイル指定は basename / file_exists / open_basedir よりフルパス指定が速い。

require_once dirname(__FILE__)."/foo.php";くらいならいいけど、PEARまでフルパスっていわれるとちょっと。

フルパスで指定するのは曖昧さ回避のためにするべきで、パフォーマンスのためにやるべきじゃない。

ていうかbasenameでファイル指定?

09. スクリプト開始時間は time() でなく $_SERVER[‘REQUEST_TIME’] で取得。

PHP5から。互換性のためにスクリプト冒頭で代入してしまうのも豪快でいいかもしれない。

10. 可能であれば、正規表現より strncasecmp、strpbrk、stripos を使おう。

正規表現よりその3つを使い分けるほうが難しいんじゃないだろうか。

11. strtr(str_replace の4倍速い) > str_replace > preg_replace の順に速い。

strtr() / str_replace()だと正規表現じゃないんだな、というメッセージは受け取れます。

12. 引数に配列、文字列両方を受け入れるような関数は避け、個別に関数化しよう。

ん? こういうコードのことを言ってるんだろうか。

function sample12_before($var){
    if(is_array($var)){
        // なんか処理 for 配列
    }elseif(is_string($var)){
        // なんか処理 for 文字列
    }
}

function sample12_after($var){
    if(is_array($var)){
        some_func_for_array($var);
    }elseif(is_string($var)){
        some_func_for_string($var);
    }
}

にしようってことかな。必要な状況がよくわからないけど、(array)$varでキャストしたらダメ?

13. if をたくさん使ってるなら switch を活用しよう。(訳注:ってことでいい?)

訳注とあったので原文見てみた。「It’s better to use select statements than multi if, else if, statements.」って、selectステートメントってなんやねんってことか。なるほど。

elseif連発よりswitchのほうが好ましいのでこれはあり。

14. @によるエラー制御はすんごく遅い。

この前かいた問題 。これもパフォーマンスでなく可読性とかエラー制御とかそういう観点から述べるべき、というのが僕の考え。

15. Apache の mod_deflate(Apache2系) を ON にしておこう。(No.42 参照)
42. Apache の mod_gzip(Apache 1系)は、転送量を最大80%減。

なんで急にHTTPサーバの話に。

とりあえずgzipに関してはみんな大嫌いなIEが例のごとく腐ってる ので留意すること。

16. 処理が終わったらデータベースの接続は切っておこう。

昔while()ループの中で毎回connectとdisconnectを繰り返すプログラムがありまして、実に大変なことになっていたことがあります。

軽くなるわけでもないし、意識的にdisconnectしなきゃいけないことは滅多にないかと思います。

17. $row[‘id’] は $row[id] より7倍速い。

後者の場合、PHPは「id」という定数が定義されてるか調べて、もし定義されてなければ「id」という文字列を返す、という処理をします。

後者のこの書き方は非推奨です。

18. エラーメッセージはハイコスト。

そうか! エラーメッセージを組み込まなければいいんだ! んなアホな!

20. メソッド内ではローカル変数をインクリメントするのが一番速い。

比較対象はなんだ。どうしろっていうんだ。$tmp=$this->foo;$tmp++;$this->foo=$tmp;しろとでもいうのか。

21. グローバル変数のインクリメントはローカル変数より2倍遅い。

グローバル変数……。インタプリタ的にローカル変数のほうがグローバル変数より手近だから。

22. オブジェクト変数(例:$this->v++)のインクリメントはローカルより3倍遅い。

たぶん理由は上に同じ。

23. 未定義のローカル変数のインクリメントは定義されたものより9~10倍遅い。
24. 未定義のグローバル変数についても同様。

そりゃ定義する必要がありますから当然です。あとE_NOTICE出ます。

25. メソッドの起動コストはクラス内のメソッド数とは無関係。

さすがに内部ではO(1)な実装にしてるでしょう。

26. 派生クラス内のメソッドは、基底クラスで定義されたメソッドよりも速い。

理由は21,22あたりと同じ。

27. 引数1つの空関数を呼び出すのにも、ローカル変数 $local++ 7~8回分のコスト。

そうですか。

28. ダブルクォート より シングルクォート の方が若干速い。

意味が違う。シングルクオートなら変数展開しない。

29. 03 と重複か。(注:これは複数の引数を受け取る echo でのみ有効)

らしいので03参照。

30. PHP スクリプトは静的な HTML ページよりも2~10倍の時間がかかる。

そりゃmod_php読んでスクリプト解析して実行するんだから重いに決まっています。

31. キャッシュを利用しよう。通常、コンパイル回数 x 25~100% 分速くなる。

コンパイルキャッシュのことかな。数値の根拠は不明だけど入れて損はないと思う。

32. これもキャッシュについて。memcached とか使おうよ。

今までのを読む限りmemcachedって言いたかっただけじゃないかと思ってしまう。memcachedはいいものです。

33. if (strlen($foo) < 5) を調べたいなら if (!isset($foo{5})) と書くと速い。

たしか後者の書き方はPHP5の時点で非推奨になってたはず。あとマルチバイトだと期待どおり動かない。百害あって一利なしかと。

34. $i++ より ++$i の方が速い。(そういう opcode optimizer が走ってる場合)

「そういう opcode optimizer が走ってる場合」を使えばなんとでも言える。

35. 全部が全部 OOP でなくても良い。(コストやメモリの浪費になってるかも)

それはそのとおり。カッコ内は微妙だけど。

36. すべてのデータをクラスにしようとしないこと。配列も十分便利です。

配列とクラスはまったく別物なんですが……。

37. メソッドを分割しすぎない。(どれを再利用するのかよく考えよう)

これもだいたいそのとおりだけど、関数呼び出しのオーバーヘッドって意味なら同意できない。

38. メソッドを分割したくなったら後でいくらでもできる。

実際に(気力的な観点から)やるかどうかは別として、これもそのとおり。

39. もとから用意されてる 関数たち を活用しよう。

これもそのとおり。そのほうが速いしバグもないし読みやすいから。

40. 非常に時間のかかる関数 → C言語による拡張モジュール化も検討しよう。

これもそう。重いorPHPだとめんどいなら他の言語のパワーを積極的に使うべき。

41. コードをプロファイリングしてボトルネックを見つけよう。Xdebug とか。

最適化の基本中の基本。プロファイリングもせずに最適化とか無茶。

43. : A HOWTO on Optimizing PHP with tips and methodologies を見てね。

見た。We summarize the optimization methods below:って書いてある表が、流れと効果がわかりやすくていいと思う。

48. 複雑なクラスを作ってるなぁと思ったら → Singleton モデルを検討しよう。(原文:Do consider using the Singleton Method when creating complex PHP classes.)

訳者の人も意味がわからず混乱中みたい。僕も意味がわからない。

たぶん、同じクラスを何回もnewするよりsingletonでインスタンスを使いまわせば(ミクロな意味で)速くなる、って言いたい、の、か、なあ。

おそらく無意味だから無視していい。

49. DB に入れる値なら GET より POST を使おう。(パフォーマンスが上がる)

HTTPのごく基本的なところだけでも勉強してください。

50. 可能なら正規表現より、ctype_alnum、ctype_alpha、ctype_digit を使おう。

それぞれ英数字のみ、英字のみ、数字のみのときにtrueを返す関数。

正規表現だと^とか$をつけ忘れたり、[0-9]*のように+じゃなくて*とかやってしまったりするかもしれないし、安全でいいと思う。パフォーマンスはどうでもいい。

53. 一時的なファイルを作るときには tmpfile や tempnam を使おう。

39と同様。

54. 外部サービスに XMLHTTP で接続するときにはエラー回避のため Proxy を使おう。

XMLHTTP? JavaScript? 外部に対して??

何にしろProxyでキャッシュしとけば安全な気はする。Proxyがその外部のサービスより安定してれば。

55. デバッグするときは error_reporting (E_ALL); にしておこう。

個人的にE_NOTICEとは決別しました。基本的にE_ALLでいいと思う。

56. Apache の allowoverride を “none” にしておくとパフォーマンスが向上。

PHP関係ない。そんなの言うくらいならApacheにももっといっぱいあるだろう。

57. 静的なコンテンツには thttpd のような高速なファイルサーバを使おう。

リバースプロクシろうぜ的な。

58. パスなどの設定パラメータは配列に serialize して入れ、キャッシュしておこう。

毎回parse_ini()するかunserialize()するかの2択ですね。わかります。

59. アクセスの多いページには PHP の 出力バッファリング を使おう。

ob_* な面々について。ついでにキャッシュしとけばなおよし。

60. DB 固有の prepare メソッドではなく PDO::prepare を使おう。

PHP5から。特にいうことなし。

61. SELECT * (ワイルドカード)を使うのはやめよう。

ちょっと前の話題。

JOINとかGROUP BYとかがからむならまだしも、毎回selectのキーを指定する処理のほうが人的にも機械的にも高コストだと思うんだけどなあ。クエリキャッシュのヒット率も下がるわけだし、あんまりいいことはなさそうに思う。

どうしてもっていうならビューでやるかなあ。

62. PHP より賢い DB ロジック(queries, joins, views, procedures)を活用しよう。

データベース側のビューとかプロシージャとかの機能を活用しよう、ってことかな。

コメントとか制約とかから自動的に入力フォームを作るライブラリをつくったことがあるけど、JOINで壁にぶつかって途中で飽きて捨てたなあ。

63. SQL の省略表記を活用しよう。(例:INSERT INTO MYTABLE (FIELD1,FIELD2) VALUES ((“x”,”y”),(“p”,”q”));)

あんまりやると互換性で泣くのでほどほどに。個人的には、上の例だとprepareしてforeachかなあ。

これは間違いないと判断したもの

なんか全部マニュアルに載ってることのような気がしてきた。PHPのマニュアルはすごく充実してるので暇なときに読んだりするといいと思う。

09. スクリプト開始時間は time() でなく $_SERVER[‘REQUEST_TIME’] で取得。

13. if をたくさん使ってるなら switch を活用しよう。

17. $row[‘id’] は $row[id] より7倍速い。(速度じゃなく別の意味で)

31. コンパイルキャッシュを利用しよう。

32. memcached とか使おうよ。

35. 全部が全部 OOP でなくても良い。

38. メソッドを分割したくなったら後でいくらでもできる。

39. もとから用意されてる 関数たち を活用しよう。

40. 非常に時間のかかる関数 → C言語による拡張モジュール化も検討しよう。

41. コードをプロファイリングしてボトルネックを見つけよう。Xdebug とか。

47. ループ処理には foreach を使おう。

53. 一時的なファイルを作るときには tmpfile や tempnam を使おう。

php
  • 2008-05-29 15:49:02

    > インデントをやめて改行も消したらより速くなるよ。
    むっかーし、意識した事が一回ある…
    何考えてたんだろー?

    17番の比較はまったく違う内容で比較になってないたまたま同じ結果が返ってるだけ(違う結果が返る場合もある)だおね


    まーもちっと懇意的に解釈しても無問題かと思うお(・∀・)

  • tt252008-05-29 19:16:50

    えー。
    どうでもいいスピードアップよりちゃんと書くことから始めようよ的なあれっすわ。

3+5=
名前