我有一个python SubProcess调用,它运行一个可执行文件并将输出传递给我的子进程stdout。
在stdout数据相对较小(约2k行)的情况下,逐行读取和读取作为块(stdout.read())之间的性能是可比较的... stdout.read()稍微有点更快。
一旦数据变得更大(比如30k +行),逐行读取的性能就会明显提高。
这是我的比较脚本:
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磁盘存储器)的结果:
line by line = 5.48
slurped = 153.03
我不清楚为什么性能差异如此极端。我的期望是read()版本应该比逐行存储结果更快。当然,在实际情况下我期望更快的逐行结果,其中在读取期间可以进行显着的每行处理。
有人能让我深入了解read()的性能成本吗?
答案 0 :(得分:4)
这不仅仅是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;
}
[readlines.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);
}
char* s = (char*) malloc(120);
s = fgets(s, 120, fh);
while ((s != NULL) && !feof(fh)) {
s = fgets(s, 120, fh);
}
free(s);
return 0;
}
他们的结果(YMMW,largefile.txt是~200MB文本文件):
$ gcc readchars.c -o readchars
$ time ./readchars
./readchars 1.32s user 0.03s system 99% cpu 1.350 total
$ gcc readlines.c -o readlines
$ time ./readlines
./readlines 0.27s user 0.03s system 99% cpu 0.300 total
答案 1 :(得分:1)
尝试在您的Popen调用中添加bufsize选项,看看它是否有所不同:
proc=subprocess.Popen(executable, bufsize=-1, stdout=subprocess.PIPE)
Popen包含一个选项,用于设置读取输入的缓冲区大小。 bufsize默认为0,表示无缓冲输入。任何其他值意味着大约该大小的缓冲区。负值表示使用系统默认值,这意味着完全缓冲的输入。
Python docs包含此注释:
注意:如果遇到性能问题,建议您使用 尝试通过将 bufsize 设置为-1或大型来启用缓冲 足够的正值(如4096)。
答案 2 :(得分: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
也许这取决于子流程?
答案 3 :(得分:0)
我在bufsize上遇到了不稳定的结果,我运行一个记录回复的连续ping脚本,我需要它运行非停止,这将每隔几天挂起,我的解决方案是编写一个单独的脚本来观察tasklist并杀死任何需要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)
这是并行运行的,两个进程同时挂起的可能性很小。您需要做的就是捕获主脚本中丢失管道导致的任何错误。