はじめに
このあたりを見て,ポータブルな(サーバに置くだけで動作する?)モジュールについて,ちょこっとだけ勉強を始めました.
この先何回か,ここにあるモジュールを触ってみたメモなんかをエントリしてみようと思います.
今回は,Cache::FileCacheモジュールについてです.恥ずかしながら,これまでデータとかのキャッシュ処理を扱ったことがほとんどなかったので,そのあたりの勉強もかねて.
では以下.
基本
use Cache::FileCache; my $cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 10, });
キャッシュするには,setメソッドを呼びます.後で値を引き出すための「キー」と値自身を引数に渡します.
my $to_be_cached = 'hogehoge'; $cache->set('keyname', $to_be_cached);
この場合,Cache::FileCacheオブジェクト生成時に指定したdefault_expires_inパラメータの値が表す時間だけ保持されます.
保持する時間を変更したい場合は,第3引数にその値を指定すればOKです.
$cache->set('keyname', $to_be_cached, 60);
キャッシュした値を取得するには,先で指定した「キー」を引数に,getメソッドで呼び出します.保持期限を過ぎていたり,指定した「キー」に対応する値が存在しない場合にはundefが返ります.
my $cached = $cache->get('keyname'); # 保持期限内 ? 'hogehoge' : undef my $undef = $cache->get('hogehoge'); # undef
簡単ですね.
イメージ的には,「ファイルシステムレベルでのハッシュ」みたいな感じ,とでも言いましょうか.
とりあえず使ってみる
まずは,キャッシュをセットしたり取得したり,保持期限前後の様子を確認してみます.
(sample1.plのソースコードは「おわりに」以降に掲載.)
% ./sample1.pl
# about 0 sec.
$VAR1 = {
'bar' => 'huga',
'baz' => 'piyo',
'foo' => 'hoge'
};
$VAR1 = {
'c' => '3',
'a' => '1',
'b' => '2'
};
# about 5 sec.
$VAR1 = {
'bar' => 'huga',
'baz' => 'piyo',
'foo' => 'hoge'
};
$VAR1 = {
'c' => '3',
'a' => '1',
'b' => '2'
};
# about 11 sec.
$VAR1 = undef;
$VAR1 = {
'c' => '3',
'a' => '1',
'b' => '2'
};
# about 15 sec.
$VAR1 = undef;
$VAR1 = {
'c' => '3',
'a' => '1',
'b' => '2'
};
# about 21 sec.
$VAR1 = undef;
$VAR1 = undef;
%ディレクトリ構造とかファイルとか
どのように保存されているかを確認してみます.
ディレクトリ構造
(sample2.plのソースコードは「おわりに」以降に掲載.)
% ./sample2.pl
# cache_root
/var/folders/sC/sCwCvkCpGzSDGTP6+Wood++++TI/-Tmp-/FileCache
# tree
/var/folders/sC/sCwCvkCpGzSDGTP6+Wood++++TI/-Tmp-/FileCache
`-- filecache_sample
|-- 9
| `-- b
| `-- 6
| `-- 9b63072850833c63c5200f2c35b3edc4118f705e
`-- a
`-- 1
`-- d
`-- a1dd4114c98069523cdf1d90ff3a43222670b8c8
7 directories, 2 files
# sha1
a1dd4114c98069523cdf1d90ff3a43222670b8c8
9b63072850833c63c5200f2c35b3edc4118f705e
# changing cache_root
# cache_root
./tmp
# tree
./tmp
`-- filecache_sample
|-- 9
| `-- b
| `-- 6
| `-- 9b63072850833c63c5200f2c35b3edc4118f705e
`-- a
`-- 1
`-- d
`-- a1dd4114c98069523cdf1d90ff3a43222670b8c8
7 directories, 2 files
# sha1
a1dd4114c98069523cdf1d90ff3a43222670b8c8
9b63072850833c63c5200f2c35b3edc4118f705e
%「キャッシュルート」となるディレクトリの下に,オブジェクト生成時にnamespaceパラメータで指定した値(名前空間)のディレクトリができ,その下に,キーと値とのペアを表すファイルが入っています.名前空間ディレクトリ(?)と各ファイルとの間には,ファイル名にちなんだ名前のディレクトリが,数階層掘られています.
「キャッシュルート」ディレクトリは,オブジェクト生成時に指定することも可能です.
$cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 10, cache_root => './tmp', });
次に示すのは,キャッシュするディレクトリを先のように./tmpと指定した場合の,ls -lコマンドのbefore/afterです.
before:
% ll total 24 -rw-r--r--@ 1 iwata staff 673 2 5 01:20 sample.yml -rwxr-xr-x 1 iwata staff 681 2 5 02:56 sample1.pl -rwxr-xr-x@ 1 iwata staff 946 2 5 03:03 sample2.pl %
after:
% ll total 24 -rw-r--r--@ 1 iwata staff 673 2 5 01:20 sample.yml -rwxr-xr-x 1 iwata staff 681 2 5 02:56 sample1.pl -rwxr-xr-x@ 1 iwata staff 942 2 5 03:07 sample2.pl drwxrwxrwx 3 iwata staff 102 2 5 03:06 tmp %
ファイル名と中身
先の実行結果より,キャッシュファイルの名前は,「キー名のSHA1ハッシュ」ということがわかるかと思います.
また,キャッシュファイルを覗いてみると,次のような感じのものが確認できます.バイナリファイルですが,それっぽい部分もありますね.
% less ./tmp/filecache_sample/a/1/d/a1dd4114c98069523cdf1d90ff3a43222670b8c8 ^E^G^B^@^@^@^B ^Dvar1^D^Q^MCache::Object^C^@^@^@^F^E^@^@^@^E_Size Kk^MS^@^@^@^K_Expires_At^E^@^@^@^D_Key Kk^M^W^@^@^@^K_Created_At^D^C^@^@^@^C ^Dhuga^@^@^@^Cbar ^Dpiyo^@^@^@^Cbaz ^Dhoge^@^@^@^Cfoo^@^@^@^E_Data Kk^M^W^@^@^@^L_Accessed_At
cache_depthオプションとdirectory_umaskオプション
Cache::FileCacheオブジェクトを生成する際のその他のオプションについてもざっと確認してみます.
(sample3.plのソースコードは「おわりに」以降に掲載.)
% ./sample3.pl
# tree -p
./tmp
`-- [drwxrwxrwx] filecache_sample
|-- [drwxrwxrwx] 9
| `-- [drwxrwxrwx] b
| `-- [drwxrwxrwx] 6
| `-- [-rw-r--r--] 9b63072850833c63c5200f2c35b3edc4118f705e
`-- [drwxrwxrwx] a
`-- [drwxrwxrwx] 1
`-- [drwxrwxrwx] d
`-- [-rw-r--r--] a1dd4114c98069523cdf1d90ff3a43222670b8c8
7 directories, 2 files
# changing cache_depth and directory_umask
# tree -p
./tmp
`-- [drwxrwxrwx] filecache_sample
|-- [drwxrwxrwx] 9
| `-- [drwxrwxrwx] b
| `-- [drwxrwxrwx] 6
| |-- [drwx------] 3
| | `-- [drwx------] 0
| | `-- [drwx------] 7
| | `-- [-rw-r--r--] 9b63072850833c63c5200f2c35b3edc4118f705e
| `-- [-rw-r--r--] 9b63072850833c63c5200f2c35b3edc4118f705e
`-- [drwxrwxrwx] a
`-- [drwxrwxrwx] 1
`-- [drwxrwxrwx] d
|-- [-rw-r--r--] a1dd4114c98069523cdf1d90ff3a43222670b8c8
`-- [drwx------] d
`-- [drwx------] 4
`-- [drwx------] 1
`-- [-rw-r--r--] a1dd4114c98069523cdf1d90ff3a43222670b8c8
13 directories, 4 files
%簡単なベンチマーク
次の4種類の処理を,それぞれ10000回実行して比較してみました.
(sample4.plのソースコード,YAMLファイルsample.ymlは「おわりに」以降に掲載.)
- b1
- YAMLモジュールを使ってYAMLファイルを読み込み,ハッシュリファレンスに変換する
- b2
- YAML::Syckモジュールを使ってYAMLファイルを読み込み,ハッシュリファレンスに変換する
- b3
- ハッシュリファレンスのキャッシュがない場合のみ,YAMLモジュールを使ってYAMLファイルを読み込んでハッシュリファレンスに変換,キャッシュする
- b4
- ハッシュリファレンスのキャッシュがない場合のみ,YAML::Syckモジュールを使ってYAMLファイルを読み込んでハッシュリファレンスに変換,キャッシュする
実行したMacBookのスペックについては,次あたりを参照してください.
実行結果は次のようになりました.
% ./sample4.pl
Benchmark: timing 10000 iterations of b1, b2, b3, b4...
b1: 75 wallclock secs (72.25 usr + 0.70 sys = 72.95 CPU) @ 137.08/s (n=10000)
b2: 3 wallclock secs ( 2.16 usr + 0.28 sys = 2.44 CPU) @ 4098.36/s (n=10000)
b3: 3 wallclock secs ( 2.62 usr + 0.64 sys = 3.26 CPU) @ 3067.48/s (n=10000)
b4: 4 wallclock secs ( 2.59 usr + 0.64 sys = 3.23 CPU) @ 3095.98/s (n=10000)
Rate b1 b3 b4 b2
b1 137/s -- -96% -96% -97%
b3 3067/s 2138% -- -1% -25%
b4 3096/s 2159% 1% -- -24%
b2 4098/s 2890% 34% 32% --
%このことから,次のことがわかるんじゃないかな,と.
- YAMLモジュールを使って毎回読み込むよりは,Cache::FileCacheによってキャッシュした方が効率がよい
- YAML::Syckモジュールが使える場合,Cache::FileCacheによるキャッシュは逆効果っぽい
おわりに
以上,Cache::FileCacheモジュールを使ってみたメモでした.Cache::Cacheモジュールのサブクラスにあたるため,そのドキュメントもざっと読んだところ,他のサブクラス(例えばCache::MemoryCacheとか)も同じようなメソッドで操作できるようですね.今後試してみることにします.
ソースコード:sample1.pl
#!/usr/bin/env perl use strict; use warnings; use utf8; use YAML::Syck; use Cache::FileCache; use Data::Dumper; sub p { print "\n", @_, "\n"; } sub d { print Dumper @_; } my $cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 10, }); my $var1 = {qw/foo hoge bar huga baz piyo/}; my $var2 = {qw/a 1 b 2 c 3/}; $cache->set('var1', $var1); $cache->set('var2', $var2, 20); sub cache_test { my @plan = @_; my $sec = 0; for my $s (@plan) { $sec += $s; sleep($s); p "# about $sec sec."; d $cache->get('var1'); d $cache->get('var2'); } } cache_test 0, 5, 6, 4, 6; __END__
sample2.pl
#!/usr/bin/env perl use strict; use warnings; use utf8; use YAML::Syck; use Cache::FileCache; use Data::Dumper; sub p { print "\n", @_, "\n"; } sub d { print Dumper @_; } my $cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 60, }); my $var1 = {qw/foo hoge bar huga baz piyo/}; my $var2 = {qw/a 1 b 2 c 3/}; $cache->set('var1', $var1); $cache->set('var2', $var2); my $cache_root = $cache->get_cache_root(); p '# cache_root'; p $cache_root; p '# tree'; p `tree $cache_root`; p '# sha1'; p sha1_hex 'var1'; p sha1_hex 'var2'; # # cache_rootを変更してみる # p '# changing cache_root'; $cache->set_cache_root('./tmp'); $cache->set('var1', $var1); $cache->set('var2', $var2); $cache_root = $cache->get_cache_root(); p '# cache_root'; p $cache_root; p '# tree'; p `tree $cache_root`; p '# sha1'; p sha1_hex 'var1'; p sha1_hex 'var2'; __END__
sample3.pl
#!/usr/bin/env perl use strict; use warnings; use utf8; use YAML::Syck; use Cache::FileCache; use Digest::SHA1 qw/sha1_hex/; use Data::Dumper; sub p { print "\n", @_, "\n"; } sub d { print Dumper @_; } my $cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 60, cache_root => './tmp', }); my $var1 = {qw/foo hoge bar huga baz piyo/}; my $var2 = {qw/a 1 b 2 c 3/}; $cache->set('var1', $var1); $cache->set('var2', $var2); my $cache_root = $cache->get_cache_root(); p '# tree -p'; p `tree -p $cache_root`; # # cache_depth,directory_umaskを変更してみる # p '# changing cache_depth and directory_umask'; $cache->set_cache_depth(6); $cache->set_directory_umask(077); $cache->set('var1', $var1); $cache->set('var2', $var2); p '# tree -p'; p `tree -p $cache_root`; __END__
sample4.pl
#!/usr/bin/env perl use strict; use warnings; use utf8; use YAML; use YAML::Syck; use Cache::FileCache; use Benchmark qw/:all/; use Data::Dumper; sub p { print "\n", @_, "\n"; } sub d { print Dumper @_; } my $cache = Cache::FileCache->new({ namespace => 'filecache_sample', default_expires_in => 600, cache_root => './tmp', }); my $yamlfile = './sample.yml'; my ($b1, $b2, $b3, $b4); # # b1: YAML # $b1 = sub { my $hash = YAML::LoadFile($yamlfile); return $hash; }; # # b2: YAML::Syck # $b2 = sub { my $hash = YAML::Syck::LoadFile($yamlfile); return $hash; }; # # b3: Cache::FileCache || YAML # $b3 = sub { my $key = 'sample4_b3'; my $hash = $cache->get($key); unless (defined $hash) { $hash = YAML::LoadFile($yamlfile); $cache->set($key, $hash); } return $hash; }; # # b4: Cache::FileCache || YAML::Syck # $b4 = sub { my $key = 'sample4_b4'; my $hash = $cache->get($key); unless (defined $hash) { $hash = YAML::Syck::LoadFile($yamlfile); $cache->set($key, $hash); } return $hash; }; my $results = timethese( 10_000, { b1 => $b1, b2 => $b2, b3 => $b3, b4 => $b4, }, ); cmpthese($results); __END__
sample.yml
---
LOCATION:
PROTOCOL: http
DOMAIN: denske.local
SUBDOMAIN: www.
PATH: /path
PAGE:
COMMON_CSS: [_base, _layout, _additional]
COMMON_JS: [_base, _additional]
DSI:
DBI: 1
YAML: 1
DB:
TYPE: mysql
NAME: sample
HOST: localhost
USER: sample
PASSWD: samplepasswd
TABLE_PREFIX: sample_
CACHE:
ENABLED: 1
NAMESPACE: samplesample
EXPIRES: 600
COOKIE:
EXPIRES: +14d
DOMAIN:
PATH: /
SECURE: 0
MAIL:
SERVER_SMTP: localhost:587
ADDRESS:
FROM: noreply@example.com
CC:
SIGNATURE: |
--
your email signature.
SESSION_KEY_NAME: sample_session
CONTENT_TYPE_DEFAULT: text/html; charset=utf-8