文字列連結のパフォーマンス比較
そういえば計ったことなかったなぁ‥と思い立って、いくつかのパターンで計ってみたメモ。
実行環境は、自宅のXenServer上に載せた以下のVM環境。
ケース1:文字列連結演算子とダブルクォーテーションと
プログラムの全体は以下のような感じ。
n,test1,test2 <?php for ($n = 10000; $n <= 50000; $n += 5000) { set_time_limit(120); // test1 $s1 = microtime(true); $str = ''; for ($i = 0; $i < $n; ++$i) { $str = $str . 'a' . $i . 'b'; } $e1 = microtime(true); // test2 $s2 = microtime(true); $str = ''; for ($i = 0; $i < $n; ++$i) { $str = "{$str}a{$i}b"; } $e2 = microtime(true); printf("%d,%.3f,%.3f\n", $n, $e1 - $s1 , $e2 - $s2); }
文字列を
$str = $str . 'a' . $i . 'b';
で連結するか
$str = "{$str}a{$i}b";
で連結するかの違い。
実行すると、以下のような出力が得られる。
n,test1,test2 10000,0.075,0.029 15000,0.228,0.065 20000,0.445,0.120 25000,0.873,0.199 30000,1.422,0.300 35000,2.168,0.451 40000,3.023,0.581 45000,2.802,0.739 50000,3.962,0.938
グラフにしてみるとこんな感じ。
ケース2:ダブルクォーテーションと複合演算子と
プログラムの全体は以下のような感じ。
n,test2,test3,test4 <?php for ($n = 10000; $n <= 100000; $n += 5000) { set_time_limit(120); // test2 $s2 = microtime(true); $str = ''; for ($i = 0; $i < $n; ++$i) { $str = "{$str}a{$i}b"; } $e2 = microtime(true); // test3 $s3 = microtime(true); $str = ''; for ($i = 0; $i < $n; ++$i) { $str .= 'a' . $i . 'b'; } $e3 = microtime(true); // test4 $s4 = microtime(true); $str = ''; for ($i = 0; $i < $n; ++$i) { $str .= "a{$i}b"; } $e4 = microtime(true); printf("%d,%.3f,%.3f,%.3f\n", $n, $e2 - $s2, $e3 - $s3, $e4 - $s4); }
文字列を
$str = "{$str}a{$i}b";
$str .= 'a' . $i . 'b';
$str .= "a{$i}b";
のように連結するという3パターン。1つ目は、ケース1で早かった方と同じ。
実行すると、以下のような出力が得られる。
n,test2,test3,test4 10000,0.016,0.001,0.001 15000,0.034,0.002,0.002 20000,0.069,0.003,0.003 25000,0.102,0.004,0.003 30000,0.149,0.004,0.004 35000,0.210,0.005,0.005 40000,0.281,0.006,0.005 45000,0.363,0.006,0.006 50000,0.455,0.007,0.007 55000,0.556,0.008,0.008 60000,0.668,0.009,0.008 65000,0.782,0.009,0.009 70000,0.916,0.010,0.009 75000,1.054,0.011,0.011 80000,1.212,0.011,0.011 85000,1.364,0.012,0.012 90000,1.534,0.013,0.012 95000,1.723,0.013,0.013 100000,1.915,0.014,0.014
‥圧倒的差である。一応グラフにしてみる。
test3のグラフは隠れて見えないが、test4の下に隠れている。
結局‥
- ケース2の結果を見る限り、長い文字列
$str
が右辺に出てくるときの評価にコストがかかるのだろうか。 - ケース1の結果から、同じ長い文字列が右辺に現れざるを得ない場合は、文字列連結演算子の処理の方がコストがかかるということか。
正直、ここまで差が出るとは思わなかったので、文字列連結処理を書くときには、これからは気を使うようにしようと思う。
PHPの識別子にアスキーコード0x7Fが使えることの検証
マニュアルにもしっかり書いてあるんだけどPHPの識別子にはアスキーコード0x7Fが使えるが、これは制御文字DELなので変じゃないかという指摘。試したところ確かに使える / “PHP :: Bug #71897 :: ASCII …” https://t.co/9CkFL2aziD
— 徳丸 浩 (@ockeghem) 2016年3月27日
ということなので、実際に試してみた。
どうせなら1つのソースコードで完結させてやろうということで得られたコードが以下。
<?php $name = "\x7F"; $expr = '$' . $name . '="b";var_dump($name, $' . $name . ');'; var_dump($expr); eval($expr);
これを実行すると、以下のように出力される。(0x7Fの部分は□で表している)
string(27) "$□="b";var_dump($name, $□);" string(1) "□" string(1) "b"
ちなみに、識別子の先頭文字としては使えない数字を入れてみたコード:
<?php $name = "1\x7F"; $expr = '$' . $name . '="b";var_dump($name, $' . $name . ');'; var_dump($expr); eval($expr);
を実行すると、ちゃんとエラーになる。
string(29) "$1□="b";var_dump($name, $1□);" PHP Parse error: syntax error, unexpected T_LNUMBER, expecting T_VARIABLE or '$' in /home/kajino/foo.php(6) : eval()'d code on line 1
is_aとis_subclass_ofの違い
クラス名の文字列を渡して使用しようとしたときに罠にはまったのでメモ。
- is_a
- is_subclass_of
はまったのは、v5.3.9から追加されたというallow_stringパラメータのデフォルト値に関連するものだが、他にも違いがあるようなので、2パターンほど試してみる。
試した環境は以下の通り。
各メソッドのマニュアルを読むと、以下のようになっている。
- is_a
- allow_stringのデフォルト値はFALSE、つまり検査対象に文字列を許さない
- is_subclass_of
- allow_stringのデフォルト値はTRUE、つまり検査対象に文字列を許す
つまり、CentOS 6(PHP 5.3.3)の環境下では上記の挙動しか選択できない。
テスト1:直系の子クラスを指定
コードは以下のようなもの。
<?php class A{} class B extends A{} $b = new B(); var_dump( is_a($b, "A") , is_subclass_of($b, "A") , is_a("B", "A") , is_subclass_of("B", "A") );
出力は以下。
bool(true) bool(true) bool(false) bool(true)
「is_a("B", "A")
」のパターンだけ、検証対象のクラス名を表す文字列を指定できないのでfalse
が返されている。
テスト2:親クラス自身を指定
コードは以下のようなもの。
<?php class A{} $a = new A(); var_dump( is_a($a, "A") , is_subclass_of($a, "A") , is_a("A", "A") , is_subclass_of("A", "A") );
出力は以下。
bool(true) bool(false) bool(false) bool(false)
「is_subclass_of($a, "A")
」で、確かに「サブクラス」ではないのでfalse
が返されている。
「is_a("A", "A")
」でfalse
が返されるのは、テスト1と同じ理由。
PostgreSQLサーバー用ディレクトリを暗号化ファイルシステムに置き換えてみる~論理ボリューム作成編
前置き
やりたいことは、前に書いた
と同じなのだが、
- HDDを追加せずに、論理ボリュームlv_rootを分割して、片方を論理ボリュームlv_pgsqlとして暗号化ファイルシステムにする
という制約を課す(というか、元々やりたかったのはこっち)。
事前確認
lv_rootの情報を確認しておく。
# df -m Filesystem 1M-blocks Used Available Use% Mounted on /dev/mapper/VolGroup-lv_root 14023 1005 12300 8% / tmpfs 372 0 372 0% /dev/shm /dev/sda1 477 57 395 13% /boot # lvdisplay --units m /dev/mapper/VolGroup-lv_root --- Logical volume --- LV Path /dev/VolGroup/lv_root LV Name lv_root VG Name VolGroup LV UUID SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z LV Write Access read/write LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900 LV Status available # open 1 LV Size 14376.00 MiB Current LE 3594 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 #
下準備
いずれにしてもPostgreSQLサーバーは一旦止めないといけないし、lv_rootを分割するにはOSを停止しないといけないので、以下を実行する。
# chkconfig postgresql off # sync;sync;sync;shutdown -h now
OSをシャットダウンしたら、lv_rootを分割するために、インストールメディアを挿入してRescueモードで再起動する。
起動したら、以下の手順でシェルを起動する。
Rescue installed system
を選択してEnterキーを押すChoose a Language
⇒English
Keyboard Type
⇒jp106
Setup Networking
⇒No
Rescue
⇒Skip
shell Start shell
を選択
lv_rootの分割
bash-4.1# lvm vgchange -a y 2 logical volume(s) in volume group "VolGroup" now active bash-4.1# fsck.ext4 -f /dev/mapper/VolGroup-lv_root e2fsck 1.41.12 (17-May-2010) Pass 1: Checking inodes blocks, and sizes (略) bash-4.1# resize2fs /dev/mapper/VolGroup-lv_root 6184M resize2fs 1.41.12 (17-May-2010) Resizing the filesystem on /dev/mapper/VolGroup-lv_root to 1583104 (4k) blocks. The filesystem on /dev/mapper/VolGroup-lv_root is now 1583104 blocks long. bash-4.1# lvm lvreduce -L6184M /dev/mapper/VolGroup-lv_root WARNING: Reducing active logical volume to 6.04GiB THIS MAY DESTROY YOUR DATA (filesystem etc.) Do you realy want to reduce lv_root? [y/n]: y Size of logical volume VolGroup/lv_root changed from 14.04 GiB (3594 extents) to 6.04 GiB (1546 extends). Logical volume lv_root successfully resized bash-4.1# exit
メニューに戻ったら、インストールメディアを抜いた後、reboot
を選択して再起動する。
空き領域の確認
ちゃんと空き領域が確保できたかどうかを確認する。
# lvdisplay --units m /dev/mapper/VolGroup-lv_root --- Logical volume --- LV Path /dev/VolGroup/lv_root LV Name lv_root VG Name VolGroup LV UUID SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z LV Write Access read/write LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900 LV Status available # open 1 LV Size 6184.00 MiB Current LE 1546 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 # vgdisplay --units m --- Volume group --- VG Name VolGroup System ID Format lvm2 Metadata Areas 1 Metadata Sequence No 4 VG Access read/write VG Status resizable MAX LV 0 Cur LV 2 Open LV 2 Max PV 0 Cur PV 1 Act PV 1 VG Size 15880.00 MiB PE Size 4.00 MiB Total PE 3970 Alloc PE / Size 1922 / 7688.00 MiB Free PE / Size 2048 / 8192.00 MiB VG UUID n0127i-W87x-Y9GI-CAT9-60yD-1ZQz-J9KQ9b #
lv_rootが縮小され、8GiBの空き領域が確保された事が確認できる。
暗号化ファイルシステムの作成と自動接続
論理ボリュームの作成
PostgreSQL用のデータディレクトリなので、lv_pgsqlとして論理ボリュームを作成する。
# lvcreate -L 8192M -n lv_pgsql VolGroup Logical volume "lv_pgsql" created. # lvdisplay --units m --- Logical volume --- LV Path /dev/VolGroup/lv_root LV Name lv_root VG Name VolGroup LV UUID SHPvKC-d2fE-Rb2D-5lpT-eqly-QWAB-rGLf3z LV Write Access read/write LV Creation host, time localhost.localdomain, 2015-11-07 11:53:33 +0900 LV Status available # open 1 LV Size 6184.00 MiB Current LE 1546 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:0 --- Logical volume --- LV Path /dev/VolGroup/lv_swap LV Name lv_swap VG Name VolGroup LV UUID 221tmz-bcIA-Tafg-kUvM-xxwv-8uID-qiJSpr LV Write Access read/write LV Creation host, time localhost.localdomain, 2015-11-07 11:53:40 +0900 LV Status available # open 1 LV Size 1504.00 MiB Current LE 376 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:1 --- Logical volume --- LV Path /dev/VolGroup/lv_pgsql LV Name lv_pgsql VG Name VolGroup LV UUID WrnmIg-daJ1-Xlnr-AJ0e-ciCZ-QeXT-GxaZSq LV Write Access read/write LV Creation host, time CentOS6, 2015-11-07 13:54:04 +0900 LV Status available # open 0 LV Size 8192.00 MiB Current LE 2048 Segments 1 Allocation inherit Read ahead sectors auto - currently set to 256 Block device 253:2 #
暗号化ファイルシステムの作成
前回とほぼ同じだが再掲。
# cryptsetup luksFormat -h sha256 /dev/mapper/VolGroup-lv_pgsql WARNING! ======== This will overwrite data on /dev/mapper/VolGroup-lv_pgsql irrevocably. Are you sure? (Type uppercase yes): YES Enter LUKS passphrase: Verify passphrase: # cryptsetup luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt Enter passphrase for /dev/mapper/VolGroup-lv_pgsql: # mkfs -t ext4 /dev/mapper/lv_pgsql_crypt mke2fs 1.41.12 (17-May-2010) Filesystem label= OS type: Linux Block size=4096 (log=2) Fragment size=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 524288 inodes, 2096640 blocks 104832 blocks (5.00%) reserved for the super user First data block=0 Maximum filesystem blocks=2147483648 64 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 Writing inode tables: done Creating journal (32768 blocks): done Writing superblocks and filesystem accounting information: done This filesystem will be automatically checked every 30 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. # cryptsetup luksClose lv_pgsql_crypt #
試しにマウント
# cryptsetup luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt Enter passphrase for /dev/mapper/VolGroup-lv_pgsql: # mount /dev/mapper/lv_pgsql_crypt /mnt/ # ls /mnt lost+found # umount /mnt # cryptsetup luksClose lv_pgsql_crypt #
自動接続の設定
前回とほぼ同じだが再掲。
# dd if=/dev/random of=/etc/lvm/lvm.seckey bs=1 count=32 32+0 records in 32+0 records out 32 bytes (32 B) copied, 0.0398393 s, 0.8 kB/s # chmod 400 /etc/lvm/lvm.seckey # cryptsetup luksAddKey /dev/mapper/VolGroup-lv_pgsql /etc/lvm/lvm.seckey Enter any passphrase: # cryptsetup --key-file /etc/lvm/lvm.seckey luksOpen /dev/mapper/VolGroup-lv_pgsql lv_pgsql_crypt # cryptsetup luksDump /dev/mapper/VolGroup-lv_pgsql (中略) UUID: 6069746a-025f-4158-963b-c729fe047281 (中略) # vi /etc/crypttab + lv_pgsql_crypt UUID=6069746a-025f-4158-963b-c729fe047281 /etc/lvm/lvm.seckey luks # vi /etc/fstab + /dev/mapper/lv_pgsql_crypt /var/lib/pgsql ext4 defaults 1 1 #
PostgreSQL用ディレクトリの移行
# mv /var/lib/pgsql /var/lib/pgsql_original.yyyy-mm-dd # mkdir /var/lib/pgsql # mount /var/lib/pgsql # chown postgres:postgres /var/lib/pgsql # chmod go-rwx /var/lib/pgsql # ( cd /var/lib/pgsql_original.yyyy-mm-dd ; tar czf - . ) | ( cd /var/lib/pgsql ; tar xzf - )
PostgreSQLサーバーの起動
# service postgresql start # chkconfig postgresql on
参考
文字とASCII値の変換
いつも関数名まで忘れて苦労するので、メモついでにおもちゃを作ってみた(謎)。
関係する関数は以下
おもちゃコードとして、文字列の独自エンコード/デコードする関数を作ってみた。
ASCII値での単なるrot1関数。可視化するためにbin2hexしているが。
<?php function rot1_encode($str) { $res = ''; $len = strlen($str); for ($i = 0; $i < $len ; ++$i) { $res .= chr(ord($str[$i])^0xff); } return bin2hex($res); } if (!function_exists('hex2bin')) { function hex2bin($str) { $res = ''; $len = strlen($str); for ($i = 0; $i < $len ; $i += 2) { $res .= pack("c", intval(substr($str, $i, 2), 16)); } return $res; } } function rot1_decode($str) { $str = hex2bin($str); $res = ''; $len = strlen($str); for ($i = 0; $i < $len ; ++$i) { $res .= chr(ord($str[$i])^0xff); } return $res; } $strs = array( 'こんにちは、ord()とchr()を使ったお遊びです。', 'お遊び', ); foreach ($strs as $str) { var_dump(rot1_encode($str)); var_dump(rot1_decode(rot1_encode($str))); }
これを実行すると以下のようになる。
$ php test.php string(122) "1c7e6c1c7d6c1c7e541c7e5e1c7e501c7f7e908d9bd7d61c7e579c978dd7d61c7d6d1b42401c7e5c1c7e601c7e75167e751c7e4c1c7e581c7e661c7f7d" string(61) "こんにちは、ord()とchr()を使ったお遊びです。" string(18) "1c7e75167e751c7e4c" string(9) "お遊び" $
PostgreSQLサーバー用ディレクトリを暗号化ファイルシステムに置き換えてみる
前置き
とりあえず、以下の環境を前提としている。
- OSはCentOS 6 (VirtualBoxのVMとして作成)
- PostgreSQL 8.4をパッケージインストールしている
- データディレクトリは「/var/lib/pgsql/data」
- PostgreSQLのデータディレクトリも含めて1つのパーティションになっている
- これは別パーティションになっていても考え方は一緒
で、暗号化されていない「/var/lib/pgsql」以下を、別HDDを追加して暗号化ファイルシステムとしてフォーマットしたものに置き換えてみようという話。(※別HDDでなくても、同じHDDに空き領域があり、別パーティションを作成できるケースも同様に対応可能)
作業前のHDDは以下のように「/dev/sda」の1本だけ存在する。
# fdisk -l Disk /dev/sda: 8589 MB, 8589934592 bytes 255 heads, 63 sectors/track, 1044 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x0006435d Device Boot Start End Blocks Id System /dev/sda1 * 1 64 512000 83 Linux Partition 1 does not end on cylinder boundary. /dev/sda2 64 1045 7875584 8e Linux LVM Disk /dev/mapper/VolGroup-lv_root: 5947 MB, 5947523072 bytes 255 heads, 63 sectors/track, 723 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 Disk /dev/mapper/VolGroup-lv_swap: 2113 MB, 2113929216 bytes 255 heads, 63 sectors/track, 257 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 #
下準備
とりあえず、PostgreSQLサーバーを一旦止めないといけないし、HDDを追加しないといけないのでOSも止めることになる(この辺は環境によるだろうが‥)。
# chkconfig postgresql off # sync;sync;sync;shutdown -h now
OSをシャットダウンしたら、HDDを一つ追加して、OSを再起動する。
追加されたHDDは「/dev/sdb」。
# fdisk -l Disk /dev/sda: 8589 MB, 8589934592 bytes 255 heads, 63 sectors/track, 1044 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x0006435d Device Boot Start End Blocks Id System /dev/sda1 * 1 64 512000 83 Linux Partition 1 does not end on cylinder boundary. /dev/sda2 64 1045 7875584 8e Linux LVM Disk /dev/sdb: 8589 MB, 8589934592 bytes 255 heads, 63 sectors/track, 1044 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 Disk /dev/mapper/VolGroup-lv_root: 5947 MB, 5947523072 bytes 255 heads, 63 sectors/track, 723 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 Disk /dev/mapper/VolGroup-lv_swap: 2113 MB, 2113929216 bytes 255 heads, 63 sectors/track, 257 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x00000000 #
暗号化ファイルシステムの作成と自動接続
作成
# cryptsetup luksFormat -h sha256 /dev/sdb WARNING! ======== This will overwrite data on /dev/sdb irrevocably. Are you sure? (Type uppercase yes): YES Enter LUKS passphrase: Verify passphrase: # cryptsetup luksOpen /dev/sdb sdb_crypt Enter passphrase for /dev/sdb: # mkfs -t ext4 /dev/mapper/sdb_crypt # cryptsetup luksClose sdb_crypt
自動接続の設定
もちろん、これをやると、セキュリティリスクと手間とのトレードオフとなる。
- 自動接続すると、パスフレーズを知らなくても、HDDを全部盗んでOSを起動すればデータが見えてしまう。
- 自動接続しないと、luksOpenから、パスフレーズの入力、マウント、PostgreSQLサーバーの起動まで、全部手動でやることになる。
# dd if=/dev/random of=/etc/lvm/lvm.seckey bs=1 count=32 32+0 records in 32+0 records out 32 bytes (32 B) copied, 58.6724 s, 0.0 kB/s # chmod 400 /etc/lvm/lvm.seckey # cryptsetup luksAddKey /dev/sdb /etc/lvm/lvm.seckey Enter any passphrase: # cryptsetup --key-file /etc/lvm/lvm.seckey luksOpen /dev/sdb sdb_crypt # cryptsetup luksDump /dev/sdb (中略) UUID: xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb (中略) # vi /etc/crypttab + sdb_crypt UUID=xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb /etc/lvm/lvm.seckey luks # vi /etc/fstab + /dev/mapper/sdb_crypt /var/lib/pgsql ext4 errors=remount-ro 0 1 #
PostgreSQL用ディレクトリの移行
# mv /var/lib/pgsql /var/lib/pgsql_original.yyyy-mm-dd # mkdir /var/lib/pgsql # chown postgres:postgres /var/lib/pgsql # chmod go-rwx /var/lib/pgsql # mount /var/lib/pgsql # ( cd /var/lib/pgsql_original.yyyy-mm-dd ; tar czf - . ) | ( cd /var/lib/pgsql ; tar xzf - )
PostgreSQLサーバーの起動
# service postgresql start # chkconfig postgresql on
psqlコマンドなどでPostgreSQLサーバーに接続し、データの取出しなどができることを確認する。
(余談1)念のため確認
OSを再起動したときに、自動接続されるかどうかを確認しておく。
# umount /var/lib/pgsql # cryptsetup luksClose sdb_crypt # sync;sync;sync;shutdown -r now (再起動後‥) # ls /var/lib/pgsql backups data lost+found pgstartup.log #
(余談2)暗号化ファイルシステムの取り外し
使用しているプロセスの停止
もちろん、今回の場合は、まずこのファイルシステムを使用しているPostgreSQLサーバーを停止する必要がある
# service postgresql stop # chkconfig postgresql off
取り外し
自動接続の設定も併せて解除しておく必要がある。(あとでまた接続する場合は、各設定ファイルの内容は削除せずにコメントアウトしておく)
# umount /var/lib/pgsql # cryptsetup luksClose sdb_crypt # vi /etc/fstab - /dev/mapper/sdb_crypt /var/lib/pgsql ext4 errors=remount-ro 0 1 # vi /etc/crypttab - sdb_crypt UUID=xxxxxxxx-yyyy-zzzz-aaaa-bbbbbbbbbbbb /etc/lvm/lvm.seckey luks
参考
Zend_Dateクラスを効率よく使うチャレンジ
序
Zend Frameworkに含まれているZend_Dateクラスは、インスタンス生成コストがとにかく高い。
どれくらい高いかというと、以下の2つのプログラムで比較してみるとなんとなく分かる。
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $zd = new Zend_Date('2015-08-01 03:04:05'); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $zd = new DateTime('2015-08-01 03:04:05'); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
- test0-3.php: Zend_Dateオブジェクトをキャッシュしておいて、strtotimeとsetTimestampで対応
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $zd = new Zend_Date(); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $time = strtotime('2015-08-01 03:04:05'); $zd->setTimestamp($time); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
これらを実行する。
$ for i in {1,2,5,8}{,00,000} ; do > printf "%d" $i > for t in test0-1.php test0-2.php test0-3.php ; do > php ${t} ${i} | awk '{printf(",%s", $0);}' > done > printf "\n" > done 1, 0.03093, 0.00011, 0.00004 100, 0.23281, 0.00048, 0.00074 1000, 2.32891, 0.00825, 0.01088 2, 0.01160, 0.00012, 0.00005 200, 0.48956, 0.00336, 0.00224 2000, 5.51129, 0.01458, 0.02203 5, 0.02411, 0.00018, 0.00012 500, 1.08754, 0.00409, 0.00425 5000,11.39618, 0.03983, 0.06424 8, 0.03674, 0.00031, 0.00000 800, 2.20668, 0.00417, 0.00931 8000,19.13784, 0.07557, 0.11488 $
並べ替えてグラフにしてみるとこんな感じ。
本
とりあえず、欲しいものは「日時計算をして、その結果を日時文字列にしたもの」ということで話を進める。
Zend_Dateオブジェクトをキャッシュして使いまわすとよさそうということで、上記「test0-3.php」をベースに、以下のような計測をしてみる。
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { return $zd->toString('YYYY-MM-dd'); } $zd = new Zend_Date('2015-08-01 03:04:05'); //var_dump(_test($zd)); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { $ts = $zd->getTimestamp(); return date('Y-m-d', $ts); } $zd = new Zend_Date('2015-08-01 03:04:05'); //var_dump(_test($zd)); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { $year = $zd->get(Zend_Date::YEAR); $month = $zd->get(Zend_Date::MONTH); $day = $zd->get(Zend_Date::DAY); return sprintf("%04d-%02d-%02d", $year, $month, $day); } $zd = new Zend_Date('2015-08-01 03:04:05'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
さて、実行。
$ for i in {1,2,5,8}{,00,000,0000} ; do > printf "%d" $i > for t in test1-1.php test1-2.php test1-3.php ; do > php ${t} ${i} | awk '{printf(",%s", $0);}' > done > printf "\n" > done
結果。
1, 0.00020, 0.00002, 0.00009 100, 0.01489, 0.00010, 0.01203 1000, 0.15603, 0.00634, 0.17088 10000, 1.28451, 0.04945, 1.37297 2, 0.00009, 0.00002, 0.00063 200, 0.02691, 0.00093, 0.04130 2000, 0.30510, 0.01436, 0.27488 20000, 2.93000, 0.12693, 3.37226 5, 0.00057, 0.00008, 0.00116 500, 0.08533, 0.00256, 0.07560 5000, 0.85879, 0.03124, 0.98879 50000,11.33206, 0.39485, 7.20063 8, 0.00063, 0.00009, 0.00074 800, 0.10203, 0.00917, 0.09432 8000, 0.86644, 0.03868, 0.81726 80000,12.23691, 0.57610,14.96780
さすがに各フィールドの値を取るのはコストが高いが、「test1-2.php」のケースが使えそうなレベル。
また、コードは省略するが、「test2-1.php」「test2-2.php」「test2-3.php」は時分秒まで含めるケース(「HH:mm:ss」「H:i:s」等の追加による)。
「test1-2.php/test2-2.php」はフォーマットに関係なくいいパフォーマンスを出しているが、Zend_Dateは時分秒まで含めるとそれだけコストが高くなる。
結
Zend_Dateクラスとは適度に付き合うのがよさそう(謎)。