PyPy与CPython相比内存使用量大

问题描述:

我用python解决了SPOJ的大输入测试problem,遇到了一个很奇怪的事情。我提交使用PyPy和Python 2.结果如下所示的相同 代码:PyPy与CPython相比内存使用量大

spoj large input test

的代码运行速度快多使用PyPy比CPython的,符合市场预期。但与此同时,内存使用量增加了7倍!我在网上进行了搜索,但是我无法找到任何证据表明PyPy的内存使用量远远超过CPython。可能somone请解释内存使用的巨大差异?

我也认为这可能是因为我的代码。因此,我已经发布了我的代码如下:

import io, sys, atexit, os 
sys.stdout = io.BytesIO() 
atexit.register(lambda: sys.__stdout__.write(sys.stdout.getvalue())) 
sys.stdin = io.BytesIO(sys.stdin.read()) 
raw_input = lambda: sys.stdin.readline().rstrip() 

line = list(map(int,raw_input().split())) 
num, k = line 
ans = 0 

for i in xrange(0,num): 
    if int(raw_input())%k == 0: 
     ans += 1; 

print(ans) 

有人可以请教我吗?

首先,我无法重现结果。不知道SPOJ使用哪些版本/设置。对于下面的实验,使用了PyPy 5.8.0和CPython 2.7.12。

由于测试条​​件下,约110MB规模最大可能的输入文件被用于:

#create_data.py 
print 10**6, 33 
for i in xrange(10**6): 
    print 10**9 

>> python create_data.py > input.in 

现在运行/usr/bin/time -v XXX solution.py < input.py产量:

Interpreter  MaximalResidentSize 
PyPy:     278 Mb 
CPython:    222 Mb 

PyPy需要多一点点记忆。 CPython和PyPy使用不同的垃圾收集器策略,我认为PyPy的权衡要快一些,但要使用更多的内存。 PyPy的家伙有一个great article关于他们的垃圾收集器和它与CPython的比较。


其次,我不相信SPJO网站的数字。 system.stdin.read()会将整个文件读入内存。 Python文档甚至says

要读取文件内容,需要调用f.read(大小),读取若干数量的数据并返回一个字符串。大小是可选的数字参数。当大小被忽略或消极时,文件的全部内容将被读取并返回; 这是您的问题,如果该文件是您的机器内存的两倍大

在此假设下,即最坏的情况是包括到他们的测试情况下,内存使用量应该是文件(110 MB)的大小至少与您使用std.stdin.read()甚至两倍的大小,因为你是处理数据。


其实,我不知道,整个问题是值得的 - 使用raw_input()可能是速度不够快 - 我只相信蟒蛇做正确的事情。 CPython通常缓冲区为stdoutstdin(如果它们被重定向到文件或为控制台进行行缓冲,则完全缓冲),并且必须使用命令行选项-uswitch it off

但如果你真的想成为肯定的是,你可以使用sys.stdin文件对象的迭代器,因为CPython的手册页状态:

-u强制标准输入,输出和错误是完全无缓冲。在 重要的系统上,还将stdin,stdout和stderr放在 二进制模式中。请注意,在xread- lines(),readlines()和文件对象迭代器(“for line in sys.stdin”)中有内部缓冲,它不受此选项的影响。要围绕此工作 ,您需要在 “while 1:”循环内使用“sys.stdin.readline()”。

这意味着你的程序看起来是这样的:

import sys 
num, k = map(int,raw_input().split()) 
ans = 0  
for line in sys.stdin: 
    if int(line)%k == 0: 
     ans += 1 
print(ans) 

这有很大的优势,只有7MB左右的内存用于这个变体。

另一个教训是,如果你害怕,你不应该使用sys.stdin.readline(),有人在非缓冲模式下运行你的程序。


一些进一步的实验(我的CPU主频下)

    CPython  CPython -u   PyPy   PyPy -u 
original  28sec/221MB  25sec/221MB  3sec/278MB 3sec/278MB 
raw_input()  29sec/7MB  110sec/7MB  7sec/75MB 100sec/63MB 
readline()  38sec/7MB  130sec/7MB  5sec/75MB 100sec/63MB 
readlines() 20sec/560MB  20sec/560MB  4sec/1.4GB 4sec/1.4G 
file-iterator 17sec/7MB  17sec/7MB   4sec/68MB 100sec/62MB 

有一些外卖:

  • raw_input()缓冲

    1. raw_input()sys.stdin.read_line()具有相同的性能,但是这缓冲区似乎有点不同于文件对象迭代器w的缓冲区至少对于这个文件来说,它至少胜过raw_input()
    2. sys.stdin.readlines()的内存开销似乎非常高,至少只要行数很短。
    3. 如果使用选项-u,则文件对象迭代器在CPython和PyPy中具有不同的行为:对于PyPy -u也会关闭文件对象迭代器的缓冲(也许是bug?)。
  • +0

    我刚刚测试了您提供的代码。结果表明,在for-in循环中使用时,通过读取sys.stdin确实可以实现缓冲。如果我正确地理解了你,除非我们关闭缓冲区,否则默认情况下,python中的所有I/O都会被缓冲? – LanceHAOH

    +0

    @LanceHAOH我不能说所有的操作系​​统,但它是针对Linux的。然而,输入/输出是否被重定向到文件还是有区别的。 – ead