首先,我無法重現結果。不知道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通常緩衝區爲stdout
和stdin
(如果它們被重定向到文件或爲控制檯進行行緩衝,則完全緩衝),並且必須使用命令行選項-u
到switch 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()
緩衝
raw_input()
和sys.stdin.read_line()
具有相同的性能,但是這緩衝區似乎有點不同於文件對象迭代器w的緩衝區至少對於這個文件來說,它至少勝過raw_input()
。
sys.stdin.readlines()
的內存開銷似乎非常高,至少只要行數很短。
- 如果使用選項
-u
,則文件對象迭代器在CPython和PyPy中具有不同的行爲:對於PyPy -u
也會關閉文件對象迭代器的緩衝(也許是bug?)。
我剛剛測試了您提供的代碼。結果表明,在for-in循環中使用時,通過讀取sys.stdin確實可以實現緩衝。如果我正確地理解了你,除非我們關閉緩衝區,否則默認情況下,python中的所有I/O都會被緩衝? – LanceHAOH
@LanceHAOH我不能說所有的操作系統,但它是針對Linux的。然而,輸入/輸出是否被重定向到文件還是有區別的。 – ead