【问题标题】:python twice as fast than pypy reading/iterating a filepython 读取/迭代文件的速度是 pypy 的两倍
【发布时间】:2020-08-19 19:33:36
【问题描述】:

通常,对于常规的简单 python 代码,使用 pypy 会更快。但是,当我读取文件并只是拆分字符串并执行非常简单的逻辑时,它怎么会比普通的python慢​​得多。

以下代码供参考。

首先让我们使用下面的代码创建一个假文件:

NUM_ROWS = 10000000
FILENAME = "testing.txt"

def create_file():
    data = []
    for x in range(NUM_ROWS):
        data.append("AA BB CC DD EE FF GG HH II JJ KK LL MM NN OO\n")

    with open(FILENAME, "w") as f:
        for d in data:
            f.write(d)
        f.close()

这只是创建了一个名为 testing.txt 的文件。

然后我们有一个sample.py

import datetime
FILENAME = "testing.txt"

start = datetime.datetime.now()
with open(FILENAME) as f:
    for i, line in enumerate(f):
        data = line.split(" ")
        if data[0] != "AA":
            print(i, line)
print(datetime.datetime.now() - start)

执行C:\pypy3.6-v7.3.1-win32\pypy3.exe sample.py 需要42spython sample.py 只需要18s

我在 Windows 10 机器上使用 python3.7。有没有办法使用pypy来加速像上面这样一个简单的脚本?是不是我用错了?

---------更新:

显然它是“读取”或遍历 pypy 中速度较慢的文件。

以 sample.py 为:

import datetime
FILENAME = "testing.txt"

start = datetime.datetime.now()
with open(FILENAME, "r") as f:
    for line in f:
        pass
print(datetime.datetime.now() - start)

我尝试使用截至 20200818 的最新 pypy3 版本。这是我对上面简单代码的发现(只是逐行迭代具有 1000 万行的文件)。在 Windows 中,常规 python (3.8) 需要 2.3 秒才能执行上述代码,而使用 pypy3 则需要非常慢的 30 秒。在 Ubuntu 中,常规 python 需要 1.2 秒,而使用 pypy3 需要 3.4 秒。 linux版本肯定比较好接受,但是windows肯定需要下功夫

有没有办法使用 pypy for windows 加快读取/迭代文件的速度?

【问题讨论】:

  • 抱歉忘记了'\n',我更新了我的问题。无论哪种方式,pypy 仍然较慢
  • 我建议让测试更具体。您的示例是从文件中读取,拆分一个短字符串并且可能永远不会打印任何内容?也许只是读取文件作为一个例子。另一个只是拆分字符串。 timeit 也很有用。
  • 打印与否并不重要。只要两个测试相同。我的意思是,为什么 pypy 比 python 慢,通常情况相反。将尝试进一步分析
  • 试试if not line.startswith("AA "):?
  • 使测试更加集中的原因是它可以让您了解实际上什么更慢。这也将有助于人们更好地了解 pypy 的工作原理。

标签: python performance pypy


【解决方案1】:

问题已更新,所以我更新了我的答案:

与每次优化一样,您需要分析每个部分。 当然你应该关注循环中的命令。

我通过相同测试的解决方案(没有分析)是:

import datetime
FILENAME = "testing.txt"

start = datetime.datetime.now()
with open(FILENAME) as f:
    i = 0
    data = f.readline()
    while data:
        if not data.startswith('AA '):
            print(i, line)
        i += 1
        data = f.readline()

print(datetime.datetime.now() - start)


然而,这不是@user1179317 所期望的解决方案 在更新的问题中@user1179317 现在意识到文件数据块的读取是问题。

你可以尝试分块读取数据,使用yield:

def read_in_chunks(file_object, chunk_size=1024):
    """generator to read file in chunks"""
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data


with open('big_file.data') as f:
    for piece in read_in_chunks(f):
        process_data(piece)

另一种选择是使用 iter 和辅助函数:

f = open('big_file.dat')
def read_chunk(chunk_size=1024):
    return f.read(chunk_size)

for piece in iter(read_chunk, ''):
    process_data(piece)

再次 - 这不是ready to use past & copyanswer - 您需要进行分析和测试,因为结果将取决于文件大小、可用内存、硬盘块大小、IP 数据包大小等...

由于该操作受 I/O 限制,多线程方法可能会更好 - 您可以尝试在单独的线程中读取文件的下一个块。

所以 - 您需要使用不同的块大小进行分析

【讨论】:

  • 这个解决方案使用pypy还是比较慢
  • 我没有说这样会更快。这只是第一步,尽可能多地从循环中移出。现在您需要分析每个命令。查看可能会产生影响的文件缓冲区等。 with 语句处理打开和关闭文件,包括是否在内部块中引发异常。 f 中的 for 行将文件对象 f 视为一个可迭代对象,它会自动使用缓冲 I/O 和内存管理,因此您不必担心大文件。
  • 这些非常简单的行在 pypy 上慢了 10 倍,除了迭代文件之外什么都不做: with open(FILENAME) as f: for i, line in enumerate(f): pass
  • 知道了,我试试。误会见谅
猜你喜欢
  • 2019-03-03
  • 2021-02-20
  • 1970-01-01
  • 2011-01-18
  • 2018-05-19
  • 2017-03-25
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多