我有一个包含一百万行的gzipped数据文件:
$ zcat million_lines.txt.gz | head
1
2
3
4
5
6
7
8
9
10
...
我处理这个文件的 Perl 脚本如下:
# read_million.pl
use strict;
my $file = "million_lines.txt.gz" ;
open MILLION, "gzip -cdfq $file |";
while ( <MILLION> ) {
chomp $_;
if ($_ eq "1000000" ) {
print "This is the millionth line: Perl\n";
last;
}
}
使用Python:
# read_million.py
import gzip
filename = 'million_lines.txt.gz'
fh = gzip.open(filename)
for line in fh:
line = line.strip()
if line == '1000000':
print "This is the millionth line: Python"
break
不管出于什么原因,Python脚本的执行时间几乎是其它语言的8倍:
$ time perl read_million.pl ; time python read_million.py
This is the millionth line: Perl
real 0m0.329s
user 0m0.165s
sys 0m0.019s
This is the millionth line: Python
real 0m2.663s
user 0m2.154s
sys 0m0.074s
我尝试对这两个脚本进行性能分析,但实际上没有太多需要分析的代码。Python脚本大部分时间都花在for line in fh
上; Perl脚本则大部分时间都花在if($_ eq "1000000")
上。
现在,我知道Perl和Python有一些预期的区别。例如,在Perl中,我使用子进程打开文件句柄并调用UNIX gzip
命令;而在Python中,我使用gzip
库。
我该怎么做才能加快Python的实现效率(即使我永远达不到Perl的性能水平)?也许Python中的gzip
模块很慢(或者我使用方法不当);是否有更好的解决方案?
编辑#1
下面是read_million.py
逐行进行性能分析的结果。
Line # Hits Time Per Hit % Time Line Contents
==============================================================
2 @profile
3 def main():
4
5 1 1 1.0 0.0 filename = 'million_lines.txt.gz'
6 1 472 472.0 0.0 fh = gzip.open(filename)
7 1000000 5507042 5.5 84.3 for line in fh:
8 1000000 582653 0.6 8.9 line = line.strip()
9 1000000 443565 0.4 6.8 if line == '1000000':
10 1 25 25.0 0.0 print "This is the millionth line: Python"
11 1 0 0.0 0.0 break
编辑 #2:
我现在也尝试了使用subprocess
Python模块,就像@Kirk Strauser和其他人建议的那样。 它更快:
Python“subproc”解决方案:
# read_million_subproc.py
import subprocess
filename = 'million_lines.txt.gz'
gzip = subprocess.Popen(['gzip', '-cdfq', filename], stdout=subprocess.PIPE)
for line in gzip.stdout:
line = line.strip()
if line == '1000000':
print "This is the millionth line: Python"
break
gzip.wait()
这是我迄今尝试过的所有事情的比较表:
method average_running_time (s)
--------------------------------------------------
read_million.py 2.708
read_million_subproc.py 0.850
read_million.pl 0.393
gzip
模块是用Python编写的,因此性能相当差。OrangeDog建议外部运行gzip
并将解压缩的输出传输到Python可能会加快速度。 - user2357112strip
或切片时会复制,Perl在chomp
时会就地修改),以及改进的名称查找(Perl在编译时链接非面向对象名称;Python在运行时反复查找)。 - ShadowRanger