作業ノート

様々なまとめ、雑感など

PHPのfor文で、配列の数を評価するときの違いによるベンチマークを取ってみた

きっかけは仕事でコードレビューをしていたときに、以下のようなfor文をみたこと。

for ($i = 0, $count = count($data); $i < $count; $i++) {
    // 何らかの処理
}

$countはfor文の条件判定でしか使用されていなかったので、そのときは変数を使わずに直接書けばいいのにと思った。

for ($i = 0; $i < count($data); $i++) {
    // 何らかの処理
}

一方でそれぞれのパフォーマンスが気になったので、模したコードで確認してみた。

確認環境のOSはCentOS 7.1。

$ cat /etc/redhat-release
CentOS Linux release 7.1.1503 (Core)

phpは5.6.11。

$ php -v
PHP 5.6.11 (cli) (built: Jul 12 2015 20:13:00)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

ベンチマーク取得のコードは下記のエントリーのコードを使用した。

d.hatena.ne.jp

実際に書いたコード。

以下は実行結果の一つ。

$ php bench_count.php
1
$count          :     0.0000 sec   @ 0.0000 sec (336.00%)
count()         :     0.0000 sec   @ 0.0000 sec (100.00%)
1
$count          :     0.0000 sec   @ 0.0000 sec (100.00%)
count()         :     0.0000 sec   @ 0.0000 sec (123.53%)
10
$count          :     0.0000 sec   @ 0.0000 sec (100.00%)
count()         :     0.0000 sec   @ 0.0000 sec (160.17%)
100
$count          :     0.0003 sec   @ 0.0000 sec (100.00%)
count()         :     0.0004 sec   @ 0.0000 sec (165.26%)
1000
$count          :     0.0027 sec   @ 0.0000 sec (100.00%)
count()         :     0.0048 sec   @ 0.0000 sec (180.65%)
10000
$count          :     0.0277 sec   @ 0.0000 sec (100.00%)
count()         :     0.0454 sec   @ 0.0000 sec (163.59%)
100000
$count          :     0.2720 sec   @ 0.0000 sec (100.00%)
count()         :     0.4506 sec   @ 0.0000 sec (165.69%)
1000000
$count          :     2.7292 sec   @ 0.0000 sec (100.00%)
count()         :     4.5232 sec   @ 0.0000 sec (165.73%)

1回から1000000回まで、実行回数を変えてそれぞれのパフォーマンスを相対的に確認する。 1回実行のテストを2回行っているのは、その内容に関わらずどうしても最初に実行したテストが遅くなったため。

何回か確認したが関数を毎回評価する方が遅く、その差は大体1.2倍から2倍ほど。 変数に比べて関数の方が遅いのは予想していたものの、相対的に見てここまで差があると変数にしたいのもわかる。

参照整合性制約で参照されるテーブルから、定義したテーブルと制約名を取得するSQL

以下はFOOテーブルのカラムを参照するテーブルと制約名を取得するSQL。

SELECT
    uc.table_name,
    uc.constraint_name
FROM user_constraints uc
JOIN user_constraints ruc ON
    ruc.owner = uc.r_owner AND ruc.constraint_name = uc.r_constraint_name
WHERE
    uc.constraint_type = 'R' AND ruc.table_name = 'FOO'
;

使用するのはuser_constraintsビュー。

constraint_typeRとなっているのが参照整合性制約の定義。これは該当テーブル名と制約名、参照先テーブルの一意制約名からなる。

参照整合性制約の定義を取得するビュー(uc)と、参照先の一意制約の定義を取得するするビューを(ruc)内部結合して、該当の定義を取得する。

参考

Oracleで特定テーブルの参照整合性制約を確認するSQL

今、Symfony2を使用した開発をしている。DBはOracle。

Symfony2のconsoleでORMの設定からSQLを作成することができる。利用しているDBの状況から最新にするために必要なSQLを作成できるので便利。しかし、その作成に時間がかなりかかるため、その調査をした。

実行ログをみると参照整合性制約を抽出するSQLに時間がかかっていたため、SQLを書き換えて改善されるか試してみた。そのとき作成したSQLが以下。

SELECT
  uc.constraint_name,
  uc.delete_rule,
  uc.search_condition,
  ucc.column_name,
  ucc.position,
  r_ucc.table_name  r_table_name,
  r_ucc.column_name r_column_name
FROM user_constraints uc
JOIN user_cons_columns ucc
  ON ucc.owner             = uc.owner
 AND ucc.constraint_name   = uc.constraint_name
JOIN user_cons_columns r_ucc
  ON r_ucc.owner           = uc.r_owner
 AND r_ucc.constraint_name = uc.r_constraint_name
 AND r_ucc.position        = ucc.position
WHERE
    uc.constraint_type = 'R'
AND uc.table_name = 'FOO'
ORDER BY uc.constraint_name ASC,
         ucc.position ASC
;

確認するために2つのビューを使用する。user_constraintsは、テーブルに定義された制約を示すビューでuser_cos_columnsはその制約で使用しているカラムを示すビュー。

user_constraints.constraint_typeは当該制約の種別でRは参照整合性を意味する。参照整合性制約の場合、user_constraints.owneruser_constraints.constraint_nameが参照元の制約であり、参照先の制約はuser_constraints.r_owneruser_constraints.r_constraint_name

それぞれのカラムをuser_cons_columnsの該当カラムと内部結合して、それぞれのテーブル名とカラム名を取得した。

ちなみに上記のSQLで試したところ元のSQLよりは改善されたが、それでも実行には数秒かかった。どうやらuser_constraintsをselectするだけでも時間がかかっているようだった。

参考