python read()从标准输出比逐行读取慢得多(slurping?)
我有一个python子进程调用,它运行一个可执行文件并将输出通过管道传输到我的子进程stdout 在stdout数据相对较小(约2k行)的情况下,逐行读取和作为块读取(stdout.read())之间的性能相当……stdout.read()稍快一些 一旦数据变得更大(比如30k+行),逐行读取的性能就会显著提高 这是我的比较脚本:python read()从标准输出比逐行读取慢得多(slurping?),python,performance,subprocess,readline,Python,Performance,Subprocess,Readline,我有一个python子进程调用,它运行一个可执行文件并将输出通过管道传输到我的子进程stdout 在stdout数据相对较小(约2k行)的情况下,逐行读取和作为块读取(stdout.read())之间的性能相当……stdout.read()稍快一些 一旦数据变得更大(比如30k+行),逐行读取的性能就会显著提高 这是我的比较脚本: proc=subprocess.Popen(executable,stdout=subprocess.PIPE) tic=time.clock() for line
proc=subprocess.Popen(executable,stdout=subprocess.PIPE)
tic=time.clock()
for line in (iter(proc.stdout.readline,b'')):
tmp.append(line)
print("line by line = %.2f"%(time.clock()-tic))
proc=subprocess.Popen(executable,stdout=subprocess.PIPE)
tic=time.clock()
fullFile=proc.stdout.read()
print("slurped = %.2f"%(time.clock()-tic))
这些是读取约96k行(或50mb磁盘内存)的结果:
我不清楚为什么性能差异如此之大。我的期望是read()版本应该比逐行存储结果更快。当然,我希望在实际情况下,在读取过程中可以进行显著的每行处理,从而获得更快的逐行结果
有人能告诉我read()性能成本吗?尝试在Popen调用中添加一个bufsize选项,看看它是否有区别:
proc=subprocess.Popen(executable, bufsize=-1, stdout=subprocess.PIPE)
Popen包含一个选项,用于设置读取输入的缓冲区大小。bufsize默认为0,这意味着无缓冲输入。任何其他值表示大约该大小的缓冲区。负值意味着使用系统默认值,这意味着完全缓冲输入
本说明包括:
注意:如果您遇到性能问题,建议您
尝试通过将bufsize设置为-1或大值来启用缓冲
足够的正值(如4096)
这不仅仅是Python,没有缓冲的按字符读取总是比按行或大块读取慢 考虑以下两个简单的C程序: [readchars.c]
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void) {
FILE* fh = fopen("largefile.txt", "r");
if (fh == NULL) {
perror("Failed to open file largefile.txt");
exit(1);
}
int c;
c = fgetc(fh);
while (c != EOF) {
c = fgetc(fh);
}
return 0;
}
我根本不懂那种行为
import subprocess
import time
executable = ["cat", "data"]
proc=subprocess.Popen(executable,stdout=subprocess.PIPE)
tic=time.clock()
tmp = []
for line in (iter(proc.stdout.readline,b'')):
tmp.append(line)
print("line by line = %.2f"%(time.clock()-tic))
proc=subprocess.Popen(executable,stdout=subprocess.PIPE)
tic=time.clock()
fullFile=proc.stdout.read()
print("slurped = %.2f"%(time.clock()-tic))
数据是文本
pts/0$ ll data
-rw-r--r-- 1 javier users 18M feb 21 20:53 data
pts/0$ wc -l data
169866 data
结果:
pts/0$ python3 a.py
line by line = 0.08
slurped = 0.01
Python 2比Python 3慢得多
pts/0$ python2 a.py
line by line = 4.45
slurped = 0.01
也许这取决于子进程?我在bufsize上的结果参差不齐,我运行了一个记录回复的连续ping脚本,我需要它不间断地运行,这将每隔几天挂起一次,我的解决方案是编写一个单独的脚本来监视任务列表,并杀死任何需要10秒以上时间的ping任务。见下文
import subprocess
import time
CREATE_NO_WINDOW = 0x08000000
previous_id = ''
while 0!=1:
command = subprocess.Popen(['tasklist'], stdout=subprocess.PIPE,
shell=False, creationflags = CREATE_NO_WINDOW)
reply = str(command.communicate()[0]).split('Ko')
for item in reply:
if 'PING.EXE' in item:
print(item.split(' ')[0][4:]+' '+item.split(' ')[22])
if item.split(' ')[22] != previous_id:
previous_id = item.split(' ')[22]
print('New ping detected, system is healthy')
else:
print('Same ping active for 10 seconds, killing')
command = subprocess.Popen(['taskkill','/f','/im','PING.EXE'], stdout=subprocess.PIPE, shell=False, creationflags = CREATE_NO_WINDOW)
err_log=open('errors.txt','w')
time.sleep(10)
这是并行运行的,两个进程同时挂起的可能性很小。您所需要做的就是捕获由于主脚本中管道丢失而导致的任何错误。子流程执行时是否总是花费相同的时间?(例如,缓存对重复运行没有影响等)在重复运行中没有观察到显著的收益。无法使用
seq 30000
()进行复制。我想我们需要一个SSCCE()。我怀疑内存压力。你比较过这两种情况下的内存使用模式吗?@NPE:这是一个windows系统……如果python执行与linux python有什么不同的话。另外,我不确定seq 30000是否会强制我看到的行为,因为我认为这是一个数据量问题,而不是行数问题。我的每一行都有大约400个字符。带缓冲区的结果:逐行=2.41 slurped no buffer=30.90 slurped w/buffer=31.78FWIW这为我的特定用例带来了8倍的加速,谢谢!确实,很抱歉。bufsize
在Python2和Python3中有不同的默认值。如果你明确地设置它;在这两个版本中,您应该会得到类似的结果。使用timeit.default\u timer
而不是time.clock()
以实现便携性。很高兴知道。谢谢
pts/0$ python3 a.py
line by line = 0.08
slurped = 0.01
pts/0$ python2 a.py
line by line = 4.45
slurped = 0.01
import subprocess
import time
CREATE_NO_WINDOW = 0x08000000
previous_id = ''
while 0!=1:
command = subprocess.Popen(['tasklist'], stdout=subprocess.PIPE,
shell=False, creationflags = CREATE_NO_WINDOW)
reply = str(command.communicate()[0]).split('Ko')
for item in reply:
if 'PING.EXE' in item:
print(item.split(' ')[0][4:]+' '+item.split(' ')[22])
if item.split(' ')[22] != previous_id:
previous_id = item.split(' ')[22]
print('New ping detected, system is healthy')
else:
print('Same ping active for 10 seconds, killing')
command = subprocess.Popen(['taskkill','/f','/im','PING.EXE'], stdout=subprocess.PIPE, shell=False, creationflags = CREATE_NO_WINDOW)
err_log=open('errors.txt','w')
time.sleep(10)