狠狠撸

狠狠撸Share a Scribd company logo
本当に恐い
パフォーマンスが悪い実装
Masakazu Nagaya
2013年09月14日(土曜日)
Overview
? 大規模サイトでパフォーマンスを著しく劣
化させる非効率な実装例や、その改善例
を紹介します。
2
アジェンダ
? はじめに
? パフォーマンスが悪い実装の紹介
? 失敗を繰り返さないために
? まとめ
3
はじめに
4
誰でも失敗する
? プログラムを書く全ての人間がスーパー
プログラマーではない
? 常に完璧で失敗をしない人間はいない
? 失敗は必ず発生する
5
大切なこと
? 失敗から目をそむけない
? 失敗を隠さない(共有する)
? 失敗を繰り返さない
6
パフォーマンスが悪い実装の紹介
7
その1
? リソースの確保と解放のタイミングと回数
に要注意
8
問題の実装
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class BlackListDB {
const DBPATH = "/tmp/db.gdbm";
public function isBlock($id) {
$dbh = dba_open(self::DBPATH, "r", "gdbm");
if ($dbh === false) {
return null;
}
$ret = dba_exists($id, $dbh);
dba_close($dbh);
return $ret;
}
}
問題点
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class BlackListDB {
const DBPATH = "/tmp/db.gdbm";
public function isBlock($id) {
$dbh = dba_open(self::DBPATH, "r", "gdbm");
if ($dbh === false) {
return null;
}
$ret = dba_exists($id, $dbh);
dba_close($dbh);
return $ret;
}
}
改善した実装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class BlackListDB {
const DBPATH = "/tmp/db.gdbm";
private $_dbh = null;
function __construct() {
$this->_dbh = dba_open(self::DBPATH, "r", "gdbm");
}
public function isBlock($id) {
if ($this->_dbh === false) {
return null;
}
return dba_exists($id, $this->_dbh);
}
function __destruct() {
dba_close($this->_dbh);
}
}
11
ポイント
? isBlock()の数だけopenされると遅くなる
? openの処理コストも内部でシステムコー
ル(open/mmap)を呼ぶので大きい
? 無駄な処理を減らす
12
検証方法
? テストコードをサーバ上に配置
ツールによる負荷テストを実施
13
1
2
3
4
5
6
7
8
<?php
$num = isset($_REQUEST["num"]) ? intval($_REQUEST["num"]) : 128;
$obj = new BlackListDB();
for($i = 0;$i < $num; $i++) {
$id = "dummy_id_".$i;
printf("%s => %d? n", $id, $obj->isBlock($id));
}
比較
14
更なる改善
? リクエスト毎に毎回Open/Closeするのは
もったいない
15
更に改善した実装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class BlackListDB {
const DBPATH = "/tmp/db.gdbm";
private $_dbh = null;
function __construct() {
$this->_dbh = dba_popen(self::DBPATH, "r", "gdbm");
}
public function isBlock($id) {
if ($this->_dbh === false) {
return null;
}
return dba_exists($id, $this->_dbh);
}
}
16
Persistent Resources とは
? プロセス単位でオープンしたリソースを永
続的に保持し、次回のリクエストで再利用
する
? Persistent Resourcesの例
sqlite_popen(), pfsockopen(),
oci_pconnect(), mysql_pconnect() など
17
(PHP5.5ではmysql_pconnectは廃止されます。代替の関数を利用すべきです)
Life Cycle
18
MINIT
RINIT
Script Execution
RSHUTDOWN
RINIT
Script Execution
RSHUTDOWN
RINIT
Script Execution
RSHUTDOWN
MSHUTDOWN
Apache Child Process
リソース確保
再利用
再利用
リソース解放
その2
? 大量のdefineによる問題
19
問題の実装
1
2
3
4
5
128
129
130
<?php
define(“XXXXX_ERR", 0);
define("XXXXX_OK", 1);
define("XXXXX_WANT_MORE_TEXT", 2);
define("XXXXX_NO_MORE_TEXT", 3);
// snip
define("XXXXX_YURAGI", 0x0002);
define("XXXXX_DOGIGO", 0x0004);
define("XXXXX_USRDEF", 0x0040);
20
問題点
21
? defineは処理コストが大きく、リクエスト毎
にdefineが実行される
MINIT
RINIT
Script Execution
RSHUTDOWN
RINIT
Script Execution
RSHUTDOWN
RINIT
Script Execution
RSHUTDOWN
MSHUTDOWN
定義処理
定義処理
定義処理
改善した実装
234
235
236
237
238
239
240
241
242
243
348
349
350
351
PHP_MINIT_FUNCTION(xxxxx)
{
/* If you have INI entries, uncomment these lines
ZEND_INIT_MODULE_GLOBALS(xxxxx, xxxxx_init_globals, NULL);
REGISTER_INI_ENTRIES();
*/
REGISTER_LONG_CONSTANT( "XXXXX_ERR", 0, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_OK", 1, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_WANT_MORE_TEXT", 2, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_NO_MORE_TEXT", 3, CONST_CS|CONST_PERSISTENT );
// snip
REGISTER_LONG_CONSTANT( "XXXXX_KUGIRI", 0x0001, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_YURAGI", 0x0002, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_DOGIGO", 0x0004, CONST_CS|CONST_PERSISTENT );
REGISTER_LONG_CONSTANT( "XXXXX_USRDEF", 0x0040, CONST_CS|CONST_PERSISTENT );
22
ポイント
23
? エクステンションで利用する定数はエクス
テンションの起動時(MINIT)で定義する
MINIT
RINIT
Script Execution
RSHUTDOWN
RINIT
Script Execution
RSHUTDOWN
MSHUTDOWN
定義処理
比較
24
その他の改善方法
? hidefを活用するのが良い
25
hidefとは
? iniファイルから定数を一括定義する
? MINITの処理で定数を定義する
? リクエスト毎に処理しないので効率的
26
その3
? ホスト名取得(exec)による問題
27
問題の実装
28
1
2
3
<?php
$hostname = exec("hostname");
printf("%s? n", $hostname);
問題点
? 激おこぷんぷんまるレベル
29
問題点
? プロセスの生成コストは非常に大きい
? Preforkの設計努力も台無し
? セキュリティ的な観点からも外部コマンド
が実行はすべきでない
30
改善した実装
31
1
2
3
<?php
$hostname = gethostname();
printf("%s? n", $hostname);
ポイント
32
? PHP5.3以降でサポートされた標準の
gethostname()を使用する
? 外部コマンドは絶対に使わない
比較
33
失敗を繰り返さないために
34
継続的なテストの実行の必要性
? 良い習慣はツールの支援なしに継続する
ことは難しい
? どんな賢人であっても魔が差すとテストを
省くときがある
35
ツールの支援で解決する
? Yahoo! JAPANで標準的に使われている
36
例えばパフォーマンステストを自動化
し結果を可視化する
37
大切なこと
? コミット、ビルド、テスト、リリースのプロセ
スを自動化するためにツールを活用し、
人に依存する過ちを減らすこと
38
まとめ
39
まとめ
? 実行回数が多くなる処理に注意しよう
? どんな達人でも必ずミスをするし
? どんな賢人でも魔が差すとテストを省く
? ツールの支援による継続的なテストは課
題解決のための良い方法の1つです
40
41

More Related Content

本当に怖いパフォーマンスが悪い実装 #phpcon2013