Archive for 4月 2014

phpフレームワークとの付き合い方

フレームワークを使うならメモリ消費について諦めなければいけない点かもしれません。

よくあるフレームワークで考えると、indexというメインコントローラがアクションクラスのインスタンスを作成します。
このメモリが解放されるタイミングは、どこになるでしょうか?
恐ろしい事に(ほぼ)全ての処理が終わってindexコントローラーからインスタンスが解放されるタイミングです。

なので、アクションクラスではprotectedやpublicのメンバ変数(プロパティ)はなるべく使用しない方が良いです。
インスタンスが解放される=メインコントローラーの最後までメモリ解放されません。

アクションメソッドの中でマスターデータなどを取得した場合もunsetしない限りはスコープから外れるまで解放されません。
スコープから外れる=アクション終了=(ほぼ)全ての処理が終わっている

オブジェクトを多用するのは、zend_class_entryに設計図が登録されるので、クラス数だけメモリを消費します。
クラスを一杯作成するのはメモリ消費には繋がります。
ですがstaticメソッドを持ったクラスを作るのは別に問題だとは思いません。
これはfunctionを作ってもfunctionが登録されるのと同じだからです。

どうせ数msで終わるのだから、頑張ってメモリ解放しなくても良いというアプローチこそ
lightweight languageに相応しい気がしますので、あまり神経質にならなくて良い気がします。
アクションクラスのメンバ変数に大きなマスターデータを持たせるとかは、やめた方が良いかもしれませんけども。

久しぶりに連日技術的なネタを投稿して充実していましたが、明日ソードアートオンラインのゲームが出るので若干更新ペースが落ちるかもしれませんw

php extensionのススメ3(メモリ消費)

同時アクセス数が多いという事はweb(ap)サーバーのメモリ消費に注意しなくてはいけません。
webサーバーレイヤーではnginxが有利だと言われる部分はアプリレイヤーでも考慮する必要があります。

phalconフレームワークとCakePHPで単純にHelloWorld的なプログラムを実行してみます。
テンプレートでmemory_get_usage()をechoするだけのプログラムです。

phalconフレームワークは400Kくらいです。
phalcon

cakephpは10倍の4Mくらいです。
cakephp

DB接続すらせずに、Actionも空っぽでテンプレートでecho memory_get_usage()しているだけのプログラムで10倍のメモリ消費となります。
phalconフレームワークは早いだけではなくメモリ消費という最重要項目に関しても優秀です。
これは、先日説明したphpのzvalを思い出して下さい。
c言語のネイティブレイヤーでスタック変数を定義するのと、phpレイヤーで変数を定義するのとではメモリ消費が違います。
同じ処理をするにしても、ネイティブ側で例えばint型(私のPCは32bitなので4バイト)で良い値があるとします。
phpレイヤーでしか定義出来ない場合は、まず$aというポインタ変数として(私のPCは32ビットなので)4バイト
ポインタの参照先の実態として、zval構造体+共用体のメモリを消費します。
ネイティブで済ませる部分が多ければ多いほど、zvalの呪縛から解放されメモリ消費を抑える事も出来ると言うことです。

Actionやテンンプレートが空っぽでこれなのですから、CakePHPをそのまま使ってしまうと1アクセス当たり平均10MBは覚悟しておいた方が良いかもしれません。
対象サーバーでの同時アクセス数を100に設定したと仮定すると軽く見て1GBは覚悟しておくべき数値かと思われます。
1000アクセスだとすると10GBですね。
ゲーム開発の場合はマスターデータをKVSから全件取得したりもしますので、多めに考えておいた方が良さげです。
あくまでアプリレイヤーのメモリ消費ですので、webサーバーは別途メモリを消費します。
そしてAPCにキャッシュさせたデータもメモリを消費します。
APCのコンパイルキャッシュもメモリを消費しますので、プログラムの本数分消費します。

その他サーバー監視プロセスやログ収集プロセスなど最低限動いているプロセスもメモリを消費します。
これから考えてもApacheとnginxの項でも書いた、C10Kという単位は普通に馬鹿かと言いたくなる単位なのは、ご理解いただけるかと思いますw

スーパーライト事件が残した傷跡?

