2011-01-10 15 views
-1

次の2つの実装はどのようにしてPythonでパフォーマンスが異なりますか?python - 2つの実装のパフォーマンスの差

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

2番目の実装は機能しません(sys.stdinは呼び出し可能ではありません)。最初のものと同じことをしません(2番目のものは改行を取り除きます。 –

+2

これらの2つの実装は大きく異なります。 – Falmarri

+0

@Rosh:imapの文字列をintに変換すると、最初の実装で '\ n'が取り除かれます – Akhil

答えて

2

2つの主要な理由:

  • 第1版が同時に並べ替えながら、唯一の内部リストを作成することができますsortedながら第二のコードは、明示的にリストを作成し、その後、それをソートします。
  • 2番目のコードはfor(Python VM)のリストを明示的にループしますが、1番目のバージョンは暗黙のうちに(Cの下位構造を介して)でループします。

とにかく、なぜそこにStringIOがありますか?最も簡単かつおそらく最速の方法は次のとおりです。

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

'' sys.stdout.writelines(x) 'と' print '' .join(x)を比較すると、 '' key = lambda l:int(l.rstrip( '\ n')) '。加えて、入力になかった追加の改行は追加されません。 –

+1

@Rosh Oxymoron: 'int( '3 \ n \ t')'などはうまく動作し、 'int'は空白を取り除きます。あなたは確かに 'writelines'と良い点を持っています。 –

+0

答えのこの部分についてより多くの洞察を与えることができます:•2番目のコードは明示的にリストを構成し、後でソートしますが、1番目のバージョンではソートで同時に内部ソートを作成できます。 2番目の実装で '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が2番目の機能です。

disは、最初の方が高速である理由の1つを示しています。

+0

imapやソートされたメソッドなどのインナーディテールを最初に実装するときに、内部の詳細を教えてくれません。 – Akhil

1

は最初のものに二からステップバイステップの変換を行い、各ステップでどのようにパフォーマンスの変化を参照してください。

  1. line.stripを削除します。これは重要なことであろうと、いくつかのスピードアップを引き起こし、別の問題です。あなたとTHC4kが言及したように、ストリッピングは余計です。
  2. 次に、l.appendを使用してforループをmap(int、sys.stdin)に置き換えます。私の推測では、これは大幅なスピードアップをもたらすだろうということです。
  3. mapl.sortimapsortedに置き換えます。私の推測では、パフォーマンスには影響しませんが、わずかに減速する可能性がありますが、それは重要ではありません。 2つの間では、私は通常、前者と一緒に行くだろうが、Python 3では、おそらく後者が好ましいだろう。
  4. printを使用してforループをprint '\n'.join(...)に置き換えます。私の推測では、これはもう一つのスピードアップだが、それはあなたにいくらかの記憶を必要とするだろう。
  5. cStringIO(これはまったく必要ありません)を追加すると、パフォーマンスにどのように影響するかを確認できます。私の推測では、それはあなたがTHC4kの答えをしようとするものより簡便な一方で、それはおそらく、上記の全てよりも高速になり、少し遅くなりますが、その後4と2

に対抗するのに十分ではないだろうということです4,5のメモリより少ないメモリを使用して読み込み、使用します。これは動作がわずかに異なります(数値の先頭に0が入ることはありません)。

もちろん、誰か推測するのではなく、自分で試してみてください。また、コードにcProfileを実行し、どの部分が最も時間を失っているかを確認します。

関連する問題