Re: rsync で削除すると速い (?) 件

こんなの頑張る意味あんの? って気もしなくもないけど、必要なことの小さいサンプルになったのでグラフまで書いてみた。

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ディレクトリ中のファイルが多くても安定して動くよ、という結論で終えたいですが、そこまで言うにはこれじゃ厳しいな。

*1:追記: syscall の回数が同じなのは、 rm -rf がディレクトリ内のファイル数 N に対して O(N) であってほしいという要請から当然といえば当然ですね。