talesasta
期間限定ガチャで登場した物はいつか再登場して当然である。
新規参入者が過去のレアカードを入手する機会が無い方が不平等であるとは思わないだろうか?
わざわざ追記で、「上記の期間終了後も、再び期間限定で登場する予定」などと断りの文章を載せなければいけない程893のユーザーは恐ろしい。
これは、期間限定だから課金したのにまた出してる!返金!!という893クレーマー防止にしか見えない。

第74回 皐月賞(GI) レース回顧

ほぼパーフェクトと言って良い予想でした。

勝つのはこの馬と言ったイスラボニータの勝利!
世代最上位と評した2頭でのワンツーフィニッシュ!
4強に割り込むのはウインフルブルーム!

しかし戸崎くんもようやく展開利を活かすという競馬を覚えてくれたようですね。
今回のアジアエクスプレスの騎乗はよくやったと褒めてあげたいです。
ただ出足で負けて、外からトゥザワールド(より内枠だったのに)を交わしていく形でしたので、前半に脚を使ってしまった分フルブルームを交わすことすら出来ませんでした。
それでも展開利を活かすために、積極的な競馬をしたのですから悲観する事はありません。
前に行って正解だったのは着順やウインフルブルームが証明しています!

しかし予想外だったのがワンアンドオンリー
この馬は本物だったんですね・・・、やはりラジオNIKKEI馬は強かった!
ダービー本命馬の誕生です!
この馬からイスラボニータとトゥザワールドに流すだけで良さそうな気がします。

第74回 皐月賞(GI) 予想

最も展開利がありそうだったバンドワゴンの回避により堅軸がいなくなりました。
しかしオッズ的には旨みがあるレースになりました。
人気が割れているので、上位4頭でも元が取れる簡単なレースで勝負出来ますね!

②イスラボニータ
ハイレベル新潟2歳Sから共同通信杯まで、世代上位である事を証明してきたこの馬は実績・実力間違いなく最上位。
多分勝つのはこの馬だと思ってます。
しかも鞍上は蝦名ですから、前目の内をロスなく回って4角先頭くらいの感じで回ってくるんじゃないでしょうか?
皐月賞は前が有利ですからね、4強の中で最も展開有利だと思っています。

⑦トーセンスターダム
無敗馬で鞍上武豊とくれば現時点で1番人気になってるのも仕方がないでしょうか?
最強牡馬と思われていたバンドワゴンをきっちり差しきったのですから、そこそこ強いのは間違いありません。
バンドワゴンがトゥザワールドより強いと仮定するなら、かなり強いのでここで初めて評価出来ますね。
しかし鞍上は武豊、前が絶対有利の皐月賞でも最後方近くから運んで届かずという結果になりそうな可能性が高いです。

⑯アジアエクスプレス
唯一の敵は鞍上の戸崎くんのみ、実力上位でも鞍上不安で人気落ちは仕方ありません。
スプリングSも戸崎くんとデムーロの実力差が出ただけで位置取り逆なら勝っていたレース、実力は本物です。
まぁ内で包まれるのを嫌うのは分かりますので、出遅れたデムーロが内から交わしていって4角外に出せたのも結果論ですからね。
ただ勝負所でクラリティシチーなんかを交わすのに苦労する位置取りになるのは、やはり戸崎くんが下手だからでしょう。
その点外目の枠を引いたのはいくら下手糞でも包まれないですし良いと思います。

⑰トゥザワールド
トゥザ一族の末裔なのですから、この馬も血統人気しますね。
鞍上が川田というのは不安ですが、トゥザワールド自体も弱メンとしか勝負していませんからね。
弱メン相手とは言え、弥生賞の横綱相撲は強すぎて世代最上位なのは間違いありません。
自在性のあるこの馬で川田がどこに付けるのか?という感じですが、外を引いたので中団ですかね。

この4強に展開利を味方に付けれる上に世代上位のウインフルブルームが割り込めるかどうか?
この馬はとにかく馬体がカッコイイ!
うまくいけばウインフルブルームは単騎逃げの展開になりますから、テレビ馬的に逃げてくれるのも有りですね。

プロジェクトスケルトンの作成

プロジェクトの設定とかは全くしていませんが、とりあえず動きました。
これでチュートリアルに進めます。
phalcon

Phalcon Developer Tool

