7

パターンマッチングを行うクラスを実験してきました。私のクラスは次のようになります。

class Matcher(object):
  def __init__(self, pattern):
    self._re = re.compile(pattern)

  def match(self, value):
    return self._re.match(value)

全体として、私のスクリプトの実行には約 45 秒かかります。実験として、コードを次のように変更しました。

class Matcher(object):
  def __init__(self, pattern):
    self._re = re.compile(pattern)
    self.match = self._re.match

このスクリプトの実行には 37 秒かかりました。このプロセスを何度繰り返しても、同じようにパフォーマンスが大幅に向上します。cProfile を介して実行すると、次のように表示されます。

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
 46100979   14.356    0.000   14.356    0.000 {method 'match' of '_sre.SRE_Pattern' objects}
 44839409    9.287    0.000   20.031    0.000 matcher.py:266(match)

なぜマッチメソッドは実行時間に 9.2 秒を追加するのでしょうか? 最も苛立たしいのは、単純なケースを再現しようとして、再現できなかったことです。ここで何が欠けていますか?私の簡単なテスト ケースにバグがありました。今、私が見ている動作を模倣しています:

import re
import sys
import time

class X(object):
  def __init__(self):
    self._re = re.compile('.*a')

  def match(self, value):
    return self._re.match(value)

class Y(object):
  def __init__(self):
    self._re = re.compile('ba')
    self.match = self._re.match

inp = 'ba'
x = X()
y = Y()

sys.stdout.write("Testing with a method...")
sys.stdout.flush()
start = time.time()
for i in range(100000000):
  m = x.match(inp)
end = time.time()
sys.stdout.write("Done: "+str(end-start)+"\n")

sys.stdout.write("Testing with an attribute...")
sys.stdout.flush()
start = time.time()
for i in range(100000000):
  m = y.match(inp)
end = time.time()
sys.stdout.write("Done: "+str(end-start)+"\n")

出力:

$ python speedtest.py 
Testing with a method...Done: 50.6646981239
Testing with an attribute...Done: 35.5526258945

参考までに、どちらもpyp を使用するとはるかに高速ですが、メソッドの代わりに属性を使用して実行すると、大幅な改善が見られます。

$ pypy speedtest.py 
Testing with a method...Done: 6.15996003151
Testing with an attribute...Done: 3.57215714455
4

2 に答える 2

8

おそらく、追加の関数呼び出しのオーバーヘッドがほとんどです。Python 関数の呼び出しは、追加のスタック フレームを設定する必要があるなどの理由で、比較的コストのかかるパフォーマンスです。同様のパフォーマンスを示す最小限の例を次に示します。

>>> timeit.timeit("f()", "g = (lambda: 1); f = lambda: g()")
0.2858083918486847
>>> timeit.timeit("f()", "f = lambda: 1")
0.13749289364989004

メソッド内で 2 つの追加の属性ルックアップを行う追加コストもあります。つまり、 をルックアップ_reしてから、そのオブジェクトをルックアップします。ただし、Python では辞書検索が非常に高速であるため、これは小さなコンポーネントである可能性があります。(上記の私の例では、二重呼び出しバージョンで追加の名前検索が 1 つしかない場合でも、パフォーマンスがかなり低下しています。)selfmatch_retimeit

于 2012-09-07T19:35:18.140 に答える
0

最初のバージョンは、毎回余分な関数呼び出しです。それにはいくらかのオーバーヘッドが発生します。

于 2012-09-07T19:33:58.767 に答える