Pythonのソート機能の速度をテストするために、次のスクリプトを書きました。
from sys import stdin, stdout
lines = list(stdin)
lines.sort()
stdout.writelines(lines)
次に、これを1000万行を含むファイルのcoreutils sort
コマンドと比較しました。
$ time python sort.py <numbers.txt >s1.txt
real 0m16.707s
user 0m16.288s
sys 0m0.420s
$ time sort <numbers.txt >s2.txt
real 0m45.141s
user 2m28.304s
sys 0m0.380s
組み込みコマンドは4つのCPUすべてを使用しましたが(Pythonは1つしか使用しませんでした)、実行に約3倍の時間がかかりました!何ができますか?
Ubuntu 12.04.5(32ビット)、Python 2.7.3、およびsort
8.13を使用しています。
Izkataのコメント 答えを明らかにしました:ロケール固有の比較。 sort
コマンドは環境によって指定されたロケールを使用しますが、Pythonはデフォルトでバイトオーダー比較になります。UTF-8文字列の比較は、バイト文字列の比較より困難です。
$ time (LC_ALL=C sort <numbers.txt >s2.txt)
real 0m5.485s
user 0m14.028s
sys 0m0.404s
どのようにそのことについて。
どちらの実装もCで行われているため、そこには平等な競争条件があります。 Coreutils sort
どうやら は mergesort アルゴリズムを使用します。 Mergesortは固定数の比較を行い、入力サイズに対数的に増加します。つまり、 big O (n log n)です。
Pythonのソートは、一意のハイブリッドマージ/挿入ソート timsort を使用します。これは、O(n)- 、既に並べ替えられたリストで-しかし、通常は対数です(論理的には、並べ替えるときの一般的なケースでは対数よりも優れていることはありません)。
2つの異なる対数ソートを考えると、特定のデータセットでは、一方が他方よりも有利になる可能性があります。従来のマージソートは変化しないため、データに関係なく同じように実行されますが、たとえば、クイックソート(対数)は変化しますが、一部のデータではパフォーマンスが向上しますが、他のデータではパフォーマンスが低下します。
3の因数(またはsort
は並列化されているため3より大きい)はかなり大きいので、ディスクへのsort
スワッピングなどの偶発性がないのではないかと思います。 (-T
オプションはそれが意味するように思われるでしょう)。ただし、システム時間とユーザー時間の差は、これが問題ではないことを示しています。
これは実際の回答よりも詳細な分析ですが、ソートされるデータによって異なるようです。まず、ベースの読み:
_$ printf "%s\n" {1..1000000} > numbers.txt
$ time python sort.py <numbers.txt >s1.txt
real 0m0.521s
user 0m0.216s
sys 0m0.100s
$ time sort <numbers.txt >s2.txt
real 0m3.708s
user 0m4.908s
sys 0m0.156s
_
OK、python ismuchfast。ただし、coreutils sort
を高速化することができます数値でソートするには:
_$ time sort <numbers.txt >s2.txt
real 0m3.743s
user 0m4.964s
sys 0m0.148s
$ time sort -n <numbers.txt >s2.txt
real 0m0.733s
user 0m0.836s
sys 0m0.100s
_
それはずっと速いですが、pythonはまだ大きなマージンで勝ちます。今、再試行しますが、ソートされていません100万件のリスト:
_$ sort -R numbers.txt > randomized.txt
$ time sort -n <randomized.txt >s2.txt
real 0m1.493s
user 0m1.920s
sys 0m0.116s
$ time python sort.py <randomized.txt >s1.txt
real 0m2.652s
user 0m1.988s
sys 0m0.064s
_
Coreutils _sort -n
_は、ソートされていない数値データの方が高速です(ただし、python sortのcmp
パラメータを微調整して高速化できる場合があります)。Coreutilssort
は、_-n
_フラグを使用しない場合でも大幅に遅くなります。では、純粋な数値ではなく、ランダムな文字についてはどうでしょうか。
_$ tr -dc 'A-Za-z0-9' </dev/urandom | head -c1000000 |
sed 's/./&\n/g' > random.txt
$ time sort <random.txt >s2.txt
real 0m2.487s
user 0m3.480s
sys 0m0.128s
$ time python sort.py <random.txt >s2.txt
real 0m1.314s
user 0m0.744s
sys 0m0.068s
_
Pythonはまだcoreutilsに勝っていますが、質問で示したものよりもはるかに小さいマージンです。驚くべきことに、純粋なアルファベット順のデータを見ると、さらに高速です。
_$ tr -dc 'A-Za-z' </dev/urandom | head -c1000000 |
sed 's/./&\n/g' > letters.txt
$ time sort <letters.txt >s2.txt
real 0m2.561s
user 0m3.684s
sys 0m0.100s
$ time python sort.py <letters.txt >s1.txt
real 0m1.297s
user 0m0.744s
sys 0m0.064s
_
2つは同じソートされた出力を生成しないことに注意することも重要です。
_$ echo -e "A\nB\na\nb\n-" | sort -n
-
a
A
b
B
$ echo -e "A\nB\na\nb\n-" | python sort.py
-
A
B
a
b
_
奇妙なことに、_--buffer-size
_オプションは、私のテストで大きな(または少しの)違いをもたらすようには見えませんでした。結論として、おそらくgoldilockの回答で言及されているさまざまなアルゴリズムのため、python sort
はほとんどの場合高速であるように見えますが、numericalGNU sort
は、並べ替えられていない数値で勝ります1。
OPはおそらく 根本原因を発見 ですが、完全を期すために、最後の比較を示します。
_$ time LC_ALL=C sort <letters.txt >s2.txt
real 0m0.280s
user 0m0.512s
sys 0m0.084s
$ time LC_ALL=C python sort.py <letters.txt >s2.txt
real 0m0.493s
user 0m0.448s
sys 0m0.044s
_
1並べ替え方法を指定することで、同じ速度を確認するためにlist.sort()
を調整してテストする必要があるよりもpython-fuを使用している人を実現できます。