Phalconフレームワークの使い方を勉強していきたいと思います。

ここからPhalcon Developer Toolsをダウンロードしてコマンドラインからプロジェクトスケルトンが作れるようです。
http://docs.phalconphp.com/en/latest/reference/tools.html

好きな所にインストールすれば良いのですが、私は/usr/local/binにインストールします。

配列(ハッシュ)よりオブジェクトのメモリ消費が少ないという嘘?

前回のエントリーの続きです。

参考サイト①に記載されている内容は別に間違いではありません。
前提条件としてオブジェクトに有利な形になる形の説明をしているだけですね。
参考サイト①の参考リンクにある参考サイト②の説明をされているだけです。

まず参考サイト①にも記載されていますが、PHPの言語仕様の説明をします。
確かPHP5から変わったハズなんですけど

①は値のコピーでは無く、$aの実態への参照となります。
①のタイミングでコピーしてしまうとメモリの消費が激しいので、値に変化がない間はメモリを節約する為に言語仕様が変わりました。
なので初めて値が変わった②のタイミングで新しいzvalがメモリに確保されます。
xdebugをインストールすると簡単に確認できます。

①は$aしかないので$aの実態の参照カウントは1です。
②は$bからも参照されたので$aの実態の参照カウントは2になりました。
③は$aの実態を参照しているので②と同じ結果になります。
④は$bは値変更により新しい実態を持ったので参照カウントが$aの1つに戻りました。
⑤は$bの新しい実態なので参照カウントは$bの1つです

という基本的な言語仕様を前提条件として理解して下さい。

昨日説明したzend_class_entryというのがクラスの設計図だと思います。

この先はphpのユーザークラスをコンパイルした後の世界なので、ソースコードもよく分かりません。
public $member0 = 0;
例えば上記のようなプロパティに初期値が設定されている時は、
①0を表すzval
②default_properties_tableが①を参照
③new Classした時にnew_class.member0はdefault_properties_table[member0]を参照
という動きになるので参考サイト①のように参照カウントが3から始まるのだと思います。

この動きと配列を比較するのであればこういう形で初期化するのが平等な計測になります。

結果は当然配列の方が早くてメモリ消費も少ないです。

メモリ消費が少ないのは、昨日説明した階層を思い出して下さい。
内部的にはzval->obj->HashTableでありプロパティの実値部分のHashTable部分のメモリ消費が同じならobjの部分が配列よりも多くなります。
参考サイト②は間違いと言っている訳ではありません、あえて配列が不利になる動きで比較をしているという事です。
逆に言うと私があえて配列が有利になるように初期化しただけですw
同条件なら配列の方が早くて軽量!
これはphpの内部データの持ち方から間違いありませんから、私にとっては予想通りの結果になったつー事ですね。

如何だろうか?
ネットには単純に鵜呑みにしてしまうと嘘になってしまう情報がゴロゴロ転がっているのである。
真実はいつもひとつ!嘘じゃない情報も嘘になる事がある。

オブジェクトより配列(ハッシュ)の方が早い理由

このエントリーは別に配列を使っての高速化を推奨する訳ではありません。
オブジェクトなんて使うより配列の方が早いからオブジェクトと配列の選択肢がある時の参考にして貰えればという感じですね。
例えば散々例に上げているPDOも結果行の形式をオブジェクト・配列から選択するパラメーターがありますよね。

これを説明するに当たって、とても素晴らしいサイトを見つけました。
2009年と古い内容ですが、図解してくれているのでとても分かりやすいです。
参考サイト:http://d.hatena.ne.jp/yokkuns/20090614/1244994082

所謂何でも型というのはc言語の共用体の拡張だというのは、どの言語でも同じだと思います。
私が業務系でVBをやり始めた頃はC言語出身者もゴロゴロ転がってましたので、VBのVariant型は共用体拡張だと聞いていたので想像はついていました。
phpの何でも型の正体?呼び名?はzvalと言います。

zval_val共用体とzval構造体

参考サイトの図を合わせて見ながら読んで貰うと分かりやすいと思います。
zval構造体の中にtypeという項目があります。
これが何でも型だけど内部的にstring型だとか持ってる奴ですね。
図の右に飛ぶと共用体に移ります。
共用体っつーのは同じメモリ領域を複数の型で共用できるそのまんまの奴なんですが、c言語を知らない人は分かりやすい図入りのサイトを探して下さい。
大事なのは、配列はHashtableで、オブジェクトはzend_object_valueで型が違う=データの持ち方が違うという事です。

