与CPython相比,PyPy占用大量内存

时间:2017-07-15 11:31:40

标签: python io cpython pypy

我使用python来解决SPOJ的大输入测试problem并遇到了一个非常奇怪的事件。我提交了相同的  使用PyPy和Python 2的代码。结果如下所示:

spoj large input test

与CPython相比,使用PyPy的代码跑得更快,正如预期的那样。但与此同时,内存使用量增加了7倍!我在网上进行了搜索,但我找不到任何证据表明PyPy的内存使用量远远超过CPython。可以解释一下内存使用量的巨大差异吗?

我也认为可能是因为我的代码。因此,我在下面发布了我的代码:

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) 

有人可以告诉我吗?

1 个答案:

答案 0 :(得分:4)

首先,我无法重现结果。不知道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(size),它读取一些数据并将其作为字符串返回。 size是可选的数字参数。当省略大小或为负时,将读取并返回文件的全部内容; 如果文件是机器内存的两倍则会出现问题。

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

实际上,我不确定,整个问题是值得的 - 使用raw_input()可能足够快 - 我会信任python做正确的事情。 CPython通常缓冲stdoutstdin(如果重定向到文件,则完全缓冲,或者为控制台进行行缓冲),并且必须使用命令行选项-u到{{3} }。

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

  

-u强制stdin,stdout和stderr完全无缓冲。上          重要的系统,也把stdin,stdout和stderr放进去          二进制模式。请注意,在xread中有内部缓冲          lines(),readlines()和 file-object iterators(“for line in          sys.stdin“),不受此选项的影响。工作          围绕这个,你会想在里面使用“sys.stdin.readline()”          一个“while 1:”循环。

这意味着您的程序可能如下所示:

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 

有一些要点:

  1. raw_input()sys.stdin.read_line()具有相同的效果
  2. raw_input()是缓冲的,但是这个缓冲区似乎与文件对象迭代器的缓冲区略有不同,它至少对这个文件的性能优于raw_input()
  3. sys.stdin.readlines()的内存开销似乎非常高,至少只要行很短。
  4. 文件对象迭代器在CPython和PyPy中有不同的行为,如果使用选项-u:对于PyPy -u也关闭文件对象迭代器的缓冲(可能是一个bug?)。 / LI>