2011-01-10 35 views
-1

以下兩種實現在Python中有不同的性能如何?python - 兩種實現之間的性能差異

from cStringIO import StringIO 
from itertools import imap 
from sys import stdin 
input = imap(int, StringIO(stdin.read())) 
print '\n'.join(imap(str, sorted(input))) 

AND

import sys 
for line in sys.stdin: 
    l.append(int(line.strip('\n'))) 

l.sort() 
for x in l: 
    print x 

第一實施比第二對的10^6行的順序輸入快。爲什麼這樣?

+2

你的第二個實現不起作用(sys.stdin不可調用),它不會執行與第一個相同的操作(第二個去掉換行符,第一個去掉換行符)。 –

+2

這兩個實現大不相同。需要做很多工作才能在最低級別比較它們 – Falmarri

+0

@Rosh:在imap中將字符串轉換爲int將在第一個實現中剝離'\ n' – Akhil

回答

2

主要有兩個原因:

  • 第二代碼明確構建一個列表,之後它排序,而第一個版本讓sorted創建只是一個內部列表,而在同一時間排序。
  • 第二個代碼顯式循環使用for(在Python VM上)的列表,而第一個代碼隱式循環使用imap(通過C中的下層結構)。

不管怎樣,爲什麼StringIO在那裏?最直接的,可能最快的方法是:

from sys import stdin, stdout 
stdout.writelines(sorted(stdin, key=int)) 
+1

它必須是'key = lambda l:int(l.rstrip('\ n'))'同時如果有'sys.stdout.writelines(x)'與'print''相比,它會減少內存消耗.join(x) '。另外它不會添加不在輸入中的附加新行。 –

+1

@Rosh Oxymoron:'int('3 \ n \ t')'等工作得很好,'int'去除空白。你確實有'writelines'的好處。 –

+0

您可以更深入地瞭解這部分答案:•第二個代碼顯式構造列表並對其進行排序,而第1個代碼允許排序時只創建一個內部列表,同時進行排序。在第二個實現中,'不'輸入'類似於'l'的列表 – Akhil

3
>>> dis.dis(first) 
    2   0 LOAD_GLOBAL    0 (imap) 
       3 LOAD_GLOBAL    1 (int) 
       6 LOAD_GLOBAL    2 (StringIO) 
       9 LOAD_GLOBAL    3 (stdin) 
      12 LOAD_ATTR    4 (read) 
      15 CALL_FUNCTION   0 
      18 CALL_FUNCTION   1 
      21 CALL_FUNCTION   2 
      24 STORE_FAST    0 (input) 
      27 LOAD_CONST    0 (None) 
      30 RETURN_VALUE   
>>> dis.dis(second) 
    2   0 SETUP_LOOP    48 (to 51) 
       3 LOAD_GLOBAL    0 (sys) 
       6 LOAD_ATTR    1 (stdin) 
       9 CALL_FUNCTION   0 
      12 GET_ITER    
     >> 13 FOR_ITER    34 (to 50) 
      16 STORE_FAST    0 (line) 

    3   19 LOAD_GLOBAL    2 (l) 
      22 LOAD_ATTR    3 (append) 
      25 LOAD_GLOBAL    4 (int) 
      28 LOAD_FAST    0 (line) 
      31 LOAD_ATTR    5 (strip) 
      34 LOAD_CONST    1 ('\n') 
      37 CALL_FUNCTION   1 
      40 CALL_FUNCTION   1 
      43 CALL_FUNCTION   1 
      46 POP_TOP    
      47 JUMP_ABSOLUTE   13 
     >> 50 POP_BLOCK   

    4  >> 51 LOAD_GLOBAL    2 (l) 
      54 LOAD_ATTR    6 (sort) 
      57 CALL_FUNCTION   0 
      60 POP_TOP    
      61 LOAD_CONST    0 (None) 
      64 RETURN_VALUE   

first是您的第一個功能。 second是你的第二個功能。

dis說明了第一個更快的原因之一。

+0

那麼它不會告訴像imap,sorted等方法在第一次執行時會進行調用的內部細節 – Akhil

1

從做第二一步一步轉換到第一個,看看如何與每一步的性能變化。

  1. 刪除line.strip。這會導致一些加速,不管它是否是重要的是另一回事。正如你和THC4k所提到的,剝離是多餘的。
  2. 然後用map(int,sys.stdin)替換使用l.append的for循環。我的猜測是,這會帶來顯着的加速。
  3. 取代mapl.sortimapsorted。我的猜測是,它不會影響業績,可能會有小幅放緩,但這不會有太大意義。在這兩者之間,我通常會和前者一起工作,但在Python 3中,後者可能更可取。
  4. print替換for循環與print '\n'.join(...)。我的猜測是,這將是另一個加速,但它會花費你一些記憶。
  5. 添加cStringIO(這是完全不必要的),看看它是如何影響性能的。我的猜測是,它會稍微慢一點,但不足以反擊4和2.

然後,如果您嘗試THC4k的答案,它可能會比以上所有更快,而更簡單,更容易閱讀和使用少於4和5的內存。它具有稍微不同的行爲(它不會去除數字中的前導零)。

當然,你自己試試看,而不是相信任何人的猜測。在您的代碼上運行cProfile並查看哪些部分最耗時。

相關問題