LoginSignup
5
6

More than 5 years have passed since last update.

高速シェルスクリプト(sortコマンド編)

Last updated at Posted at 2017-11-15

前書き

Qiita初投稿です。よろしくお願いします。

シェルスクリプトを使って面倒な仕事をささっと片付けよう!

シェルスクリプトは主にちょっとした仕事を短時間で実装し、結果を得るのに向いています。
今回はよく行う「ユニーク数を数える処理」を早く実装し、早く結果を出そうと思います。

使用したマシンのスペック等

SSD 512GB
CentOS7
シェル: bash
CPU: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz

サンプルデータ

テキストファイルからMeCab+NEologdで抽出した地名。(MeCabが地名と判断したもの)
4億行のtextファイル!

$ wc -l extracted_geo.txt # 行数 (= 抽出された地名の数)
488139214 extracted_geo.txt
$ head -n 30 extracted_geo.txt # ファイルの中身(先頭30行)
カレー
中東
岩井
日光
横浜
ベネチア
静岡
大学前
玉取
ケンタッキー
沖縄
函館
広橋
三橋
一日
東京
大阪
バッキンガム
三上
石川
8号
秋葉原
新台
サンセイ
東京
ムリ
福岡
関東
和歌山
関西
$ du -m extracted_geo.txt # データの大きさ(MB)
4037    extracted_geo.txt

余談ですが、MeCab( http://taku910.github.io/mecab/ )とは日本語の形態素解析器であり、日本語の文章を与えると、品詞を解析してくれたり、分かち書きをしてくれたりする優れものです。
NEologdは、多数のWeb上の言語資源から得た新語を追加することでカスタマイズした MeCab 用のシステム辞書です。詳しくは下記URLを参照してください。
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md

地名が何種類含まれているか知りたい

サンプルファイルには地名が重複して入っています。
このファイル中に何種類の地名(ユニーク数)が含まれているか、調べてみましょう。

何も考えずにスクリプトを組むと、

$ time cat extracted_geo.txt | sort | uniq  | wc -l
^C

real    108m14.580s
user    108m5.770s
sys 0m1.845s

のようになりますが、これは100分経っても終わりませんでした。ちーん

高速化

2バイト文字に対応したために、sortコマンドが遅くなってしまっているらしいので、英語環境で実行しました。

コードの意味を日本語で表すなら、「(英語環境で)extracted_geo.txtの中身を標準出力し、行方向にソートして、同じものが続くなら一つにしたあと、行数を数える」という具合でしょうか。
単純に英語環境にしてみた結果、爆速になりました!

$ time cat extracted_geo.txt | LANG=C sort | uniq | wc -l
554332

real    3m36.962s
user    3m47.305s
sys 0m10.531s

引数にファイル名を与えるとさらに高速化!
user timeが増えていることから、並列化が有効になったと推測されます。

$ time LANG=C sort extracted_geo.txt | uniq | wc -l
554332

real    3m5.450s
user    8m43.576s
sys 0m6.934s

上記のコマンドはstable sortが無効になっていたので有効にしてみました。

最終形態!

$ time LANG=C sort -s extracted_geo.txt | uniq | wc -l
554332

real    2m59.063s
user    8m35.422s
sys 0m6.600s

ちょっと高速化!

標準入力で、データを与えるのではなく、引数にファイル名を与えてsortするときに、top -H で実行の様子を見たら、並列化されていることがわかりました。

のべ4.8億件の地名があったのに、55万程度しか種類がありませんでした。
sortは一般的に計算量がO(nlogn)だが、約4.8億行のソートに3分程度しかかかりませんでした。

結論

sortコマンドを使う時は、特に必要がなければLANG=Cを先頭につけましょう!

余談等

今回の処理ではわざわざsortをする必要はないですが、実装が楽(数秒で済む)なので上記のようなスクリプトを書きました。データの統計を短い実装時間で求めたいときにはシェルスクリプトをつかうことを考えてもいいとおもいます。
データ量がさらに多い場合や、何度も繰り返しする処理であるならば、シェルスクリプトを書かずに高速な言語でプログラミングをした方がいいと思います。
シェルスクリプト(シェル芸)に限った話ではありませんが、使い所を考えることは大事ですね。
間違い等あればご指摘よろしくお願いします。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6