作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个包含一百万行的 gzip 数据文件:
$ 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")
.
gzip
命令;在 Python 中,我使用
gzip
图书馆。
gzip
Python 中的模块很慢(或者我使用它的方式很糟糕);有更好的解决方案吗?
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
根据@Kirk Strauser 和其他人的python 模块。它更快:
# 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
程序正在这样做(它是用 C 编写的,所以它运行得非常快);在该版本的代码中,您将并行计算与串行计算进行比较。 python
进一步减少 Python 启动的开销。与 -E
开关(在启动时禁用对 PYTHON*
环境变量的检查)和 -S
开关(禁用自动 import site
,这避免了大量涉及磁盘 I/O 的动态 sys.path
设置/操作,代价是切断对任何非内置库的访问)。 subprocess
模块比 Perl 的要高一点 open
调用,并在 Python 中实现(在较低级别的原语之上)。广义subprocess
代码需要更长的时间来加载(加剧了启动时间问题)并增加了进程启动本身的开销。 subprocess
默认为无缓冲 I/O,因此除非您传递显式 bufsize
,否则您将执行更多系统调用参数(4096 到 8192 似乎工作正常)line.strip()
调用涉及的开销比您想象的要多; Python 中的函数和方法调用的开销比它们真正应该的要高,而且 line.strip()
不会改变 str
到位方式 Perl 的 chomp
确实(因为 Python 的 str
是不可变的,而 Perl 字符串是可变的)subprocess
:
#!/usr/bin/env python
import subprocess
# Launch with subprocess in list mode (no shell involved) and
# use a meaningful buffer size to minimize system calls
proc = subprocess.Popen(['gzip', '-cdfq', 'million_lines.txt.gz'], stdout=subprocess.PIPE, bufsize=4096)
# Iterate stdout directly
for line in proc.stdout:
if line == '1000000\n': # Avoid stripping
print("This is the millionth line: Python")
break
# Prevent deadlocks by terminating, not waiting, child process
proc.terminate()
gzip
模块与
gzip
程序没有有意义的区别),以牺牲为代价进行了可笑的微优化可读性/可维护性/简洁性/便携性:
#!/usr/bin/env python
import os
rpipe, wpipe = os.pipe()
def reader():
import gzip
FILE = "million_lines.txt.gz"
os.close(rpipe)
with gzip.open(FILE) as inf, os.fdopen(wpipe, 'wb') as outf:
buf = bytearray(16384) # Reusable buffer to minimize allocator overhead
while 1:
cnt = inf.readinto(buf)
if not cnt: break
outf.write(buf[:cnt] if cnt != 16384 else buf)
pid = os.fork()
if not pid:
try:
reader()
finally:
os._exit()
try:
os.close(wpipe)
with os.fdopen(rpipe, 'rb') as f:
for line in f:
if line == b'1000000\n':
print("This is the millionth line: Python")
break
finally:
os.kill(pid, 9)
subprocess
代码需要:
0.173s/0.157s/0.031s wall/user/sys time.
0.147s/0.103s/0.013s
-E -S
通过消除设置导入机器以处理非内置函数的开销,调用又减少了 0.01-0.015 秒的挂钟和用户时间;在其他评论中,您提到您的 Python 需要将近 0.6 秒才能启动,但什么也不做(但其他方面的表现似乎与我的相似),这可能表明您在非默认包或环境方面有更多的了解定制正在进行中,以及
-E -S
可能会为您节省更多。
open
删除字符串解析和存储从
pid
返回的
open
到明确
kill
在退出之前)有最好的时间:
0.183s/0.216s/0.005s
关于Python 与 Perl : performance reading a gzipped file,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36559626/
我是一名优秀的程序员,十分优秀!