綾小路龍之介の素人思考

[linux] xargsでプログラムを並列起動することによる実行速度測定

xargs -Pを使うと簡単に並列起動をすることが出来る。また、bashの組み込みコマンドであるtimeを使ってプログラムの実行時間を測定できる。これらを組み合わせて、任意のプログラムを並列に起動(エセ並列化)させたときの速度を測定する。

速度測定をするプログラムは./a.out。これに適当な引数を与え、100回走らせることを考える。プログラムをひとつづつ連続的に走らせる方法と、プログラムをP個づつ並列に走らせる方法とがある。

まずは適当な引数を100個収めたファイルを作る。ここでは、null文字を100個収めたファイルnull.txtを作成。

$ for i in `seq 1 100`; do echo -n -e "\0000"; done > null.txt
$ ls -l null.txt
-rw-r--r-- 1 **** **** 100 Jan  6 01:50 null.txt

このファイルから引数をひとつづつ読み込んでxargsに渡すには以下のようにする。-aは引数を収めたファイル、-0は引数の区切り文字をnull文字にするオプション、-nはプログラムに与える引数の数。-rは引数が無い場合にはプログラムを起動しないオプション。-tはプログラム起動前にsterrに起動するコマンドを表示するオプション。ここではechoコマンドを走らせている。

$ xargs -a null.txt -0 -n1 -r -t echo
echo

echo

...
echo

echo

本当に100回起動されたか確認。

$ xargs -a null.txt -0 -n1 -r -t echo 2>&1 | grep echo | wc
    100     100     700

上の例では、それぞれの引数を1つずつechoコマンドを走らせている。xargsを使って、10並列でプログラムを起動するには、-Pオプションを使って、以下のようにする。最初の例は-P1と同じことである。

$ xargs -a null.txt -0 -n1 -r -P10 -t echo 2>&1 | grep echo | wc
    100     100     700
$ xargs -a null.txt -0 -n1 -r -P10 -t echo
echo
echo
echo
echo
echo
echo
echo
echo
echo
echo

echo

echo

echo

echo






echo
echo
echo
echo
echo
echo

echo

...
echo

echo










bash組み込みのtimeコマンドを使って、上記2つの場合について速度を比較すると以下のような結果が得られる。並列度10にすることで、xargsの起動から終了までにかかる時間がおよそ10分の一になっていることがわかる。

$ time xargs -a null.txt -0 -n1 -P1 -r -t echo 1>/dev/null 2>/dev/null

real    0m9.913s
user    0m0.068s
sys     0m0.000s
$ time xargs -a null.txt -0 -n1 -P10 -r -t echo 1>/dev/null 2>/dev/null

real    0m1.132s
user    0m0.020s
sys     0m0.000s

並列度を変化させることで、xargsの起動から終了までにかかる時間が変化する様子を調べると、以下のようになる。便宜的に並列度を出力している。

$ i=0; while [ 0 ]; do i=`expr $i + 1`; echo $i; time xargs -a null.txt -0 -n1 -P$i -r -t echo 1>/dev/null 2>/dev/null; done
1

real    0m10.036s
user    0m0.036s
sys     0m0.000s
2

real    0m4.920s
user    0m0.008s
sys     0m0.000s
3

real    0m3.380s
user    0m0.008s
sys     0m0.000s
4

real    0m2.551s
user    0m0.008s
sys     0m0.000s
...

timeコマンドの出力が見づらいので、出力フォーマットを指定する。これにはTIMEFORMAT変数を使う。以下のようになる。

$ i=0; while [ 0 ]; do i=`expr $i + 1`; TIMEFORMAT=$i$'\t%3R\t%3U\t%3S'; time xargs -a null.txt -0 -n1 -P$i -r -t echo 1>/dev/null 2>/dev/null; done
1       9.764   0.020   0.000
2       5.106   0.020   0.004
3       3.376   0.008   0.000
4       2.555   0.028   0.000
5       2.103   0.048   0.000
6       1.775   0.044   0.004
7       1.599   0.056   0.012
8       1.384   0.004   0.008
9       1.292   0.012   0.000
10      1.112   0.000   0.008
11      1.068   0.004   0.004
12      1.013   0.004   0.008
13      0.868   0.008   0.000
14      0.872   0.016   0.000
15      0.795   0.000   0.008
16      0.805   0.008   0.000
17      0.715   0.008   0.000
18      0.707   0.000   0.008
19      0.701   0.008   0.000
20      0.671   0.000   0.008
21      0.691   0.008   0.008
22      0.621   0.012   0.000
23      0.619   0.008   0.000
24      0.605   0.000   0.008
25      0.648   0.032   0.000
26      0.636   0.008   0.000
27      0.566   0.008   0.000
28      0.563   0.008   0.000
29      0.561   0.008   0.000
30      0.510   0.008   0.000
31      0.486   0.012   0.000
32      0.597   0.000   0.008

実際に速度を測定したいプログラムを使ってテストを行う。以下のようにすれば良い。これまでのechoだった部分を"./a.out args"のように書き換える。timeコマンドの結果はstderrに吐き出されるので、null_result.stderrを見ると上のような結果が得られるはずである。