脱線してzval構造体のrefcount_gcがガベージコレクタの説明によく出てくる参照カウンタつー奴ですね。
GCはこのヒープ変数がどこからも参照されていない時(=0)に解放する仕組みです。
面白いのでついでに覚えておくと自己満足できます!
オブジェクト側にもrefcountとか出てくるので、GC側のソースも何かあれば見てみたいと思います。

zend_object_value構造体

ここからの説明の仕方に迷うというか文章力を試されてしまいますね。
まずは、表題通りの何故オブジェクトの方が遅いのか?について説明したいと思います。
参考サイトで図解してくれていますので私が簡単に書くのと合わせて見て下さい。
ハッシュの場合は
zval->value->ht->zval->value
オブジェクトの場合は
zval->value->obj->(Hashtable)get_properties->zval->value
単純に矢印が多い=目的の値に辿り着くまでの経路が多いと思って下さい。
参考サイトにも書かれているように、最終的にオブジェクトのプロパティは内部的にはハッシュとして扱われるんです。

で、オブジェクトはクラスの型を管理する情報と実際にヒープメモリに展開したインスタンスの情報を管理してるぽいですね。
やたらとceという変数が出てきますが、これがzend_class_entryという奴でクラスの設計図の情報ぽいですね。
さっきのzend_object_valueのhandleとhandlersというのがインスタンス側の管理情報で、グローバル変数objects_storeで管理してるぽいです。

その中のバケットという中にオブジェクトを詰め込んでいってるポジションがhandle的な感じぽいです。

糞長すぎて混乱してしまいますねw
実際に使用されている部分を抜粋するとこんな感じです。
EG(objects_store).object_buckets[handle].bucket.obj;

さらに最終的にこのバケットに入ってる_store_bucket->obj->objectがこれになるんだと思うんですが、ちょっとそろそろ眠たいのでソース見て確証するのは後日で・・・。

なので、さっき書いた
zval->value->obj->(Hashtable)get_properties->zval->value
を詳細に書くと
properties_hash = zval->value->obj->handlers->get_properties(EG(objects_store).object_buckets[zval->value->obj->handle].bucket.obj);
多分こんな感じの糞長い動きでようやくプロパティ配列が取れる感じだと思います。

さて配列の方が早い理由はご理解頂けたでしょうか?
分かり難い人は

より

の方が早いのと同じだと思って下さい。
$hoge->0->1->$iより1->$iの方が目的の場所に辿り着く経路が短い=早いです。

次に、参考サイトに書かれているオブジェクトのメモリ消費量について書きたいと思いますが、やっぱり長くなったのでエントリーを分けたいと思います。
to be continued

クエリ発行回数の削減(SELECT)

MySQLのプロシージャは複数結果セットに対応しています。
これを利用しない手はありません。
昨日、関連テーブル一括削除のエントリーで1:nの親子関係テーブルを複数JOINするとねずみ算式に結果セットの行数が膨らむ話をしました。
そのようなケースでも利用できますし、JOIN出来ない関係ないデータを1クエリで同時に取得するという用途にも利用出来ます。

1:nの子テーブルがあるパターンです。
取得するカラム数も結果セット毎に違います。

スクリプトを準備します。
PDOには複数結果セットの為のPDOStatement::nextRowsetというメソッドが準備されています。

3回クエリを発行するのと同じ結果が1クエリで取得出来ます。

比較の為に普通に3回SELECTするテストスクリプトも用意します。

今回はループじゃなくてabでベンチマークを取ってみます。
ネットワークを介するデメリットが対象なのでDBサーバーとWEBサーバーはちゃんと別のVMになっています。
私のPCはシングルコアの糞PCにLubuntu入れて延命してる環境なので負荷は軽めにしています。

秒間38.35リクエストが64.57リクエストまで跳ね上がっています。
ネットワークを介するデメリットは想像以上ではないですか?
スレーブから取得するデータであれば、IF文等の制御ロジックを含むプロシージャも許容範囲となるかもしれません。