Python 优化IO性能-读取由空格分隔的5000万个整数
如果使用解释型Python 2.7.6,并尝试从链接到stdin的文件中读取大约5000万个整数(有符号,32位),那么如果这些整数是以单行(结尾没有\n)、空格分隔还是逗号分隔的,那么最快的(性能)方法是什么?最好使用生成器和/或分块读取,以便不将整个文件一次读入内存,或一次存储所有50M整数的列表。列表应缩减为所有相邻元素XOR的总和Python 优化IO性能-读取由空格分隔的5000万个整数,python,performance,csv,io,Python,Performance,Csv,Io,如果使用解释型Python 2.7.6,并尝试从链接到stdin的文件中读取大约5000万个整数(有符号,32位),那么如果这些整数是以单行(结尾没有\n)、空格分隔还是逗号分隔的,那么最快的(性能)方法是什么?最好使用生成器和/或分块读取,以便不将整个文件一次读入内存,或一次存储所有50M整数的列表。列表应缩减为所有相邻元素XOR的总和(A[0]^A[1]+A[1]^A[2]+…),这些数字彼此非常接近,因此缩减不会中断32位带符号整数 可以添加初始行,使其具有整数数(n)和/或行长度(L)
(A[0]^A[1]+A[1]^A[2]+…)
,这些数字彼此非常接近,因此缩减不会中断32位带符号整数
可以添加初始行,使其具有整数数(n)和/或行长度(L) 我不精通python,而且我得到了不可接受的结果(>30秒)。对于十分之一的极限,我只需要6秒,所以我觉得我需要进一步改进 在我看来,如果他们以断线分开,这可能是可能的。有没有办法告诉python对readline()使用不同的分隔符 尝试:
中的ch,循环所有ch需要3秒钟,但是使用乘法生成整数,然后手动进行缩减需要花费太长时间对于stdin.read()
,分块读取,然后使用split和map int存储不完整的尾部以备以后使用,对于xrange和reduce,按顺序在块上构建缩减列表,但似乎再次花费太长时间read(n)
read(1)
我可以看到,如果可以只初始化b一次,然后不使用append,而是实际访问索引,那么它可能(可能)会得到改进,但是当我尝试b=[None]*12
时,我在加入过程中得到了一个RTE不能加入任何人
,需要一个范围内的加入,所以我暂时放弃了这个想法。还有更快的功能来完成我已经完成的工作
更新:
import re
import sys
from collections import deque
def main():
n,l=map(int,raw_input().split())
#print n
#print l
r = 0
p = 0
q = 0
b = sys.stdin.read(l)
b = deque(b.rsplit(' ',4000000))
n = len(b)
while n == 4000001:
c = b.popleft()
b = map(int,b)
for i in xrange(n-2,0,-1):
r += b[i] ^ b[i-1]
m = b[0]
b = deque(c.rsplit(' ',3999999))
b.append(m)
n = len(b)
b = map(int,b)
for i in xrange(n-1,0,-1):
r += b[i] ^ b[i-1]
print r
main()
这是3倍的速度(1000万可以在6秒钟内完成,但50可以超过30),对于5000万,速度仍然太慢,IO似乎不是主要瓶颈,而是数据处理
可以使用常规列表代替deque,调用pop(0)而不是popleft。也可以不在每个循环中调用len(b),因为开始时有n,可以进行减法,但除此之外,这似乎是迄今为止最快的。读取字节流直到EOF。一旦你点击一个空格,将一个“数字”字节列表转换成一个整数,进行异或运算,然后重置列表。或者在列表中添加数字,直到找到空格为止。类似于以下未经测试的代码:
f = open("digits.txt", "rb")
try:
bytes = []
previous_num = None
byte = f.read(1)
while byte != "":
if byte != " ":
bytes.append(byte)
else:
# convert bytes to a number and reset list
current_num = int(''.join(map(str, bytes)))
if not previous_num:
previous_num = current_num
else:
# do your operation on previous and current number
bytes = []
byte = f.read(1)
finally:
f.close()
您可以通过读取字节块(而不是一次读取一个字节)来优化这一点。另一种优化方法可能是为列表保留一种“nul”终止符,一种保持列表“长度”的索引。不是在每个循环中清除它,而是在字节的开始/结束索引子集上执行映射
操作。但希望这能证明这一原则
除此之外,您可能还可以使用Unix实用程序(如sed
)将空格替换为换行符,并将sed
的输出通过管道传输到Python脚本,让Python从stdin
流中读取,同时使用其(可能是优化的)能力一次读取一行
(但实际上,对于任何需要快速I/O的东西,Python可能是错误的答案。)我运行了以下代码:
#!python2.7
from __future__ import print_function
import os, time
numbers = "100 69 38 24 17 11 3 22 "
print("Numbers:", numbers)
if not os.path.isfile('numbers.txt'):
with open('numbers.txt', 'w') as outfile:
n = 7*1000*1000
print("Repeating %d times..." % n)
print(numbers * n, file=outfile)
print("Starting read. Time:", time.strftime("%c"))
total = 0
with open('numbers.txt') as f:
prv = None
for nxt in f.read().split():
nxt = int(nxt)
if prv is not None:
total += prv ^ nxt
prv = nxt
print("Finished. Time:", time.strftime("%c"))
print("Total:", total)
得到了这些结果:
$ python2.7 test.py
Numbers: 100 69 38 24 17 11 3 22
Starting read. Time: Fri Feb 3 19:20:32 2017
Finished. Time: Fri Feb 3 19:21:36 2017
Total: 2603999886
这是5600万(小)个数字,在一台5年历史的MacBookPro电脑上,在64秒左右的时间内——大约每秒100万个数字。您能告诉我们您的时间安排,以及您希望得到什么吗?如果您能找到比我们更快的实现,我会感到惊讶
然而,从文本文件解析int要比仅仅读取二进制数据慢得多。下面是一些快速而肮脏的基准测试,使用两个具有相同~50M整数的文件。第一种是文本格式,另一种是二进制格式(使用numpy.ndarray.tofile
编写)
这个怎么样
from itertools import tee, izip as zip
import re
def pairwise(iterable):
a,b = tee(iterable)
next(b,None)
return zip(a,b)
def process_data(data):
return sum( a^b for a,b in pairwise(data) )
def process_str_file_re(fname):
exp = re.compile(r"\d+")
with open(fname,"rb") as archi:
return process_data( int( data.group() ) for data in exp.finditer(archi.read()) )
不要一次使用一个字符,而是使用一个专门处理字符的模块,如re
这似乎不是一个与[csv]相关的问题,因为您说的数字是空格分隔的。你能给我们看一下你迄今为止试过的代码吗?也许是最快的版本?问题是它可以用逗号分隔(如果有任何东西处理逗号而不是空格,我怀疑),如果文件是二进制格式的,你可以使用array.fromfile
,这应该很快。您是否可以控制文件的写入方式?这意味着每个32位id必须存储一个整数,对吗?不,该文件是csv/空格分隔文本“可以添加初始行,使其具有整数数(n)和/或行长度(L)。”-要使它们成为什么?希望至少得到哦,不。首先要正确,然后要快速。总是,只是个小虫子。。。做得很好,速度很快,但仍然不够快。我把open改为open('rb'),并下降到37秒左右。这篇链接文章中的建议似乎非常有效。值得注意的是,当他转向python 3时,他使用了自己的一种模式,获得了非常好的性能。我尝试了read(1),但得到的结果比在read()中为c做,我不确定我是否理解您的映射块想法。请看这篇文章,了解一些想法:
%timeit numpy.fromfile('numbers.txt', dtype=int, sep=' ')
1 loop, best of 3: 23.6 s per loop
%timeit numpy.fromfile('numbers.bin')
1 loop, best of 3: 2.55 s per loop
from itertools import tee, izip as zip
import re
def pairwise(iterable):
a,b = tee(iterable)
next(b,None)
return zip(a,b)
def process_data(data):
return sum( a^b for a,b in pairwise(data) )
def process_str_file_re(fname):
exp = re.compile(r"\d+")
with open(fname,"rb") as archi:
return process_data( int( data.group() ) for data in exp.finditer(archi.read()) )