ハッシュに大量のデータを挿入する際、プログラムの動作を出来る限り早く(高速に)、処理を軽くしたい。実は一行付け足すだけで、かなり違ってくる技がある。
keysは本来ハッシュのキーを取り出す際に利用するのだが、これから登録するハッシュのキーの数をあらかじめ宣言する機能を持つ。これを行うことでPerl内部の処理が1ステップ減り、大量のデータを追加する際、高速な動作が期待できる。
サンプル
;# ;#ハッシュの処理速度を上げる ;# my %hash = (); #-- ここで個数を指定する --# keys(%hash) = 30000; #この場合は3万件 open(DAT, 'ken_all.txt') or die($!); while(<DAT>){ chomp; my ( $post_cd, $address ) = split(/\t/); $hash{$post_cd} = $address; } close(DAT);
検証
原理はシンプル。 例外もあるが、原則としてPerlはハッシュに値が追加されるたびにメモリの確保を行っている。この作業を毎回行うよりも、冒頭で一括して行う方が速いため、動作が軽くなるのである。本当に軽くなるか、実際に実験を行ってみた。
ここでは1,000件、5,000件、30,000件のテストデータを用意した。郵政公社が公開している郵便番号のデータを加工し、ランダムに抽出した物である。
○1,000件 Benchmark: timing 1000 iterations of NON_SET, SET... NON_SET: 4 wallclock secs ( 3.99 usr + 0.00 sys = 3.99 CPU) @ 250.63/s (n=1000) SET: 3 wallclock secs ( 3.60 usr + 0.01 sys = 3.61 CPU) @ 277.01/s (n=1000) ○5,000件 Benchmark: timing 1000 iterations of NON_SET, SET... NON_SET: 20 wallclock secs (19.87 usr + 0.12 sys = 19.99 CPU) @ 50.03/s (n=1000) SET: 18 wallclock secs (18.30 usr + 0.08 sys = 18.38 CPU) @ 54.41/s (n=1000) ○30,000件 Benchmark: timing 1000 iterations of NON_SET, SET... NON_SET: 121 wallclock secs (119.55 usr + 0.93 sys = 120.48 CPU) @ 8.30/s (n=1000) SET: 111 wallclock secs (110.51 usr + 0.58 sys = 111.09 CPU) @ 9.00/s (n=1000)
実行結果を見て分かる通り、1000件などデータ件数が少ないうちは大差ないが、数万件レベルになってくると如実な違いが見て取れる。
ベンチマーク測定用コード
#!/usr/bin/perl ;# ;#ハッシュのベンチマーク ;# use Benchmark; #--------------------------------------------# #ベンチマーク #--------------------------------------------# timethese( 1000, { 'SET' => 'set1();' , 'NON_SET' => 'set2();' } ); #--------------------------------------------# #都度メモリ確保 #--------------------------------------------# sub set1{ my %hash = (); open(DAT, 'ken_all.txt') or die($!); while(<DAT>){ chomp; my ( $post_cd, $address ) = split(/\t/); $hash{$post_cd} = $address; } close(DAT); } #--------------------------------------------# #冒頭で一括してメモリ確保 #--------------------------------------------# sub set2{ my %hash = (); keys(%hash) = 30000; open(DAT, 'ken_all.txt') or die($!); while(<DAT>){ chomp; my ( $post_cd, $address ) = split(/\t/); $hash{$post_cd} = $address; } close(DAT); }