ハッシュに値が1件でも登録されているか、知りたい場合

追記2009-11-5:
初めてのPerl第5版には、スカラコンテキストでは keys は要素の個数を返すだけでハッシュの全要素にアクセスしたりはしないと書いてある。なので、以下の例なら問題ないはずなんだけど・・・。


どーも Perl スクリプトの動作が重ったるい。1万件程度のデータの処理をしているのだが、2分程度も要してしまう・・・。テスト環境でもそれだけの件数を投入したところ再現したため、すぐに原因は特定出来たが、その原因というのが非常に初歩的なミスで、昔に書いた意味の無いコードの切れっ端がソースに残っていたという・・・。


どうも、ハッシュに値が一件でも登録されているか?ということを確認したかったようで(私が書いたコードなのだが、数ヶ月前のもので、よく覚えていない)、以下のようなコードになっていた。

if (my @k = keys %hash) {
  ...
}

で、この %hash に数万件が登録されており、この処理自体も数万回呼ばれる処理だったというわけである。※ すみません、ちょっと記憶が曖昧なんで、例が不自然っす・・・。さすがにこんな書き方しないと思うが、まあこういう雰囲気ということで。


keys 関数はハッシュのキーだけをすべて抜き出して配列を作り返してくれるため、上記コードでももちろん期待動作はするが、ハッシュに大量のデータが登録されており、しかもそれが大量にコールされた場合は、非常に遅くなる。


今回の場合は、上記の if 文は一回あたりの処理におよそ 10ms を要しており、それが1万数千回呼ばれるために、全体の処理時間が2分になった、というわけだ。テストデータとして数百件処理した程度では気がつかない。

要素の無いハッシュはスカラコンテキストで 0 を返すため、もっと単純に、

if (%hash) {
  ...
}


と書き換えるだけでこの処理は1万回であっても1秒以内に終わるようになるだろう。


今回の場合は、そもそもこの if 判定自体が不要であったため、この判定ごと除去した。


スクリプトの処理速度は全体で1秒以内、CPU 負荷も 90% 以上であったものが、0.5 % 以下まで落ちた。要するにこのスクリプトの処理時間の 99% 以上がこの if 文で費やされていたわけである・・・。


2重に反省させられるケアレスミスだった。てゆーかへこんだ。