<%Dewplayer(javascript)%> 肉少なめ | Item - PHPのチューニングネタ 和訳的なメモ

PHPのチューニングネタ 和訳的なメモ

Title :
PHPのチューニングネタ 和訳的なメモ
Posted on :
2006-11-25
Author :
NKJG
Category :
Webメモ
Hatena Star :

本文

12のPHP最適化テクニック:phpspot開発日誌が参照にしている、12 PHP optimization tips - Alex Moskalyuk Blogからリンクされている、Ilia Alshanetsky (Ilia Alshanetsky - Talks)氏のPHP & PerformancePHPのチューニングの部分のみ読んだ、つもり。英語ダメなんで。

以下PHPのチューニングに関する部分のみのまとめ的なもの。私的なメモ。誤訳の可能性を考えると信頼はしないほうがよさげ。私以外の人は原文を読むべき。

Now let's tune PHP code (PHP & Performance P32-67)についてのメモ

オブジェクトについて

PHP5関連の話。

staticなメソッドは、常に宣言を行うべき

単純にそのほうが速くなる。

オブジェクト定数を活用する
  • コンパイル時にオーバーヘッドがなくなる
  • ハッシュが短くなると早く見つけることができる
  • 名前空間を使って短いハッシュを使おう
  • きれいなコードはデバッグしやすい
マジックメソッドは使わない

_get(), _set(), _autoload(), _call()は遅い。

require_once()は遅い

もしrequire_once()include_once()を使わなければならないなら、せめてフルパスで指定すべき。

PHP5.2以降では同じファイルを二度読み込むことはなくなるだろう。

定数で代用できる関数は定数のほうが速い

西部で一番速いWin32判別法
$isWindows = DIRECTORY_SEPARATOR == '\\';
  • 関数を使わずに済む
  • WinXP,WinNT,Windows,Windows98,NT5.0などの違いを考慮せずに済む
  • どこでも使える

time()を何度も使うより

time()を何度も使うより$_SERVER['REQUEST_TIME']を使うほうが良い。関数を使わずに済む。

文字列置換・比較のチューニング

正規表現のチューニング
"後方参照しないパターン"を使う

以下の2つのコードについて……

$text = preg_replace('/=?\([^?]+)\?/', '=?iso-8859-1?', $origtext);
$text = preg_replace('"/(\n|\t|\r\n|\s)+/"', ' ', $origtext);

サブパターンの最初に?:をつけると後方参照しないパターンになる
メモリの節約になってお得

$text = preg_replace('/=?\(?:[^?]+)\?/', '=?iso-8859-1?', $origtext);
$text = preg_replace('"/(?:\n|\t|\r\n|\s)+/"', ' ', $origtext);
可能なら正規表現を使わない
if (preg_match("!^foo_!i", "FoO_")) { }

より

if (!strncasecmp("foo_", "FoO_", 4)) { }

のほうがかなり速い

if (preg_match("![a8f9]!", "sometext")) { }

より

if (strpbrk("sometext", "a8f9")) { }

のほうが速い

if (preg_match("!string!i", "text")) { }

より

if (stripos("text", "string") !== FALSE) { }

のほうが速い

$text = preg_replace("/\n/", "\\n", $text);

などはstr_replaceに置き換えるとより単純で高速。

$text = str_replace("\n", "\\n", $text);

しかし…

strtrを使う

上の例よりstrtrを使った場合のほうが速い。

とくに文字列を使ってstrtrを使う
$rep = array('-' => '*', '_' => '*');
if (sizeof($globArr) > 1) {
  $glob = '-' . strtr($globArr[1], $rep);
} else {
  $glob = strtr($globArr[0], $rep);
}
if (sizeof($globArr) > 1) {
  $glob = '-' . strtr($globArr[1], '-_', '**');
} else {
  $glob = strtr($globArr[0], '-_', '**');
}

↑ 下のほうが10倍速い。

そもそも置換自体をあまりしないように

置換はメモリを喰うので、strposあたりを使って置換を行うべきか否か事前にチェックするべき。

より良い文字列比較の方法

良い方法

if (!strncmp(PHP_OS, 'WIN', 3)) { }
if (!strncasecmp(PHP_OS, 'WIN', 3)) { }

悪い方法

if (substr(PHP_OS, 0, 3) == 'WIN') { }
if (strtolower(substr(PHP_OS, 0, 3)) == 'win') { }

最悪

if (preg_match('!^WIN!', PHP_OS)) { }
if (preg_match('!^WIN!i', PHP_OS)) { }
オフセットを反映させて文字列を比較するとき

PHP5ではsubstr()を使わずに、substr_compare()を使うことで文字列の開始位置以外での比較ができる。

if (substr($class, -15) != 'text') { }
if (substr_compare($class, 'text', -15)) { }

↑ 2つは同じ意味。

@演算子は最悪

@使うくらいなら、

$old = ini_set('error_reporting', 0);
action();
ini_set('error_reporting', $old);

を使うべき。

定数を間違って使わない

$foo = array("bar" => 0);
$foo[bar] = 1;

↑ ナンセンス。

何が悪いか

  • 定数のくせに小文字 (この辺よく訳せず)
  • 2回ハッシュの検索が実行される
  • E_NOTICEレベルのエラーが発生する
  • 一時的な文字列が生成されている

$foo['bar']と正しく記述することで7倍速くなる

"無害な"エラー・メッセージを修復

全てのエラーは結果として

  • 複合的な文字列を作成する
  • 標準出力に出力する
  • ファイルやsyslogに出力するかも
  • PHP5.2以前ではメモリリークを生むかも

forループの判定に関数を使わない

for ($i = 0; $i < sizeof($array); $i++) { }
for ($i = 0; $i < count($array); $i++) { }
for ($i = 0; $i < strlen($string); $i++) { }

↑のように書くと毎回関数が呼ばれるから遅くなる。

車輪の再発明はしない

使いたいものがすでに用意されている可能性は十分にある。

なるべくフルパスを使う

フルパスのほうが速い。

参照を使ったトリック

多次元配列は参照を使うとシンプルで高速になる。

// 以下の例はアクセスのたびに2つのハッシュを検索しているため遅い
for ($i = 0; $i < 5; $i++)
  $a['b']['c'][$i] = $i;
 
//参照を使うとより高速になる
$ref =& $a['b']['c'];
for ($i = 0; $i < 5; $i++)
  $ref[$i] = $i;

迷信

以下は迷信。

  • コメントを除去すると速くなる
  • "を使うと'を使うより速くなる
  • 参照渡しを使うと速くなる
  • オブジェクトを使うと速くなる
  • 三項演算子を使うとif...elseを使うより速くなる