优化IO的性能 - 读取由空格分隔的5000万个整数

时间:2017-02-03 23:08:02

标签: python performance csv io

如果使用解释的Python 2.7.6,并尝试从链接到stdin的文件中读取大约5000万个整数(有符号,32位),如果它们排在一行,那么最快(性能)的方法是什么(最后没有\ n,空间分开?,或者可能以逗号分隔?优选地使用生成器和/或读取块,使得整个文件不被一次读入存储器,或者一次存储的所有50M整数的列表。该列表应减少为所有相邻元素xors (A[0]^A[1] + A[1]^A[2] + ... )的总和,这些数字彼此非常接近,因此减少不会破坏32位有符号整数。

可以添加一个初始行,使其具有整数(n)和/或行长(L)。

我不熟悉python,我得到了不可接受的结果(> 30秒)。对于十分之一的限制,我做了大约6秒,所以我觉得我需要进一步改善这一点。

在我看来,如果他们分开了断线,这可能是可能的。有没有办法让python为readline()使用不同的分隔符?

尝试:

  • for ch in stdin.read(),循环所有ch需要3秒钟,但是使用乘法构建整数然后手动减少需要太长时间。
  • read(n),读取块,然后存储不完整的尾部,以便稍后使用split和map int,对于xrange,并按顺序减少块以构建缩减列表,但似乎也需要花费太长时间。 / LI>

我已经在更快的语言上完成了这一点,感谢,寻找解释的python答案。

这是我最好的代码,在某些情况下在18秒内运行,在其他情况下它太慢了。但它比我在累加器上使用乘法构建整数的版本更快。它也比每字节读取字节快:read(1)

def main():
    n,l=map(int,raw_input().split())
    #print n
    #print l

    r = 0 #result
    p = 0 #previous
    q = 0 #current

    b = [] #buffer
    for c in sys.stdin.read(): #character
        if c == ' ':
            q = int(''.join(b))
            b = []
            r += q ^ p #yes there is a bug with A[0] but lets optimize the loop for now
            p = q
        else:
            b.append(c)
    r += int(''.join(b)) ^ p

    print r
main()

我可以看到它可以(可能)得到改进,如果有可能只初始化b一次然后不使用追加但实际访问索引,但是当我尝试b = [None]*12时我在加入期间得到了一个RTE {{ 1}},需要一个范围内的连接,所以我暂时放弃了这个想法。也可以更快地完成我已经做过的工作。

更新

cant join None
这是快3倍(1000万可以在6秒内完成,但是50接超过30),对于5000万,它仍然太慢,IO似乎不是主要的瓶颈,而是数据处理。 / p>

可以使用常规列表代替deque,调用pop(0)而不是popleft。也可以不在每个循环上调用len(b),因为你在开头有n并且可以相减,但除此之外,这似乎是迄今为止最快的。

4 个答案:

答案 0 :(得分:1)

读取字节流直到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”终结符,这是一个保持列表“长度”的索引。您可以在map的开始/结束索引子集上执行bytes操作,而不是在每个循环上清除它。但希望这证明了这一原则。

如果没有这个,您可以使用像sed这样的Unix实用程序用空行替换空格并将sed的输出传递给Python脚本,并从{{1}读取Python流,使用其(可能是优化的)一次读取一行的能力。

(但是,对于任何需要快速I / O的事情,Python可能是错误的答案。)

答案 1 :(得分:0)

我运行了这段代码:

WM_MOUSEWHEEL

得到了这些结果:

#!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)

在5年历史的MacBook Pro上,这个5600万(小)的数字在64秒左右 - 大约每秒100万个数字。你能告诉我们你的时间,以及你期望得到什么吗?

答案 2 :(得分:0)

如果您能找到比numpy.fromfile

更快的实施,我会感到惊讶

但是,从文本文件中解析int比读取二进制数据要慢得多。这里有一些快速而肮脏的基准测试,使用两个具有相同~50M整数的文件。第一种是文本格式,另一种是二进制(使用numpy.ndarray.tofile编写)

%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

答案 3 :(得分:0)

这个怎么样

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()) )

当时不使用1个字符,而是使用专门处理字符操作的模块,如re