我有一个很大的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