代码之家  ›  专栏  ›  技术社区  ›  Donald

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

  •  7
  • Donald  · 技术社区  · 8 年前

    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 回复  |  直到 8 年前
        1
  •  7
  •   ead    8 年前

    首先,我无法重现结果。不知道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
    

    great article 关于他们的垃圾收集器及其与CPython的比较。


    其次,我不相信SPJO网站上的数字。 system.stdin.read() says

    要读取文件内容,请调用f.read(size),它读取一定数量的数据并将其作为字符串返回。大小是可选的数字参数。当忽略大小或大小为负数时,将读取并返回文件的全部内容; 如果文件的大小是机器内存的两倍,这就是你的问题

    在假设最坏的情况包括在他们的测试用例中的情况下,内存使用量至少应该是您使用的文件大小(110 MB) std.stdin.read()


    实际上,我不确定,所有的麻烦都值得使用 raw_input() 可能已经足够快了——我相信python会做正确的事情。CPython正常缓冲 stdout stdin (如果重定向到文件,则完全缓冲,或者控制台的行缓冲)并且必须使用命令行选项 -u switch it off .

    但是如果你真的想确定,你可以使用 sys.stdin

    -u强制stdin、stdout和stderr完全无缓冲。在…上 在重要的系统中,也将stdin、stdout和stderr放入 二进制模式。请注意 有内部缓冲 在xread中 文件对象迭代器(“for line-in 系统标准“

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

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


                       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. 原始输入() sys.stdin.read_line() 具有相同的性能
    2. 原始输入() 是缓冲的,但该缓冲区似乎与文件对象迭代器的缓冲区略有不同,后者的性能优于 至少对于此文件。
    3. 的内存开销 sys.stdin.readlines() 似乎很高,至少只要队伍很短。
    4. 文件对象迭代器在CPython和PyPy中有不同的行为,如果选项为 -u 用于:PyPy 同时关闭文件对象迭代器的缓冲(可能是错误?)。