Ruby で foo/bar/baz というディレクトリーが作りたいとする。
FileUtils.#makedirs というメソッドを使い,以下のように書ける
require "fileutils"
FileUtils.mkpath("foo/bar/baz")
何階層の深さでも一気に作ってくれる。
ディレクトリーが既に存在していれば何もしない(エラーも出ない)。
なので使い勝手が良い。
メソッド名は mkdir_p, makedirs でもよい(エイリアス)。
さまざまな階層の大量のファイルを生成・保存するような場合,「FileUtils.mkpath で格納ディレクトリーを作ってファイルを保存する」ということを多回数繰り返すことになる。
このとき,ディレクトリーパスに重複が多いなら,
FileUtils.mkpath(dir_path) unless File.exist?(dir_path)
のように「ディレクトリーがまだ無かったら mkpath する」という書き方にしたほうがよいかもしれない。
既に存在していても問題ないはずなのに,なぜわざわざそんなことをするのか?
こうしないと遅いからだ。
ベンチマークテストをしてみよう。
benchmark_driver gem を install しておき,
prelude: |
require "fileutils"
dir_path = "foo/bar/baz"
FileUtils.mkpath(dir_path)
benchmark:
check: FileUtils.mkpath(dir_path) unless File.exist?(dir_path)
no_check: FileUtils.mkpath(dir_path)
という YAML ファイルを用意する。
これは,あらかじめ foo/bar/baz を作った状態で以下の二つを比較する:
- あらかじめ存在をチェックして
FileUtils.mkpathするかどうか決める(check) → 既に存在しているので 100% 実行されない - 問答無用で
FileUtils.mkpathする(no_check)
実行は
benchmark-driver mkpath.yaml
で。
結果は,私の環境では以下のとおり:
Comparison:
check: 961947.1 i/s
no_check: 628537.3 i/s - 1.53x slower
存在チェックしないほうはなんと 1.5 倍もの時間がかかっている。
なんでやねん?
「存在チェックする場合は FileUtils.mkpath を呼び出さないんだから速くてあたりまえじゃん」ではない。
メソッド呼び出し自体のコストは小さいし,存在チェックするほうは File.exist? のコストがかかっているわけだし。
そもそも,存在チェックで実行時間が抑えられるなら,FileUtils.mkpath の内部でそれをやってくれてもよさそうなものだ。
というわけで,私にはたいへん謎なのだが,FileUtils.mkpath のコストが無視できない用途で,生成ディレクトリーに重複が多い場合には存在チェックを検討してもよいかと思う。