首先要做到在這種情況下總是簡介:
15147/297 0.032 0.000 0.041 0.000 tree.py:102(__eq__)
15400/200 0.031 0.000 0.106 0.001 tree.py:399(convert)
1 0.023 0.023 0.129 0.129 plcfrs_cython.pyx:52(parse)
6701/1143 0.022 0.000 0.043 0.000 heapdict.py:45(_min_heapify)
18212 0.017 0.000 0.023 0.000 plcfrs_cython.pyx:38(__richcmp__)
10975/10875 0.017 0.000 0.035 0.000 tree.py:75(__init__)
5772 0.016 0.000 0.050 0.000 tree.py:665(__init__)
960 0.016 0.000 0.025 0.000 plcfrs_cython.pyx:118(deduced_from)
46938 0.014 0.000 0.014 0.000 tree.py:708(_get_node)
25220/2190 0.014 0.000 0.016 0.000 tree.py:231(subtrees)
10975 0.013 0.000 0.023 0.000 tree.py:60(__new__)
49441 0.013 0.000 0.013 0.000 {isinstance}
16748 0.008 0.000 0.015 0.000 {hasattr}
我首先注意到的是,很少功能是從用Cython模塊本身。其中大部分來自tree.py模塊,也許是瓶頸。
着眼於用Cython邊我看到richcmp功能:
我們可以簡單地通過在方法聲明中添加值的類型進行優化
def __richcmp__(ChartItem self, ChartItem other, int op):
....
這使在價值
ncalls tottime percall cumtime percall filename:lineno(function)
....
18212 0.011 0.000 0.015 0.000 plcfrs_cython.pyx:38(__richcmp__)
添加elif語法而不是單個if if將啓用the switch optimization of cython
if op == 0: return self.label < other.label or self.vec < other.vec
elif op == 1: return self.label <= other.label or self.vec <= other.vec
elif op == 2: return self.label == other.label and self.vec == other.vec
elif op == 3: return self.label != other.label or self.vec != other.vec
elif op == 4: return self.label > other.label or self.vec > other.vec
elif op == 5: return self.label >= other.label or self.vec >= other.vec
獲得:
17963 0.002 0.000 0.002 0.000 plcfrs_cython.pyx:38(__richcmp__)
試圖找出哪裏是tree.py:399轉換來自我發現裏面DOPG此功能。PY花費這麼長的時間
def removeids(tree):
""" remove unique IDs introduced by the Goodman reduction """
result = Tree.convert(tree)
for a in result.subtrees(lambda t: '@' in t.node):
a.node = a.node.rsplit('@', 1)[0]
if isinstance(tree, ImmutableTree): return result.freeze()
return result
現在我不知道,如果在樹中的每個節點是目的ChartItem如果的GetItem值 正在其他地方使用,但加入這個變化:
cdef class ChartItem:
cdef public str label
cdef public str root
cdef public long vec
cdef int _hash
__slots__ = ("label", "vec", "_hash")
def __init__(ChartItem self, label, int vec):
self.label = intern(label) #.rsplit('@', 1)[0])
self.root = intern(label.rsplit('@', 1)[0])
self.vec = vec
self._hash = hash((self.label, self.vec))
def __hash__(self):
return self._hash
def __richcmp__(ChartItem self, ChartItem other, int op):
if op == 0: return self.label < other.label or self.vec < other.vec
elif op == 1: return self.label <= other.label or self.vec <= other.vec
elif op == 2: return self.label == other.label and self.vec == other.vec
elif op == 3: return self.label != other.label or self.vec != other.vec
elif op == 4: return self.label > other.label or self.vec > other.vec
elif op == 5: return self.label >= other.label or self.vec >= other.vec
def __getitem__(ChartItem self, int n):
if n == 0: return self.root
elif n == 1: return self.vec
def __repr__(self):
#would need bitlen for proper padding
return "%s[%s]" % (self.label, bin(self.vec)[2:][::-1])
和mostprobableparse內:
from libc cimport pow
def mostprobableparse...
...
cdef dict parsetrees = <dict>defaultdict(float)
cdef float prob
m = 0
for n,(a,prob) in enumerate(derivations):
parsetrees[a] += pow(e,prob)
m += 1
我得到:
189345 function calls (173785 primitive calls) in 0.162 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
6701/1143 0.025 0.000 0.037 0.000 heapdict.py:45(_min_heapify)
1 0.023 0.023 0.120 0.120 plcfrs_cython.pyx:54(parse)
960 0.018 0.000 0.030 0.000 plcfrs_cython.pyx:122(deduced_from)
5190/198 0.011 0.000 0.015 0.000 tree.py:102(__eq__)
6619 0.006 0.000 0.006 0.000 heapdict.py:67(_swap)
9678 0.006 0.000 0.008 0.000 plcfrs_cython.pyx:137(concat)
所以接下來的步驟是優化heapify和deduced_from
deduce_from可以優化多一點:
cdef inline deduced_from(ChartItem Ih, double x, pyCx, pyunary, pylbinary, pyrbinary, int bitlen):
cdef str I = Ih.label
cdef int Ir = Ih.vec
cdef list result = []
cdef dict Cx = <dict>pyCx
cdef dict unary = <dict>pyunary
cdef dict lbinary = <dict>pylbinary
cdef dict rbinary = <dict>pyrbinary
cdef ChartItem Ilh
cdef double z
cdef double y
cdef ChartItem I1h
for rule, z in unary[I]:
result.append((ChartItem(rule[0][0], Ir), ((x+z,z), (Ih,))))
for rule, z in lbinary[I]:
for I1h, y in Cx[rule[0][2]].items():
if concat(rule[1], Ir, I1h.vec, bitlen):
result.append((ChartItem(rule[0][0], Ir^I1h.vec), ((x+y+z, z), (Ih, I1h))))
for rule, z in rbinary[I]:
for I1h, y in Cx[rule[0][1]].items():
if concat(rule[1], I1h.vec, Ir, bitlen):
result.append((ChartItem(rule[0][0], I1h.vec^Ir), ((x+y+z, z), (I1h, Ih))))
return result
雖然我有信心,我們可以繼續優化的多我也就到此爲止對這個問題有了深入的瞭解。
一系列單元測試將有助於斷言每個優化不會引入任何細微的錯誤。
一個側面說明,嘗試使用空格而不是製表符。
這是一個不幸的缺點,需要在很多地方分裂。我希望有一個相當於元組的實習生。我已經在我當前的代碼中插入了非終結符。單獨的rcgules.py的內存使用率分別爲67M,78M和100M,因此它不是罪魁禍首。 – Andreas 2011-03-21 16:42:26
@Andreas:因爲這是CKY的變體,你需要明確的議程嗎? (我無法順便運行你的代碼,因爲'dopg.py'丟失了。你的網站上的這個版本不適用於'disco-dop'。) – 2011-03-21 16:51:08
dopg.py在版本庫eodop中,也在我的github上。在其他語法分析器中,您可以從左到右瀏覽句子,但是這種形式主義支持不可能的不連續性和詞序變化,所以我認爲需要議程。無論哪種方式,我直接從出版物中實施這個解析器,儘管他們也討論了有助於的啓發式。但是,我確信由於Python中的某些內容,我的開銷有問題,我只是不知道該怎麼做。我通過將它們存儲在全局字典中來實現元組實習功能,但它沒有幫助。 – Andreas 2011-03-21 17:34:16