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

41
本当に恐い パフォーマンスが悪い実装 Masakazu Nagaya 20130914日(土曜日)

Upload: yahoo

Post on 15-Jan-2015

27.655 views

Category:

Technology


2 download

DESCRIPTION

(PHPカンファレンス2013での発表内容です) Yahoo! JAPANほどの大規模サイトにおいては、小さなコードでも圧倒的に使われることで大量のコストを生み出します。実例を交えて非効率な実装と、その改善例を紹介します。

TRANSCRIPT

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

本当に恐い

パフォーマンスが悪い実装

Masakazu Nagaya

2013年09月14日(土曜日)

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

Overview

• 大規模サイトでパフォーマンスを著しく劣化させる非効率な実装例や、その改善例を紹介します。

2

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

アジェンダ

• はじめに

• パフォーマンスが悪い実装の紹介

• 失敗を繰り返さないために

• まとめ

3

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

はじめに

4

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

誰でも失敗する

• プログラムを書く全ての人間がスーパープログラマーではない

• 常に完璧で失敗をしない人間はいない

• 失敗は必ず発生する

5

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

大切なこと

• 失敗から目をそむけない

• 失敗を隠さない(共有する)

• 失敗を繰り返さない

6

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

パフォーマンスが悪い実装の紹介

7

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

その1

• リソースの確保と解放のタイミングと回数に要注意

8

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

問題の実装

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; } }

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

問題点

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; } }

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

改善した実装

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

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

ポイント

• isBlock()の数だけopenされると遅くなる

• openの処理コストも内部でシステムコール(open/mmap)を呼ぶので大きい

• 無駄な処理を減らす

12

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

検証方法

• テストコードをサーバ上に配置

ツールによる負荷テストを実施

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)); }

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

比較

14

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

更なる改善

• リクエスト毎に毎回Open/Closeするのはもったいない

15

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

更に改善した実装

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

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

Persistent Resources とは

• プロセス単位でオープンしたリソースを永続的に保持し、次回のリクエストで再利用する

• Persistent Resourcesの例 sqlite_popen(), pfsockopen(), oci_pconnect(), mysql_pconnect() など

17

(PHP5.5ではmysql_pconnectは廃止されます。代替の関数を利用すべきです)

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

Life Cycle

18

MINIT

RINIT

Script Execution

RSHUTDOWN

RINIT

Script Execution

RSHUTDOWN

RINIT

Script Execution

RSHUTDOWN

MSHUTDOWN

Apache Child Process

リソース確保

再利用

再利用

リソース解放

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

その2

• 大量のdefineによる問題

19

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

問題の実装

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

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

問題点

21

• defineは処理コストが大きく、リクエスト毎にdefineが実行される

MINIT

RINIT

Script Execution

RSHUTDOWN

RINIT

Script Execution

RSHUTDOWN

RINIT

Script Execution

RSHUTDOWN

MSHUTDOWN

定義処理

定義処理

定義処理

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

改善した実装

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

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

ポイント

23

• エクステンションで利用する定数はエクステンションの起動時(MINIT)で定義する

MINIT

RINIT

Script Execution

RSHUTDOWN

RINIT

Script Execution

RSHUTDOWN

MSHUTDOWN

定義処理

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

比較

24

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

その他の改善方法

• hidefを活用するのが良い

25

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

hidefとは

• iniファイルから定数を一括定義する

• MINITの処理で定数を定義する

• リクエスト毎に処理しないので効率的

26

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

その3

• ホスト名取得(exec)による問題

27

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

問題の実装

28

1 2 3

<?php $hostname = exec("hostname"); printf("%s¥ n", $hostname);

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

問題点

• 激おこぷんぷんまるレベル

29

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

問題点

• プロセスの生成コストは非常に大きい

• Preforkの設計努力も台無し

• セキュリティ的な観点からも外部コマンドが実行はすべきでない

30

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

改善した実装

31

1 2 3

<?php $hostname = gethostname(); printf("%s¥ n", $hostname);

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

ポイント

32

• PHP5.3以降でサポートされた標準のgethostname()を使用する

• 外部コマンドは絶対に使わない

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

比較

33

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

失敗を繰り返さないために

34

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

継続的なテストの実行の必要性

• 良い習慣はツールの支援なしに継続することは難しい

• どんな賢人であっても魔が差すとテストを省くときがある

35

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

ツールの支援で解決する

• Yahoo! JAPANで標準的に使われている

36

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

例えばパフォーマンステストを自動化し結果を可視化する

37

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

大切なこと

• コミット、ビルド、テスト、リリースのプロセスを自動化するためにツールを活用し、人に依存する過ちを減らすこと

38

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

まとめ

39

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

まとめ

• 実行回数が多くなる処理に注意しよう

• どんな達人でも必ずミスをするし

• どんな賢人でも魔が差すとテストを省く

• ツールの支援による継続的なテストは課題解決のための良い方法の1つです

40

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

41