0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby】存在するディレクトリーを FileUtils.mkpath するのが遅い件

Posted at

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 しておき,

mkpath.yaml
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 のコストが無視できない用途で,生成ディレクトリーに重複が多い場合には存在チェックを検討してもよいかと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?