今作ってるモノ

UnityのAndroid/iOSライセンスをセール時に買ってからちょくちょく弄ってたけど、最近やっと1ゲーム出来上がったのでそろそろ公開しようと思います。

device-2014-01-21-213529

いわゆる3マッチパズルです。
キャンディークラッシュとか細々とやってたけどなんかこれ運ゲーじゃね?って思い始めた頃から自分でも作ってみようと思い立ったもので。まあ結論としては、ピース生成時に近辺のピースと不揃いになるように設定したり不揃い具合をどのくらいにするか調整することで難易度を設定すればプレイヤーが運要素を調整できるなって思ってそのようにしました。

これが外観

device-2014-01-21-213646

device-2014-01-21-213749

device-2014-01-21-213823

3Dモデルを入れつつもベースは2Dゲームです。2DToolKit最高や!NGUIなんかいらんかったんや!

まあ、2DToolKitのUIはまだベータ版で、古いボタンシステムとかdeprecateされてるんですけどね。でも使っちゃうよ?次回作作る頃にはUIシステムがベータじゃなくなってると信じて。

がんばって140ステージ&3つの難易度を作りました。

これで小遣い稼げたらUnityのAndroid Proライセンス買おうかな。スプラッシュ画像は独自に作りたいとも思うしプロファイラ使いたいしね。

あとやっぱり、パズルゲームとか作るのってアルゴリズムの勉強になるわ。シンプルな3マッチパズルだと再帰関数だの何だのはあんまり必要ないけどどう書けば効率的かとかプロファイラ見ながら試したりしてみたいし。やっぱりProライセンス要るわ。買うしかねぇわ。

でもって導入する広告をいま検討しています。

フリーで公開されてるAdmobプラグインがあるけど常に広告出っぱなしなので断念した。

私は俗物なので小遣いは欲しいけど、ユーザビリティ犠牲にしてまで載せる気にはならん。

なので、このへんが妥当だろうか。

あと、シュミレーション用の実機をもう一台欲しいなぁ・・・・

週末あたり大須に買いに行くか。

PHPer必見の文書『A HOWTO on Optimizing PHP』を和訳してみたよ!

正式名称は『A HOWTO on Optimizing PHP with tips and methodologies』ですね。

PHPの最適化に関する文書です。

PHP4の時代に書かれたものですが、今でも通用する内容だと思います。

この週末を使ってコレを読んでたので、ついでに和訳しました。

かなり意訳&拙い翻訳ですが、何かしらのお役に立てればと思って載せます。
情報古かったり個人的に縁のない部分は省略したりしました。
ポロリもあるよ!

—–ココから和訳—–

やあ!

