我有一個很大的Python代碼庫,我們最近開始用Cython編譯。在不對代碼做任何修改的情況下,我期望性能保持不變,但我們計劃在性能分析後使用Cython特定的代碼優化較重的計算。然而,編譯的應用程序的速度急劇下降,似乎是全面的。方法比以前多花費10%到300%。Cython字符串串聯超慢;它還有什麼不好呢?
我一直在玩弄測試代碼來試圖找到Cython做得不好的事情,看起來字符串操作就是其中之一。我的問題是,我做錯了什麼或是Cython在某些事情上真的很糟糕?你能幫我理解爲什麼這麼糟糕,而且Cython可能做得很差嗎?
編輯:讓我試着澄清。我意識到這種類型的字符串連接是非常糟糕的;我只注意到它有一個巨大的速度差異,所以我發佈了它(可能是一個壞主意)。代碼庫沒有這種類型的可怕代碼,但仍然顯着放緩,我希望指出什麼類型的結構Cython處理不好,所以我可以找出在哪裏看。我試過分析,但它並不是特別有用。
僅供參考,這裏是我的字符串操作測試代碼。我意識到下面的代碼是可怕的和無用的,但我仍然對速度差異感到震驚。
# pyCode.py
def str1():
val = ""
for i in xrange(100000):
val = str(i)
def str2():
val = ""
for i in xrange(100000):
val += 'a'
def str3():
val = ""
for i in xrange(100000):
val += str(i)
時序代碼
# compare.py
import timeit
pyTimes = {}
cyTimes = {}
# STR1
number=10
setup = "import pyCode"
stmt = "pyCode.str1()"
pyTimes['str1'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
setup = "import cyCode"
stmt = "cyCode.str1()"
cyTimes['str1'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
# STR2
setup = "import pyCode"
stmt = "pyCode.str2()"
pyTimes['str2'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
setup = "import cyCode"
stmt = "cyCode.str2()"
cyTimes['str2'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
# STR3
setup = "import pyCode"
stmt = "pyCode.str3()"
pyTimes['str3'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
setup = "import cyCode"
stmt = "cyCode.str3()"
cyTimes['str3'] = timeit.timeit(stmt=stmt, setup=setup, number=number)
for funcName in sorted(pyTimes.viewkeys()):
print "PY {} took {}s".format(funcName, pyTimes[funcName])
print "CY {} took {}s".format(funcName, cyTimes[funcName])
編譯與
cp pyCode.py cyCode.py
cython cyCode.py
gcc -O2 -fPIC -shared -I$PYTHONHOME/include/python2.7 \
-fno-strict-aliasing -fno-strict-overflow -o cyCode.so cyCode.c
所得定時
> python compare.py
PY str1 took 0.1610019207s
CY str1 took 0.104282140732s
PY str2 took 0.0739600658417s
CY str2 took 2.34380102158s
PY str3 took 0.224936962128s
CY str3 took 21.6859738827s
對於參考用Cython模塊,我已經與用Cython 0.19.1嘗試這樣做和0.23.4。我用gcc 4.8.2和icc 14.0.2編譯了C代碼,嘗試使用兩種標誌。
瞭解CPython專門針對此進行優化確實有所幫助。事實上,將第三個例子改爲'val = str(i)+ val'使得CPython比Cython花費的時間更長(約24s)。所以也許真正的問題是,我怎麼知道CPython優化的其他實現可能不會?我確信字符串連接在我們的代碼庫中不是真正的問題。 – rpmcnally
要了解優化發生的方式,請參閱CPython解釋器源代碼:https://hg.python.org/cpython/file/7fa3e824a4ee/Python/ceval.c#l1677。注意特殊情況檢查「pyunicode」(至少在Python 3中!)。相比之下,Cython只是'PyNumber_InPlaceAdd' – DavidW
如果你想知道CPython在哪裏進行優化,Cython不會那麼搜索'_CheckExact'文件可能是一個很好的開始,儘管它可能有點乏味。主要的其他明顯的候選人,我可以看到如果'%'字符串格式 – DavidW