2017-07-15 102 views
2

我用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) 

有人可以請教我嗎?

回答

2

首先,我無法重現結果。不知道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