4

我最近一直在尝试使用 PyPy,它比我当前的项目快 25 倍,而且运行良好。然而不幸的是,写入文件非常慢。写入文件大约慢 60 倍。

我一直在谷歌搜索,但我没有发现任何有用的东西。这是一个已知的问题?有解决方法吗?

在这样一个简单的测试用例中:

with file(path, 'w') as f:
    f.writelines(['testing to write a file\n' for i in range(5000000)])

与常规 Python 相比,我看到 PyPy 的速度降低了 60 倍。这是使用 64 位 2.7.3 和 PyPy 1.9、32 位和 Python 2.7.2。当然,两者都在相同的操作系统和机器上(Windows 7)。

任何帮助,将不胜感激。PyPy 对于我正在做的事情要快得多,但文件写入速度限制为每秒半兆字节,它的用处显然不那么大。

4

4 回答 4

2

它更慢,但在这个系统上不会慢 60 倍

TLDR;使用write('\n'.join(...))代替writelines(...)

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in range(5000000)])"
10 loops, best of 3: 1.15 sec per loop

$ python -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in range(5000000)])"
10 loops, best of 3: 434 msec per loop

xrange没什么区别

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines(['testing to write a file\n' for i in xrange(5000000)])"
10 loops, best of 3: 1.15 sec per loop

使用生成器表达式对 pypy 来说较慢,但对 python 来说更快

$ pypy -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines('testing to write a file\n' for i in xrange(5000000))"
10 loops, best of 3: 1.62 sec per loop
$ python -m timeit -s "path='tst'" "with file(path, 'w') as f:f.writelines('testing to write a file\n' for i in xrange(5000000))"
10 loops, best of 3: 407 msec per loop

在基准之外创建数据会放大差异(~4.2x)

$ pypy -m timeit -s "path='tst'; data=['testing to write a file\n' for i in range(5000000)]" "with file(path, 'w') as f:f.writelines(data)"
10 loops, best of 3: 786 msec per loop
$ python -m timeit -s "path='tst'; data=['testing to write a file\n' for i in range(5000000)]" "with file(path, 'w') as f:f.writelines(data)"
10 loops, best of 3: 189 msec per loop

使用write()而不是writelines()对两者来说都快得多

$ pypy -m timeit -s "path='tst'; data='\n'.join('testing to write a file\n' for i in range(5000000))" "with file(path, 'w') as f:f.write(data)"
10 loops, best of 3: 51.9 msec per loop
$ python -m timeit -s "path='tst'; data='\n'.join('testing to write a file\n' for i in range(5000000))" "with file(path, 'w') as f:f.write(data)"
10 loops, best of 3: 52.4 msec per loop

$ uname -srvmpio
Linux 3.2.0-26-generic #41-Ubuntu SMP Thu Jun 14 17:49:24 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ python  --version
Python 2.7.3
$ pypy --version
Python 2.7.2 (1.8+dfsg-2, Feb 19 2012, 19:18:08)
[PyPy 1.8.0 with GCC 4.6.2]
于 2012-09-25T13:58:06.017 回答
0

xrange是这个例子的答案,因为它不生成列表,而是生成器。在生成包含 5000 万个项目的列表时,64 位 python 可能比 32 位 pypy 更快。

如果您有其他代码,请发布实际代码,而不仅仅是测试。

于 2012-09-25T13:40:19.627 回答
0

让我们首先直接了解您的基准测试方法。

当目标是测量纯文件写入性能时,在您正在计时的代码段中创建要写入文件的数据是一个重大缺陷,一个系统错误。这是因为数据创建也需要您不想衡量的时间。

因此,如果您打算将整个虚拟数据保存在内存中,请在测量时间之前创建它。

但是,在您的情况下,动态数据生成可能比您的 I/O 更快。因此,通过使用 Python 生成器,在本例中为生成器表达式,结合write调用,您可以摆脱这种系统性错误。

不知道writelines对比表现如何write。但是,根据您的writelines示例:

with file(path, 'w') as f:
    f.writelines('xxxx\n' for _ in xrange(10**6))

写入大块数据write可能会更快:

with file(path, 'w') as f:
    for chunk in ('x'*99999 for _ in xrange(10**3)):
       f.write(chunk)

当您正确进行基准测试时,我很确定您会发现 Python 和 PyPy 之间的差异。在某些情况下,也许 PyPy 甚至更慢。但是,通过适当的基准测试,我相信您将设法找到 PyPy 的文件写入速度足以满足您的目的的条件。

于 2012-09-25T14:05:47.413 回答
-1

您在这里生成两个列表,一个带有range列表理解,一个带有列表理解。

列表 1:一种选择是range用生成器替换返回的列表xrange。另一种方法是尝试 PyPy 自己的优化,称为range-lists

–objspace-std-withrangelist您可以使用该选项启用此功能。

列表 2:您在编写输出列表之前创建它。这也应该是一个生成器,因此将列表推导式转换为生成器表达式:

f.writelines('testing to write a file\n' for i in range(5000000))

只要生成器表达式是传递给函数的唯一参数,甚至不需要在括号上加倍。

于 2012-09-25T14:05:59.663 回答