$ i=0; while [ 0 ]; do i=`expr $i + 1`; TIMEFORMAT=$i$'\t%3R\t%3U\t%3S'; time xargs -a null.txt -0 -n1 -P$i -r ./a.out args 1>/dev/null 2>/dev/null; done 1>null_result.stdout 2>null_result.stderr;

結果は以下のようになった。

$ cat null_result.stderr
1       2231.087        745.335 0.760
2       1124.442        738.622 0.748
3       908.133 745.207 0.864
4       763.334 754.663 0.728
5       683.871 744.787 0.648
6       625.984 743.258 0.644
7       602.584 748.495 0.700
8       563.021 740.770 0.596
9       565.600 762.632 0.684
10      544.916 761.512 0.692
11      521.619 751.451 0.604
12      503.836 746.031 0.604
13      506.959 756.531 0.636
14      494.386 757.923 0.740
15      486.060 749.827 0.516
16      490.879 775.648 0.836
17      483.918 769.444 0.712
18      479.095 762.464 0.688
19      476.927 777.509 0.712
20      485.698 796.518 0.788
21      467.004 765.268 0.736
22      463.364 762.036 0.732
23      461.189 761.072 0.556
24      462.172 783.633 0.716
25      444.407 756.083 0.612
26      445.948 759.835 0.652
27      448.918 759.539 0.644
28      451.934 767.004 0.708
29      449.246 768.168 0.732
30      454.193 769.976 0.748
31      446.738 767.532 0.652
32      447.351 786.585 0.792
33      436.138 767.104 0.748
34      433.883 762.136 0.624
35      434.963 767.620 0.768
36      425.329 749.183 0.700
37      426.545 753.271 0.644
38      424.011 750.539 0.596
39      427.766 757.179 0.648
40      439.302 774.432 0.640
41      429.527 760.288 0.616
42      431.858 765.164 0.660
43      420.383 746.231 0.588
44      436.645 769.828 0.692
45      433.256 764.844 0.648
46      430.029 762.780 0.596
47      431.488 763.760 0.632
48      423.816 768.032 0.696
49      423.298 775.164 0.728
50      428.359 770.068 0.888
51      431.649 775.944 0.864
52      435.586 779.437 0.652
53      430.384 770.820 0.756
54      429.638 771.364 1.284
55      432.674 773.464 1.504
56      426.718 775.732 0.768
57      430.906 788.725 0.784
58      426.632 779.757 0.604
59      421.790 774.300 0.748
60      431.381 785.629 0.732
61      419.327 768.572 0.656
62      423.703 774.852 0.804
63      418.136 766.016 0.852
64      421.852 774.152 0.752
65      441.128 808.671 0.800
66      452.106 824.768 0.844
67      438.597 804.358 0.832
68      443.580 814.139 0.792
69      434.589 792.294 0.848
70      420.656 768.780 0.828
71      412.692 755.023 0.708
72      432.970 795.366 0.616
73      424.758 776.497 0.748
74      428.825 787.397 0.760
75      418.555 765.460 0.788
76      416.814 764.396 0.816
77      409.508 749.803 0.692
78      414.816 761.104 0.716
79      419.677 768.580 0.784
80      453.185 835.932 0.700
81      419.973 772.328 0.664
82      419.565 770.188 0.900
83      416.712 764.336 0.832
84      436.502 801.018 0.820
85      408.737 750.091 0.752
86      418.378 764.428 0.924
87      425.620 780.813 0.904
88      423.889 778.313 0.776
89      416.633 761.368 0.680
90      415.428 760.608 0.740
91      431.925 794.134 0.688
92      417.406 768.136 0.592
93      408.748 749.823 0.764
94      412.365 753.531 0.724
95      394.567 751.211 0.604
96      400.393 760.612 0.616

以下のgnuplotスクリプトでグラフを書いてみる。

$ cat null_result.plt
#!/usr/bin/gnuplot
set terminal png
set output 'null_result.png'
set grid
set ytics nomirror
set y2tics
set yrange [0:]
set y2range [0:]
set ylabel "real time, user time [sec]"
set y2label "sys time [sec]"
set xlabel "parallel process [num.]"
plot 'null_result.stderr' using 1:2 title "real",\
     '' using 1:3 title "user",\
     '' using 1:4 title "sys" axis x1y2
$ gnuplot < null_result.plt

グラフは以下のようになる。

parallel process

リファレンス

  1. Man page of XARGS
  2. xargs を使ってカジュアルに並列処理 - tagomorisのメモ置き場
  3. xargsもすごいのでもう死ぬしかない | uuu
  4. 【 time 】 指定したコマンドの実行時間を表示する - Linuxコマンド集:ITpro
  5. Bash Variables - Bash Reference Manual
  6. bash time TIMEFORMAT - Google 検索
  7. bashでtimeを一行表示する - 計算機と戯れる日々

ソーシャルブックマーク

  1. はてなブックマーク
  2. Google Bookmarks
  3. del.icio.us

ChangeLog

  1. Posted: 2010-03-21T03:38:25+09:00
  2. Modified: 2010-03-21T03:38:25+09:00
  3. Generated: 2017-02-06T23:09:17+09:00