大量のファイルを同時に圧縮する2012年03月25日 11時43分36秒

時折、大量のファイルを圧縮することがある。一つずつのファイルの大きさは、数 MB から数 GB まで、多種多様。そんなファイルが数百にも上る。Solaris など、特にコア数が多いホスト上の時は、手っ取り早く並列処理したい。CPU リソースは余っている事が多いので、100 以上あるコアのうち、20、30 ぐらい使ったところで気がつく人はほとんどおらず、苦情も来ない。Solaris ほどコアの数があると、CPU を全てしっかり使い切るのは結構難しい。とは言ったものの nice 20 で処理しても、流石に一人で全ての CPU を 100% 使うのは気が引ける。

実は、大きさの異なる仕事を並列的に処理するスクリプトを書くのはとても難しい。シェルスクリプトを筆頭に、特定の数だけ並列化するライブラリがない。そうなると、自分で子プロセスを生成し、管理することになる。それに、各々の仕事量が異なっているので、仕事の割り振りも工夫が必要になる。一つだけ、大きい仕事ばかり抱えて、他のプロセスが直ぐに終わってしまったのでは、効果が下がる。そう考えていくと、ささっと書いて使い捨てられるほど、簡単にはいかない。

いくつかそのようなスクリプトの尻拭いをしなければいけないことがあったが、どれも醜いものだった。コード数が不必要に長い。余計な処理ばかり増えて、本題の作業効率が余り上がっていない。負荷が偏っていて、無駄が多い。どれも一生懸命、書いたのは見て取れるのだが、直す人の気苦労などまったくわかっちゃいない。

実は、その様に百行から数百行にわたる、たった一行のプログラムで並列処理の仕事配分をしてくれるプログラムがある。その名も make。make だけだとちょっと扱いづらいので awk も当てる。

残念ながら、make にコマンドラインから直接 make ルールを打ち込む人たち等は見たことがない。make をシェルスクリプトの様に、手順を追って書く人たちや、make に依存関係の処理を任せれば簡単なのに、スクリプトで難解な発明をし、四苦八苦している人たちはよく見かけるが。

さて、愚痴と能書きが多くなったがさて本題。make での古いやつだと並列処理が出来ないものもあるので、GNU make や BSD make など若干新しいものが必要になる。


% cat bzip20
#!/bin/sh

exec $* | \
  nawk 'BEGIN{ print "ALL:" }
    { print $1 ".bz2 : " $1;
    print "\tbzip2 " $1;
    print "ALL : " $1 ".bz2" }' | \
  gmake -j 20 -f -
% bzip20 "ls *.txt"
% bzip20 "find . -name '*.log'"

exec を通して、ファイルを探し、それらのファイルを圧縮するターゲットを作る。ALL を一番最初にしているので、全てが圧縮される。exec を通しているので、圧縮順を気にせず適当に渡したり、逆に大きさ順に並べて、極力処理が偏らないように出来る。