こいつの最終更新日は2009年9月30日だよ!
ベンチマークはちょっと古いかもしれないけど、一般的なアドバイスとしてはまだまだイケるんじゃないかな!
8年間やってきた中でいちばん需要なのはキャッシングだって気付いたからそこんとこは修正したよ。
まあ、squidとmemcacheについて付け加えたんだけどね。
変更事項について知りたきゃ原文のログを探すことだね。
こいつが気に入ってくれたら、僕のブログ『どこでもPHP!』(http://phplens.com/phpeverywhere/)を読んでよ。

【PHPを最適化する方法】
PHPはすっげー早いプログラム言語なんだけどさ、そいつを最適化することでもっと早くなるんだぜ。
『スピードは俺の方が上だったな』ってくらいに。

ここじゃあさ、PHPに関係あるなしじゃなく、PHPがサーバー上で他のサブシステムにどう影響するのかとか、
PHPを最適化するべき要因とかさ、そのへんについて説明しつつボトルネックを確認していくよ。

【高性能ってやつを実現しよう】

パフォーマンスがどのくらい良いかって話するときにさ、PHPがいかに早いかっていう話はしたことないよね。

そもそもパフォーマンスってのはさ、

『速度&正確な動作&スケーラビリティ』

ってのを一緒くたに考えなくちゃいけないんだ。

例えば速度と正確さって話だと、キャッシングして動作が早くなっても参照してるのが古いデータだと

「こんなの絶対おかしいよ!」

って話になるじゃん?
もしくはさ、速度とスケーラビリティを重視して正確さを損なうようなことだってあるわけじゃん?

つまりさ、PHPはすっげー早く走る短距離ランナーであると同時にタフな長距離ランナーでなくちゃいけないんだよ。

問題をハッキリさせなくちゃいけないから、リアリティのある例を出そう。

僕が250Kのファイルを読み込んでさ、そいつについてHTMLでのサマリーを出力するとしよう。
僕は同じ挙動をする2つのスプリクトを書くよ。

ファイル全部を一度に読み込むhare.phpと、一行ずつ読み込むtortoise.php。
読み込むのはtortoise.phpのほうが遅いし、より多くのシステムコールを必要とするんだ。

hare.phpは0.04秒かけて10MBのメモリを消費し、tortoise.php は0.06 秒かけて5MBのメモリを喰う。
サーバーのメモリは100MB、CPUは99%アイドルタイム、話を単純化するためにメモリの断片化は
起こらないとしよう。

並列で10のスプリクトが動いてるとすると、hare.phpはメモリ(10のx 10 = 100)が尽きちゃう。
ところがね、tortoise.php は50MBのメモリが自由なままなんだ。
hare.phpが11個目のスプリクトを動かしたら、最初の半分で減速しちまう。
トータルで0.08秒かかる。
ところがどっこい、tortoise.phpは0.06秒で終わっちゃうんだ。

つまり、良いパフォーマンス=早いPHP、ってわけじゃないんだ。

ハイパフォーマンスなPHPは、
下にあるハードウェア、OSと支えるソフトウェア(例えばウェブサーバとデータベース)
の十分な知識を必要とするんだ。

【ボトルネック】

ウサギとカメの例が分かりやすいよね、ボトルネックについて話すとき。
無限のメモリはで、hare.phpは常にtortoise.phpより速いよ。
でも残念なことに、この二つの例えは単純すぎるし、メモリ以外のボトルネックだってあるんだ。

(a) ネットワーク
君が使ってるサーバーのネットワークが多分最大のボトルネックだよね。
秒当たり1MBの転送速度なら、30kのウェブページが33個あれば溢れちまう。
さらに細かく言うと、DNSだのなんだのってのもある。

(b) CPU
質素なHTMLを送るだけならCPUはネットワーク以上のボトルネックにならない。
でもリッチなやつを送るとそうでもないんだ。
対策としては、サーバーを冗長化することだね。

(c) 共有メモリ
共有メモリの使いすぎもあんまりよくないよね。

(d) ファイルシステム
メモリからデータを読むより、ハードディスクにアクセスすることは、50~100倍遅かったりするんだ。
メモリを使っているファイル・キャッシュはこいつを軽減できる。
でもメモリが低い場合はそうじゃない。
Unixのシンボリックリンクの重い使用は、ディスク・アクセスも遅くすることがあるんだ。

(e) プロセス管理
マルチスレッドでPHP動かすのもかなり遅いよ
(メモ: PHPの古いバージョンは、マルチスレッドのモードで安定ではありません)。
(訳者注:今のバージョンでもworkerで動かして平気かどうかは試したことがない)。
webサーバーにあんまり多くのことをさせないでほしい。
サーバーとしてしか使わないなら、X Windowとかまじカンベン。

使ってないプロセスは極力止めよう。

/etc/init.dとかetc/rc.d/init.dとかで。
もしくは、cronを使ってオフピーク時にやるとかね。

(f) 他のサーバーに接続すること
君のwebサーバが他のサーバーで動いているサービスを必要とするなら、それらのサーバーがボトルネックになるかもね。
例えば、複数のwebサーバーからリクエスト受けまくってるDBサーバーとか。

【いつチューニングを始める?】

コーディングが終了するまでチューニングを延ばしたほうがいいって言う人もいるけど、
それはプログラミング・チームが最高のコーディングができて既に良好なパフォーマンスが得られてる場合だけだよね。
そうでないなら、テスト後に相当量のコードを書き直さなきゃいけなくなるかもよ。

アドバイスするとすれば、アプリを設計する前にハードとソフトで若干の初歩的なベンチマークをしておこう。
それからアプリを設計して、パフォーマンスやらセキュリティと柔軟性をキープするようにしよう。

テストデータもいいものを選ばないといけない。
100,000のデータがあるのに100だけ選んでテストしちゃいけない。

これらは僕の同僚がやらかして、後から大部分のコードを書き直す羽目になったんだ。

【PHP最適化のためにサーバーを調整すること】

今日、Apache 1.3とIISの二つで最高のパフォーマンスを得る方法を教えるよ。こいつはHTMLについてもカバーできる。
マルチスレッド(worker)じゃあApache 1.3についてApache 2.0でもパフォーマンスに優位性はないってとあるPHPerが言ってたんだ。
preforkについてはまた今度。

(a) Apache 1.3/2.0
ApacheはUnixとWindowsで利用可能な世界で一番人気のwebサーバーだ。
Apache 1.3は基本preforkを使う。
Apacheが起動すると、HTTPリクエストを扱うための子プロセスを作成する。
親プロセスはさ、『円環の理』みたいなもんだ。子プロセスがきちんと機能/調整してることを確認してる。
HTTPリクエストが増えると、子プロセスも増えるんだ。
HTTPリクエストが減ると、親プロセスはアイドルタイムになってる子プロセスを殺す。
一連のそれらの動作がApacheをより強力にしてる。
たとえ子プロセスがクラッシュしても、親プロセスがそいつを放棄しちゃうからね。

preforkはそれ以外のものほど早くないけど、それが顕在化する前にApacheの他のボトルネックが出てくるから、それはそんなに問題じゃない。
Apacheが頑丈で信頼できるってのが重要なんだ。

Apache 2.0 は、マルチスレッドモードで動作するけど、僕がベンチマークするかぎりそれにはあまり利点はないし、
PHPの互換性(GDとかIMAPとか)で問題が出てくる(Apache 2.0.47 の話だよ)。

Apache は、httpd.confで設定するんだけど、特に以下の項目は重要だ。

MaxClients:
(default)256
作成する子プロセスの最大数。デフォルトは、最高256のHTTPリクエストが並行して取り扱われる。
それ以上は処理待ちになっちゃうよ。

StartServers:
(default)5
起動時にいくつ子プロセスを作っておくか、だよ。

MinSpareServers:
(default)5
アイドルタイムの子プロセスをいくつ作っておくか。
この数値より減少すると、まず1つ子プロセスを作り、一秒後に2つ、次の一秒で32、それ以降は1秒につき4つ子プロセスが作られる。

MaxSpareServers:
(default)10
子プロセスがこれ以上の数になると、そいつは殺されちゃうんだ。

MaxRequestsPerChild:
(default)0
HTTPリクエストの数に対してどれだけ子プロセスを作るか、の設定。
0だと無制限になる。
メモリリークが起こったり、サーバーリソースが遊んでると思ったら、100~10000の間で調整してほしい。

以下の設定は広い範囲で有効だよ。

MinSpareServers 32
MaxSpareServers 64

(訳者注:このへんの数値は同じにしたほうが必要以上にプロセスをforkしないのでいい、っていう意見もある。
参考:http://bit.ly/iody6T いろいろ試してみたほうがいいかも)

windowsのApacheはちょっと違って、子プロセスを作るかわりにスレッドを作る。
なので上記パラメータの代わりに50がデフォルトのThreadsPerChild ってパラメータを使う。
これはApacheで作り出すスレッド数を意味していて、デフォルトだと50のHTTPリクエストが並列して扱われる。
忙しいサーバーなら256~1024の間で調整するといい。

他の役立つパラメータは以下の通り。

KeepAlive:
(default)on
HTTPリクエストは個別にサーバーとの接続をしなくちゃいけないので、
コネクションを維持してオーバーヘッドを減らすためのパラメータとして作られたのがコレ。
同一のソケット接続を複数のHTTPリクエストで再利用するんだ。
画像とかは別の専用webサーバーに入れてる場合は無効にすることもできるよ。
これを設定すれば利用するリソースを大幅に削減できるよ。

KeepAliveTimeout:
(default)15
ソケット接続を生かしておく秒数だよ。
応答が完了したのに接続が生きたままだとネットワークが遊んじゃうから、この値は低くしておくべきだね。
(訳者注:応答時間+αくらいの設定で。長すぎるとwebサーバーがリクエスト送っているDBサーバー等にも負荷をかけます)

MaxKeepAliveRequests:
(default)100
リクエスト数がこの値に達すると接続を終了するよ。
MaxClientsまたはThreadsPerChild以下のなるべく高い値に設定したほうがいいね。

TimeOut:
(default)300
処理待ち何秒で接続を切るか。
クライアント数が少ないなら、低く設定してもいいよ。

DNSルックアップ(IPからホストの逆引き)を必要としていなくて個々のディレクトリでhtaccseeファイルを読み込まない場合、
以下のように値を設定するといい。

# disable DNS lookups: PHP scripts only get the IP address

HostnameLookups off

# disable htaccess checks

AllowOverride none

シンボリックリンクにアクセスしてもセキュリティ上問題ないなら以下のようにしよう。

Options FollowSymLinks

#Options SymLinksIfOwnerMatch

(訳者注:FollowSymLinksだと、指定したディレクトリ内でシンボリックリンクにアクセスできる。
SymLinksIfOwnerMatchだと、シンボリック先のユーザー一致を確認するので結構な負荷。)

(b) IISチューニング
(訳者注:省略します^q^読みたい方は原文を読んでね。)

【PHP’s Zend Engine】

Zend EngineってのはPHPでの内部コンパイラかつ実行エンジンだよ。
Zeev SuraskiとAndi Gutmansが開発したものの省略形さ。
PHP4初期だと、以下のように動作してた。

Zend EngineでロードされたPHPスプリクトはZend opcodeでコンパイルされ、それが実行されるとHTMLとしてクライアントに送信される。
opcodeはその後メモリから排除されて停止される。
今ではこれらのプロセスをより早くするプロダクトや技術のオプションがいっぱいある。
下の画像がソレ。影のついてるのがオプションだよ。

PHPスプリクトはメモリにロードされて、Zend opcodesでコンパイルされる。
現在では、Zend Optimizerを使ってこれを最適化できる。
スプリクトによっては0~50%スピードを上げることができるだろう。

opcodesはそれを停止した後は廃棄されてたんだけど、現在ではオープンソース・プロダクトや商用の
Zend Acceleratorによってそれらをキャッシュすることもできるんだ。
Zend Optimizerと互換性を持つ唯一のものはZend Acceleratorだ。
これはスプリクトのロードとコンパイルを不要にすることで、実行時間を10~200%早くできるよ。

このへんの詳細はウィキペディアを見てよ。
あとZendのサイト(http://zend.com)。こいつはマジですごいから。
ちなみに僕は大人気のeAcceleratorを使ってるよ。
あとAPC。
僕はPHP6が出来るころにはAPCが組み込まれてるんじゃないかと思ってる。
(訳者注:ちなみにeAcceleratorとZend Optimizerは併用が可能です。)

【キャッシュは最高の加速装置】

高性能の秘密の一つはさ、早いPHPコードを書かなくても、メモリにPHPコードやHTMLを
キャッシュしてそれ自体を実行しないことにあるんだ。
PHPは一度動くだけでHTMLをキャプチャしてロードする。
データを定期的に更新したいならば、expiresを出力すればいい。
そういうクラスやライブラリは沢山ある。PEAR CacheとかSmartyテンプレートとかね。

ついでにさ、クライアントに送信されるHTMLは圧縮することもできるんだ。
PHPスプリクトの最初にこいつを入れてごらん。


ob_start("ob_gzhandler");

これで多い時には50%~80%もHTMLを圧縮できる。
ネットワークの負荷がだいぶ下がるよ。
もっとも、CPUの負荷は増えちゃうけどね。

【PEAR CacheでHTMLをキャッシュする】

PEAR Cacheは、HTMLや画像といった複数のファイルをキャッシュできるクラスだよ。
一般的な使い方はHTMLのキャッシュだろうね。
以下のコードは、start()からend()の間のテキストを出力するかキャッシュに保存する
Output bufferingクラスだよ。

require_once("Cache/Output.php");

$cache = new Cache_Output("file", array("cache_dir" => "cache/") );

if ($contents = $cache->start(md5("this is a unique key!"))) {

#
# aha, cached data returned
#

print $contents;
print "Cache Hit";

} else {

#
# no cached data, or cache expired
#

print "Don't leave home without it…";
# place in cache
print "Stand and deliver";
# place in cache
print $cache->end(10);

僕がこれを書いてから、さらに優れたPEARのキャッシュクラスであるCache Liteも作られた。

このCacheコンストラクタは、最初のパラメータにストレージドライバを取得する。
これにはファイルやデータベース、共有メモリを利用できる。
pear/Cache/Containerディレクトリを見てごらん。
ちなみにUlf Wendelのベンチマークによると、ファイルを指定するのが最高のパフォーマンスらしい。
2番目のパラメータはストレージドライバのオプション。
キャッシングディレクトリの位置を指定するcache_dirと、ファイルの接頭語として設定するfilename_prefixだ。
不思議なんだけど、キャッシュの保存期間はオプションではセットされない。

データをキャッシュするために、キャッシュのキーとなるユニークなIDを作成するんだ。
僕達はmd5()を使ってるけどね。

start()でこのキーを使ってキャッシュを探す。キャッシュが存在しない場合は空の文字列を返す。
そしてend()が呼ばれるまで、echoやprintは全てキャッシュで出力される。
end()でバッファの内容を返すんだけど、end()は最初のパラメータにキャッシュの満了期間を使う。

PEAR Cacheを使うもうひとつの方法は、変数または他のデータを保存することだね。


require_once("Cache.php");
$cache = new Cache("file", array("cache_dir" =< "cache/") );
$id = $cache->generateID("this is a unique key");

if ($data = $cache->get($id)) {

print "Cache hit.
Data: $data";

} else {

$data = "The quality of mercy is not strained...";
$cache->save($id, $data, $expires = 60);
print "Cache miss.";

}

データを保存するにはget()を使うよ。
ユニークなキーがすでにファイル名にあるなら、generateID()を省略することができる。

save()はデータをシリアライズするので、オブジェクトや配列も保存することができる。
最後のパラメータでキャッシュの保存期間を設定するんだ。
こいつは秒単位とかUnixスタンプ、または0に指定すると無期限になる。

キャッシュしたデータを検索するにはget()を使ってね。

$cache->delete($id)でデータ項目を削除できるし、
$cache->flush()ですべてのキャッシュを破棄できる。

【SquidとHTTP Accelerators】

(訳者注:Squidつかおうぜ!ってことなので省略^q^)

【Distributed Caches】

(訳者注:memcacheつかおうぜ!ってことなので省略^q^)

【ベンチマークを使って】

今まではパフォーマンスの問題をカバーしてたけど、次はいよいよ骨格となるベンチマークの話をするよ。
webサーバーで現実的なベンチマークをしたければ、HTTPリクエストを送信するツールが必要だ。
Unix上でのベンチマークツールといえば、Apacheがリリースしてるabが一般的だね。
windows上では、マイクロソフトが提供してる無料の分析ツールを使うことができる。

これらによって複数のHTTPリクエストを並列に処理でき、詳細なデータを得ることができる。
Unixサーバー上では、ベンチマーク中に『vmstat 1』コマンドでモニタリングできる。
こいつは毎秒ごとにディスクI/Oやメモリ使用量、CPU負荷を教えてくれるんだ。
あるいは『top d 1』コマンドでプロセスごとのCPU負荷を監視することもできる。
1秒毎だとちょっと変動が激しいから、僕は『vmstat 5』を推奨してる。

windows2000以降だと、パフォーマンス・モニタまたはタスクマネージャーで確認できる。
もしオーバーヘッドについて心配することはないけど特定のコードが・・・って時には
microtime()を仕込んでベンチマークすることもできるよ。

$time = microtime(true);

#
# code to be benchmarked here
#

echo "Time elapsed: ",microtime(true) - $time, " seconds";

あるいは、プロファイリング ・ツール(例えばAPDまたはXDebug)を使用することもできるよ。

【ベンチマーク・ケーススタディ】
(訳者注:ここからしばらく長いので要約すると、
『PHPの最適化にはネットワーク、メモリ、CPU、Apacheの子プロセスの動作、PHPスプリクトとデータベース構成を含む
複数のソフトウェア・サブシステムの理解を必要とするよ!』
っていう話です。

【コードの最適化】

なんでこんなにPHP以外の問題について語るのかって疑問を持ってる読者もいるよね。
思い出してごらん、PHPはすっげー早い言語だって。
それを遅くしてるボトルネックは、多くはPHPの外側にあるんだ。

大部分のPHPスプリクトは単純なんだ。
それは若干のセッションを読んで、コンテンツの管理システムまたはデータベースからデータをロードして、
適切なHTMLに変換してクライアントに返す。
スプリクトが0.1秒で完了してレイテンシが0.3秒だと仮定すると、PHPの演算時間はわずが33%だ。
スプリクトの速度を20%改善したところで、応答速度は0.28秒早くなるだけ。
取るに足らない改善だよね。
もちろんそれでも、サーバーは20%分他の処理ができるからスケーラビリティは改善されるけど。

上の例はさ、僕達がやるべきことじゃなくて、やめるべきことを意味してる。
僕達はコードの最後の1%まで改善してそれを誇ってちゃいけない、
しかしより高いリターンを得られる、コードの価値ある領域を最適化することに時間をかけなきゃいけない。

【ハイリターンなコード最適化】

そういう高いリターンを得られる場所は存在する、
僕達のコードを散らかすループは存在して、そいつを繰り返すごとにスピードは落ちる。
いくつかの例を使って、何を最適化できるかを理解しようか。

例1:
配列を出力する単純な例がここにある。


for ($j=0; $j<sizeof($arr); $j++)
echo $arr[$j]."<br>";

こいつはこうすればもっと早いよね。


for ($j=0, $max = sizeof($arr), $s = ''; $j<$max; $j++)
$s .= $arr[$j]."<br>";
echo $s;

最初のやつは、評価式$jでもsizeof($arr)は不変だから、変数$maxにキャッシュすればいい。
専門用語だと、こいつは「ループ不変の最適化」っていうんだ。

第二の問題は、PHP4では、全部を文字列に保存して一度だけコールするより、複数回echoで出力するほうが遅いってことだ。
なぜなら、echoはそれをTCP/IPパケットでクライアントに送信することが必要なハイコストな処理だからだ。
もちろん、変数に文字列を足していくことはメモリも使うし、複雑なトレードオフではある。

上記のコードは、output bufferingを使用することでもスピードアップできる。
これは内部的に出力する文字列を集めて、スプリクトの終わりにそいつを出力するんだ。
これによって、メモリとレイテンシを犠牲にしながらも大幅にネットワークのオーバーヘッドを減らすことができる。
echo文だけで作った僕のコードの一部では、15%もの改善がなされた。


ob_start();
for ($j=0, $max = sizeof($arr), $s = ''; $j<$max; $j++)
echo $arr[$j]."<br>";

ob_start()による出力バッファリングは全てのPHPスプリクトの最適化に使用できる。
ランニングタイムが長いスプリクトでは、いくらかのフィードバックを返すために定期的に出力したい場合もあるよね。
これはob_end_flush()で実現できる。
この関数もoutput bufferingをオフにするから、flushした後にもう一回ob_start()をコールしたくなるかもね。

要約すると、ループ不変量を最適化することとoutput bufferingを使うことでコードを
スピードアップさせることが出来るってのをこの例は示してる。

例2:
以下の例では、取得した列をフォーマットしてechoするPEAR DBをループしてみるよ。
この実行時間をベンチマークすると、SQL接続やクエリの実行時間を除いて、10.2msだった。


function FormatRow(&$recordSet)
{
$arr = $recordSet->fetchRow();
return $arr[0].'/'.$arr[1];
}

for ($j = 0; $j numRows(); $j++) {
print FormatRow($rs);
}

例1のとおりコードを最適化したら8.7msになった。

function FormatRow(&$recordSet)
{
$arr = $recordSet->fetchRow();
return ‘<strong>’.$arr[0].'</strong><em>’.$arr[1].'</em>’;
}

ob_start();

for ($j = 0, $max = $rs->numRows(); $j < $max; $j++) {
print FormatRow($rs);
}

$maxの使用で0.5ms、ob_start()の使用で1msから1.5msのスピードアップを達成した。
さらにループの最適化により、コードの単純化とスピードアップを達成できる。
以下の例だと8.5msになったよ。


function FormatRow($arr)
{
return '<strong>.$arr[0].'</strong><em>'.$arr[1].</em>';
}

ob_start();

while ($arr = $rs->fetchRow()) {
print FormatRow($arr);
}

さらにもう一つの最適化により、0.1ms減らした8.4msになった。


ob_start();

while ($arr = $rs->fetchRow()) {
print $arr[0].'/'.$arr[1];
}

これをPEAR Cacheに変えて、キャッシュを使うことにより実行時間は3.5msにまで下がった。


require_once("Cache/Output.php");

ob_start();

$cache = new Cache_Output("file", array("cache_dir" => "cache/") );

$t = getmicrotime();

if ($contents = $cache->start(md5("this is a unique kexy!"))) {
print "Cache Hit";
print $contents;
} else {
print "Cache Miss";

##
## Code to connect and query database omitted
##

while ($arr = $rs->fetchRow()) {
print '<strong>'.$arr[0].'</strong><em>'.$arr[1].'</em>';
}

print $cache->end(100);
}

print (getmicrotime()-$t);

上の例からも、コードの微調整はob_start()のような最適化もしくはHTMLキャッシュのように大幅に速度を向上させないことが分かる。

【オブジェクト指向プログラミングの最適化】

2001年3月にPHP4でベンチマークした結果から僕は若干のアドバイスを得た。
要点は以下の三つだよ。

1.使用前にすべての変数を初期化しよう。
2.値に二度以上アクセスするなら、全てのグローバル変数やプロパティをローカル変数にキャッシュしよう。
3.頻繁に使われるメソッドは派生クラスに置いてみよう。

ただし、PHPは継続して改善されてるから、将来的にこのへんは変わるかもしれないね。

【さらに詳しく】
クラス内で定義してるメソッドをコールするのは、通常のメソッドをコールするより2倍遅いことに僕は気付いたんだ。
以下はメソッド内での話。

a.メソッド内でローカル変数の使用をインクリメントするのが一番早い。

b.グローバル変数を使うことはローカル変数より2倍遅い。

c.オブジェクトのプロパティをインクリメントするのはローカル変数よりも3倍遅い。

d.未定義のローカル変数をインクリメントするのは初期化されたものより9~10倍遅い。

e.関数内で使用しないグローバル変数を宣言するのはローカル変数の宣言にくらべて遅い。
PHPはグローバル変数が存在しないかいちいちチェックするからね。

f.クラス内のメソッドを増やしてもパフォーマンスにそれほど影響はない。

g.派生クラス内のメソッドは親クラス内のものより早い。

【調整のサマリー】

a.使用しているソフトウェア、そしてOS(ネットワークやサーバー・ハードウェア)の知識を深めることで
コードとシステムの最適化が可能になる。

b.できるだけ多くのキャッシングを使おう。僕はSquidとmemcacheを使ってるよ。

c.PHPスクリプトについて、最もハイコストなボトルネックは通常CPUだろうね。

d.PHPをインストールする時に”configure — enable-inline-optimization ” オプションをつけると一番早いPHPになる。

e.データベースを調整して、WHERE句に用いられるフィールドにインデックスをつけよう。

f.めったに更新されないデータがあるならHTMLキャッシュを使おう。毎分更新されるとしても、キャッシュと同期すればいい。
10倍くらいパフォーマンスが上がるよ。

g.複雑なコードは早目にベンチマークしておこう。xdebugか、有料だけどZend Studioとかで。

h.opcode cacheを使うことを考えよう。10~200%くらいスピードアップするよ。

i.コードの最初にob_start()を使おう。5~15%スピードアップする。ob_gzhandler()でgzip圧縮することもできるよ。

j.Zend Optimizerのインストールを検討しよう。ただし一部のコードは逆に減速するかもしれない。
君のコードに多くのループが含まれている場合、Zend Optimizerは非常に有効だよ。

k.ループの外に不変の定数を動かして最適化しよう。

l.可能な限り配列または文字列にデータを入れてからechoしよう。

m.一つの大きな文字列に小さな文字列を複数付け足していくなら、ob_start()を使うのが最速だよ。
終了時にはob_get_contents()を使って内容を取得しよう。

n.可能な限りオブジェクトや配列は関数として参照しよう。短いコードやコード・メンテナンスが問題でないなら
グローバル変数にしてもいいと思うよ。

o.セッション変数を多く使うPHPスプリクトがあるなら、共有メモリにセッションをストックするモジュールを使うよう
PHPの再コンパイルを検討しよう。

p.文字列検索はstrpos() > preg_match () > ereg()の順に早い。
文字列の置き換えはstr_replace() > preg_replace() > ereg_replace()の順に早い。

q.switch文の一番最初に最もよくあるケースを持ってこよう。

r.正規表現によるXMLパースは、DOMまたはSAXを使うよりかなり早い。

s.メモリ使用量を減らすために、大きな配列などは用が済んだらunset()しよう。

t.派生クラス内のメソッドは親クラスで定義されるものより早いので、頻繁に使われるメソッドは派生クラス内に複製しよう。

【役に立たない最適化】

いくつかの最適化は役に立つけど、それ以外はそうでもない。
でもって、PHPは内部的に変わっていくから、それらは時代遅れになるだろうね。

以下に挙げるのはそういったものだよ。

a.echoはprintより早い
echoは返り値がない分printより早いと言われてる。でも僕がPHP4.3でベンチマークすると、
一部のスプリクトではprintのほうが早かった。

b.コメントを止めるとスピードアップする
PHP3時代の神話だね。

c.’var=’.$var は “var=$var” より早い
PHP4.2以前ではそうだけど、4.3以降で修正されたよ。若干オーバーヘッドが減る部分もあるけど、取るに足らないものだよ。

【参照渡しってスピードってどうよ?】

参照渡しにしたからスピードアップするってことはない。
以下のコードを見てみよう。


function TestRef(&$a)
{
$b = $a;
$c = $a;
}
$one = 1;
ProcessArrayRef($one);

以下は参照渡しをしない同様のコード。


function TestNoRef($a)
{
$b = $a;
$c = $a;
}
$one = 1;
ProcessArrayNoRef($one);

PHPは値を見落とした時に変数を複製したりはしないんだけど、内部的にカウントされる高速参照を行っている。
TestRef()では参照元を追跡しなきゃいけないので$bと$cは代入されるまでそれだけ時間がかかる。
この場合はTestNoRef()のほうが早い。

対照的に、配列やオブジェクトをパラメータとして持っている時は、参照渡しはパフォーマンスを向上させる。
これは、配列やオブジェクトはカウントされるような参照を行わないからなんだ。

次のコードを見てみよう。


function ObjRef(&$o)
{
$a =$o->name;
}

is faster than:

$function ObjRef($o)
{
$a = $o->name;
}

注:PHP5では、オブジェクトのパラメータは自動的に参照渡しになる。
パフォーマンスはかなり良くなってるはずだよ。

—–ココまで和訳—–

誤訳があってもかんべんしてにゅる。