それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

並列実行はxargsコマンドのマルチプロセスを使うと楽ちん高速化ですね。

ruby で同時処理のスレッド制御 でスレッド制御しながら、キューを使わずコマンドを並列実行してきた。

よく考えたら、こんな車輪の再発明などしなくても先人の知恵を借りたほうが楽そう。

調べてみたらxargsに同時実行のオプションがあるのを確認した。
max-procsのこのオプションを知って、せっかく覚えたての find -exec 投げ捨てたくなった


xargs 最強じゃないですか。

xargs にコマンドを並列実行する方法がある。

-P/ --max-procs オプション
       --max-procs=max-procs
       -P max-procs
              Run  up  to max-procs processes at a time; the default is 1.  If max-procs is 0, xargs will run as many processes as possible at a
              time.  Use the -n option with -P; otherwise chances are that only one exec will be done.

(man ページより)

並列で早くなるのか次の実験をしてみました

test.pngをtest.jpg に変換する作業を繰り返してみました。

 test.png を 1000枚 jpg に変換する
 作ったJPEG画像 1000枚 を削除する

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -I{}  convert test.png  {}

real     0m26.534s
user     0m15.756s
sys      0m6.532s 

次に削除を測定。

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -I{}  rm  {}

real     0m4.271s
user     0m1.278s
sys     0m1.830s

並列度10で、並列実行してみる。

PNGJPEGの変換

1個ずつの時は、26秒だったが・・・

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P10 -I{}  convert test.png  {}

real     0m7.555s
user     0m18.874s
sys     0m7.169s
JPG画像1000枚の削除

削除1ずつ起動すると、4 秒だったが・・・

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P10 -I{}  rm  {}

real     0m1.818s
user     0m1.711s
sys     0m2.408s

というわけで、ディスクのIOコストやCPUのコストを超えない限りは限界まで早くなりそうです。

並列度を色々変えて実験してみました。

10までで充分早くなったので満足でした。せっかくなので、並列度を色々変えて時間を測ってみました。

並列度2の場合

#作成

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P2 -I{}  convert test.png  {}

real     0m16.761s
user     0m18.541s
sys     0m7.461s
#削除

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P2 -I{}  rm  {}

real     0m2.409s
user     0m1.384s
sys     0m1.973s  

並列度4の場合

#作成

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P4 -I{}  convert test.png  {}

real     0m10.660s
user     0m18.978s
sys     0m7.364s
#削除

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P4 -I{}  rm  {}

real     0m1.850s
user     0m1.685s
sys     0m2.330s

並列度100の場合

100までくると、さすがに起動コストやメモリのスワップが発生スルだろうと思っテストしてみた。
やっぱり遅くなった。一方で削除のような簡単な処理は、影響が少なさそう

#作成
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P100 -I{}  convert test.png  {}

real     0m9.006s
user     0m19.025s
sys     0m7.514s 

#削除
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P100 -I{}  rm  {}

real     0m1.681s
user     0m1.704s
sys     0m2.363s 


 ***並列度1000の時

#作成
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P1000 -I{}  convert test.png  {}

real     0m8.841s
user     0m18.919s
sys     0m7.318s
#削除
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P1000 -I{}  rm  {}

real     0m1.788s
user     0m1.707s
sys     0m2.388s


処理1000に対して、並列度1000.つまり & を着けて起動したのと同じことですね。
どうもうちのマシンだと、7秒が上限のようです。別のボトルネックにアタったようです。

並列度10000の時

並列度をもっとあげてみたり。

takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P10000 -I{}  convert test.png  {}

real     0m9.127s
user     0m19.045s
sys     0m7.499s
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P10000 -I{}  rm  {}

real     0m1.793s
user     0m1.705s
sys     0m2.371s

まとめ

並列度 PNGJPEGに1000枚を変換 1000枚のJPEG画像を削除
なし(何もしない) 0m26.534s 0m4.271s
   2 0 m16.761s 0m2.409s
   4 0m10.660s 0m1.850s
  10 0m7.555s 0m4.271s
 100 0m9.006s 0m1.681s
1000 0m8.841s 0m1.793s




どうやら、私のマシン限界は今回の実験環境だと10s弱程度のようでした。
単純な起動だけ26secよりは断然早くなったのでOKです。


ディスクIOで詰まりそうなので、real で比較 。

ちなみに、単純なCPU起動時間だけのSYSだと並列度で結果が殆ど変わらないので、IOコストが限界を迎えていることも想像がつく。

1つずつ起動するより、ファイルに書き出している最中にコマンドを起動したほうが時間節約にはなるってことですね。

実験をしたマシン


f:id:takuya_1st:20131101061818p:plain




サンプル画像
f:id:takuya_1st:20131101061816p:plain


おまけ

ギリギリまで早くする -P0 オプション。

パフォーマンスチューニングなんて測定したり面倒だぜ!って人には xargsにお任せってオプションが有ります。



試してみた。

作成
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P0 -I{}  convert test.png  {}

real     0m8.294s
user     0m18.902s
sys     0m7.349s
削除
takuya@air:~/Desktop$ time for i in {1..1000}; do echo  $i.jpg ; done | sort | xargs -P0 -I{}  rm  {}

real     0m1.895s
user     0m1.709s
sys     0m2.418s  


たしかに、測定値のギリギリ上限に近いです。

自動的に上限に近づけてくれるなら楽ちんですね。




 -n : さらに早くなるかもしれないオプション  

上記の例だとrm コマンドは単純に1000回起動してるわけで、 rm コマンドに一度にもっと引数を渡せば、起動回数が少なくて済む。というメリットも有る。

最後に。 -P オプションはかならずあるとは限らない

このオプションを知ってても、POSIX仕様には含まれないので、必ずあるとは限らない。だから使う前に調べましょう。いきなりシェルスクリプトに組み込むのはご法度