ARAIBOムービーまとめengSearch上田ブログなお、このサイトはbashでできている。今回からしばらく、コマンドに手分けさせて仕事をさせる方法を扱います。これを休ませずに働かせます。今回扱うのは、最重要並列化テクニック:「普通にパイプを使う」です。先に答えを言ってしまうと、パイプにつなげたコマンドは並列に動作します。それを使おうというのが今回のお題です。パイプやファイルからの入力がないと待ちの状態になります。互いに影響を受けながら待ちの状態とアウトプットしている状態を繰り返します。パイプにつながったコマンドは同時に動いているように見えます。ここでCPUが2個あったらどうなるか考えてみます。単純に考えてもう1個のCPUで半分受け持てばよいということになります。端末を操作している側の人間は気にしなくてよいほど効率化されています。パイプは基礎¶ここ数年はCPUのマルチコア化によって様々な並列処理技術が開発されています。それに対してパイプは有難味を感じないほど取扱いが簡単です。それは、OSから相対的に見たときに応用技術なのか基礎技術なのかという違いです。応用技術はOS側が要件を満たしていないと動きません。内部はパイプを使ったり、プロセスを操作したりとOSの機能を使っているわけです。様々な違いを吸収しなければなりません。そのため、OSの違いを吸収するライブラリなどが増えてしまいがちです。セットアップから使用までにどうしても手間がかかります。OSの各機能は、とにかく単機能でシンプルに使えるように実装されます。ユーザがよく使うものはシェルから記号一つで呼び出せます。簡単ぶりが徹底されます。開発や改良にかなり力が入る部分でもあります。OSのメジャーバージョンアップの際にパフォーマンスが向上することがあります。お膳立てされているので積極的に使うべきだと思います。これを知らずに他の応用技術をあれこれ議論するのはよろしくありません。「パイプの発明者」は、ダグラス・マキルロイという人です。おもしろい記述があります。黎明期のUNIXに強烈なインパクトを与えたようです。前置きが長くなってしまいました。昨年5万円で買ったノートパソコンで計測します。リスト1にスペック等を示します。(脚注:あまり感心なことではありませんが、カジュアルさを演出ということで・・・)。実験の基礎中の基礎も割愛します。先生(脚注:指導教官を指す。実験に使うデータは、これまでの連載でさんざん使ってきたapacheのアクセスログです。日ごとにgzip圧縮されているログをSL510にコピーして処理の対象とします。実験用に、同じログを何回もくっつけて1GBのファイルを作ります。処理時間の測り方¶コマンドの処理時間は、timeコマンドで測ります。ファイルを全部解凍してファイルに出力する時間を測ってみましょう。カーネルモードだった時間です。CPU時間よりもrealの時間が長くなります。いうところに出力をリダイレクトします。(ビットバケツ)はなんでも吸い込んで消してしまう特殊なファイルです。この時間は計測されます。「デブヌル」と呼んでいます。以下のようにコマンドをセミコロンで区切って括弧で囲む方法があります。ように呼び出してもよいでしょう。パイプラインの並列処理¶では、まずパイプを使うと何が起こるか見てみましょう。aをすべてbに置換しています。bをaに置換しています。下の処理の方が2.5[s]遅いですが、注目すべきはuserの値がrealの値を上回っていることです。並列化されているということになります。リスト3は、処理中にtopコマンドを打ってみたものです。sedのCPU使用率が92%と82%で、足すとsedだけで1.7個CPUを使用していることになります。compizちなみに、この処理をパイプを使わずに行うとこうなります。0m4.480s当然、途中でファイルに書き出す手間がかかるので、時間を食うことになります。中間ファイルは消さないといけません。書き方もゴテゴテしています。どこまでパイプでつないでよいのかという疑問が生じます。普通、この手のテクニックは度が過ぎるとパフォーマンスが落ちるものです。sedで1GBのファイルのアルファベットを一文字ずつ変換してみます。変換するsedコマンドをパイプで数珠つなぎにして処理時間を計測します。sedの数は1~13まで変化させます。処理の総量はsedの数に比例して大きくなります。CPUを8コア持っているサーバ機でもやってみました。高い並列化の効果を観測できるかもしれません。図1に、計時結果をグラフを示します。(脚注:これもシェルスクリプトで描いています。4月号・5月号参考のこと。細かくデザインしやすいからで、別に意固地になっているわけではありません。処理量に比例して時間が増えているように見えます。計測時間をsedの数で割ってみたものを図2に示します。この図は、同じ処理量で計算能力を比較したものになります。(ただし、ファイルを読み出す時間を考慮するとsedの数が多い方が若干有利な値になります。)↓図2:sed一個あたりの処理時間こちらの図は並列化の効果がよくわかります。時間あたりにたくさんの処理をこなしています。8CPUのサーバの場合は、7個あたりまで時間が短くなっています。この範囲のsedの数では性能が落ちないことも分かります。思いつく限りずらずらコマンドをパイプで並べていってもパイプが詰まる心配は小さいと言えます。ないときはどうなるでしょうか?例えば、同じ置換のコマンドであっても、trコマンドはsedに比べてかなり高速です。リスト4の例のようにtrの方が遊んでしまいます。このときは後のコマンドで扱うデータ量が少ないので、これも負荷に違いが出ます。負荷もそれくらいの違いが出ます。・・・これら場合は、一番負荷の高いコマンドの処理時間以上に処理が早く終わることはありません。早い処理が必要ならば、別の方法をとる必要があります。負荷の高いコマンドを待っている間にCPUを効率的に使うことは可能です。他のコマンドの負荷が軽ければそちらで勝手に処理が分散されます。/dev/null#topの抜粋。言ったように、コマンドを二つつなげたらそれぞれがCPUを一個ずつ占領するわけではありません。CPUが遊ばないように負荷分散されます。頭の隅に置いておくとよいでしょう。処理を分割すると早くなるという例を下に示します。このように処理を分割した方が処理が早く終わる場合が多いです。シェルスクリプトの場合は分けて書いた方が読みやすくなります。これの処理については、先ほどパイプをたくさんつなげる実験をした8CPUのサーバでもやってみました。コマンドをつなげることに抵抗がなければ、CPUが多い環境だとこれだけ速くなるという例です。いい加減なコードですが・・・。ボトルネックになります。次の場合、sortが終わるまで、awk以後の処理は開始しません。早く処理を終わらせるテクニックはあります。上の例の場合は、単純にgrepとawkの順序を変えるだけでかなり処理時間が違ってきます。後者の並び次の二つのグループが同時に実行されることが分かっていることも大事です。並列化の効果はsortが間にあってもちゃんと出ています。今回は、パイプにコマンドをつないで並列化するというお題を扱いました。データを処理するシェルスクリプトを書く際には必須の知識です。改善するということはお見せできたと思います。CPUが多いと、実験で出てきたサーバ機のように効果は明確になります。パイプはUNIXの特性に大きな影響を与えています。パイプの発明が我々に大きな恩恵を与えていることは疑いのないことでしょう。これがインターネットに間接的に与えた影響についても考えてみると良いかもしれません。次回はパイプを使わないで並列化する方法について扱ってみたいと思います。AdSenseやその他ソーシャルボタンのためにCookieを使用しています。同意いただけない場合はブラウザでクッキーを無効にして閲覧をお願いします。