こんなの頑張る意味あんの? って気もしなくもないけど、必要なことの小さいサンプルになったのでグラフまで書いてみた。
rsync --delete よりも rm -rf のほうが、 getdents (readdir) の累計時間が 30 秒多いから real も 30 秒多い、というのが前提のように書いてますが、実際に正しいのかは未確認。重要なのは methodology ってことで。
調査用 dtrace スクリプト
aggregation の key に wall からの経過時刻 (秒単位) を入れることで秒ごとに出した。 quantize したいけど @ を数えんのめんどいし、あんまり printf しまくると drop されちゃうし。
avg いるの? というのは excel の pivot table の都合。
#!/usr/sbin/dtrace -s int start_wall; dtrace:::BEGIN { start_wall = timestamp; } syscall:::entry /execname == "rm" || execname == "rsync"/ { self->start[pid, probefunc] = timestamp; } syscall:::return /self->start[pid, probefunc] && (probefunc == "getdents64" || probefunc == "unlinkat")/ { @sum[pid, execname, probefunc, "sum", (timestamp - start_wall)/1000000000] = sum(timestamp - self->start[pid, probefunc]); @avg[pid, execname, probefunc, "avg", (timestamp - start_wall)/1000000000] = avg(timestamp - self->start[pid, probefunc]); @cnt[pid, execname, probefunc, "cnt", (timestamp - start_wall)/1000000000] = count(); self->start[pid, probefunc] = 0; }
getdents64 と unlinkat の流れ
なんとも分かりやすい結果で、以下がわかった。
- rsync は最初に getdents (readdir) しまくってファイルリスト作り (~80sec)、その後いっきにファイルを消す (~120sec)
- rm -rf は 5~10 秒ごとに readdir と unlink を繰り返す
- ただし、どちらも最終的に syscall の回数は同じ *1
結果として rm のほうが遅くなる理由は、やはり 1回の readdir にかかる時間が長い様子。
直感的にはファイル数が少ないほうが readdir が速いのかと思ったが、そうでもなかった。ブレはあるものの、 rm で unlink が進んだ後の readdir でも分かりやすい速度変化はなかった。
まとめ
やっぱ一気にやったほうが速い。ただし最初にファイルリスト作る分メモリをたくさん必要とする。 prstat で眺めただけだけど、 rm は RSS が 24M で cap されてるような雰囲気。一方 rsync のほうはモリモリ増えていって 44M で止まった。
...という以前に、これはファイルシステムで相当変わるはず (とくに zfs なら log device に ssd 突っ込んで xargs -P で rm とか) なので、 zfs においては余り差が出なかったようです。
個人的には zfs はディレクトリ中のファイルが多くても安定して動くよ、という結論で終えたいですが、そこまで言うにはこれじゃ厳しいな。