为什么coreutils的排序速度比Python慢​​?


20

我编写了以下脚本来测试Python排序功能的速度:

from sys import stdin, stdout
lines = list(stdin)
lines.sort()
stdout.writelines(lines)

然后,我将其与sort包含1000万行的文件中的coreutils 命令进行了比较:

$ 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

内置命令使用了所有四个CPU(Python仅使用了一个),但是运行时间大约是它的3倍!是什么赋予了?

我正在使用Ubuntu 12.04.5(32位),Python 2.7.3和sort8.13


@goldilocks是的,请查看用户与实时。
augurar 2014年

嗯-你是对的。显然,它已在coreutils 8.6中并行化。
goldilocks 2014年

您可以--buffer-size用来指定sort使用所有可用的物理内存,看看是否有帮助?
iruvar

@ 1_CR使用1 GB缓冲区进行了尝试,时序没有明显变化。它仅使用了约0.6 GB,因此进一步增加缓冲区大小无济于事。
augurar 2014年

Answers:


22

Izkata的评论揭示了答案:特定于语言环境的比较。该sort命令使用环境指示的语言环境,而Python默认使用字节顺序比较。比较UTF-8字符串比比较字节字符串更难。

$ time (LC_ALL=C sort <numbers.txt >s2.txt)
real    0m5.485s
user    0m14.028s
sys     0m0.404s

那个怎么样。


以及如何比较UTF-8字符串?
吉尔(Gilles)'所以

@Gilles修改了locale.strxfrm用于排序的Python脚本,该脚本花费了大约32秒的时间,sort但仍然快了很多,但要快得多。
augurar 2014年

3
Python 2.7.3正在进行字节比较,但是Python3将进行unicode字比较。对于此“测试”,Python3.3的速度大约是慢速Python2.7的两倍。将原始字节打包为Unicode表示形式的开销甚至比Python 2.7.3必须要做的已经非常重要的打包对象还要高。
Anthon 2014年

2
我发现与cut和其他人一样的速度变慢。我现在在几台机器export LC_ALL=C.bashrc。但是要当心:这实际上会中断wc(除外wc -l),仅举一个例子。根本不算“坏字节” ...
Walter Tross 2014年

1
还会出现以下性能问题grep:通过禁用UTF-8来grep大型文件时,您可以获得显着的性能改进grep -i
Adrian Pronk 2014年

7

这比实际答案更像是一种额外的分析,但它似乎根据要排序的数据而有所不同。首先,基础阅读:

$ 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

好的,python 快得多。但是,可以sort通过告诉coreutils 进行数字排序来使其更快:

$ 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

这是快,但仍然蟒通过大幅胜出。现在,让我们再试一次,但要列出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

sort -n对于未排序的数字数据,coreutils 速度更快(尽管您可以调整python sort的cmp参数以使其更快)。sort没有该-n标志,Coreutils 仍然明显较慢。那么,随机字符而不是纯数字呢?

$ 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

同样重要的是要注意,两者不会产生相同的排序输出:

$ 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的答案中提到的算法不同,sort在大多数情况下python 似乎更快,但数值 GNU sort在未排序的数字上胜过python 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 python-fu超过我应尝试测试list.sort()的人,可以通过指定排序方法来实现相同的速度。


5
根据您的上一个示例:ASCII数值顺序,Python排序还有一个额外的速度优势。 sort似乎在进行大写/小写比较时需要做一些额外的工作。
Izkata 2014年

@Izkata就是这样!请参阅下面的答案。
augurar 2014年

1
实际上,python从原始stdin输入中创建其内部字符串有很多开销。这些转换为数字(lines = map(int, list(stdin)))和背面(stdout.writelines(map(str,lines)))使得整个分选走慢,从0.234s实到我的机器上0.720s。
Anthon

6

两种实现都在C中进行,因此这里有一个公平的竞争环境。Coreutils sort 显然使用了mergesort算法。Mergesort执行固定数量的比较,该比较数量成对数地增加到输入大小,即大O(n log n)。

Python的排序使用唯一的混合合并/插入排序timsort,它将与O(n)的最佳情况进行可变数量的比较-大概是在已经排序的列表上-但通常是对数的(从逻辑上讲,在排序时,一般情况下不能比对数更好)。

给定两种不同的对数排序,在某些特定数据集上,一种可能比另一种更具优势。传统的合并排序不会发生变化,因此无论数据如何它都会执行相同的操作,但是例如,发生变化的快速排序(也是对数)将在某些数据上表现更好,而在其他数据上表现较差。

但是,因数为三(或3,因为sort已并行化,所以要大于三),这使我想知道这里是否没有意外情况,例如sort交换到磁盘(该-T选项似乎暗示确实如此)。但是,您的系统时间与用户时间比较短,这不是问题所在。


两种实现都是用C语言编写的。我敢肯定,如果我用Python实现一种排序算法,速度会慢很多。
augurar 2014年

顺便说一下,该文件由0到1之间的随机生成的float值组成,因此不应有太多要利用的结构。
augurar 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.