Lan*_*AOH 7 python io pypy cpython
我用python解决了SPOJ的大输入测试问题,遇到了一个很奇怪的现象。我 使用 PyPy 和 Python 2提交了相同的代码。结果如下所示:
正如预期的那样,与 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)
Run Code Online (Sandbox Code Playgroud)
有人可以建议我吗?
首先,我无法重现结果。不知道 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
Run Code Online (Sandbox Code Playgroud)
现在运行/usr/bin/time -v XXX solution.py < input.py产量:
Interpreter MaximalResidentSize
PyPy: 278 Mb
CPython: 222 Mb
Run Code Online (Sandbox Code Playgroud)
PyPy 需要多一点内存。CPython 和 PyPy 使用不同的垃圾收集器策略,我认为 PyPy 的权衡是更快但使用更多内存。PyPy 的人有一篇很棒的文章,介绍了他们的垃圾收集器及其与 CPython 的比较。
其次,我不相信来自 SPJO 站点的数字。system.stdin.read()将整个文件读入内存。python 文档甚至说:
要读取文件的内容,请调用 f.read(size),它读取一定数量的数据并将其作为字符串返回。size 是一个可选的数字参数。当 size 省略或为负时,将读取并返回文件的全部内容;如果文件是您机器内存的两倍大,那是您的问题。
假设最坏的情况包含在他们的测试用例中,内存使用量应该至少是您使用的文件大小 (110 MB) std.stdin.read(),甚至是文件大小的两倍,因为您正在处理数据。
实际上,我不确定,整个麻烦是否值得 - 使用raw_input()可能足够快 - 我只是相信 python 会做正确的事情。CPython 通常缓冲stdout和stdin(如果它们被重定向到文件,则完全缓冲,或者为控制台行缓冲),您必须使用命令行选项-u将其关闭。
但是,如果您真的想确定,您可以使用 的文件对象迭代器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)
Run Code Online (Sandbox Code Playgroud)
这有一个很大的优势,即此变体仅使用大约 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
Run Code Online (Sandbox Code Playgroud)
有一些要点:
raw_input()并且sys.stdin.read_line()有相同的表现raw_input()是缓冲的,但是这个缓冲区似乎与文件对象迭代器的缓冲区有点不同raw_input(),至少对于这个文件来说,它的表现要好一些。sys.stdin.readlines()似乎相当高,至少只要行很短。-u:对于 PyPy-u也会关闭文件对象迭代器的缓冲(可能是错误?)。