Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Cython字符串连接速度非常慢;它还能做什么?_Python_Performance_Cython - Fatal编程技术网

Python Cython字符串连接速度非常慢;它还能做什么?

Python Cython字符串连接速度非常慢;它还能做什么?,python,performance,cython,Python,Performance,Cython,我有一个庞大的Python代码库,我们最近开始使用Cython进行编译。在不对代码进行任何更改的情况下,我希望性能保持不变,但我们计划在评测之后使用Cython特定的代码优化更繁重的计算。然而,编译后的应用程序的速度急剧下降,而且似乎是全面的。这些方法比以前花费了10%到300%的时间 我一直在玩弄测试代码,试图找出Cython做得不好的地方,而字符串操作似乎就是其中之一。我的问题是,我是做错了什么,还是Cython在某些方面真的很糟糕?你能帮我理解为什么这件事这么糟糕,还有什么事Cython可

我有一个庞大的Python代码库,我们最近开始使用Cython进行编译。在不对代码进行任何更改的情况下,我希望性能保持不变,但我们计划在评测之后使用Cython特定的代码优化更繁重的计算。然而,编译后的应用程序的速度急剧下降,而且似乎是全面的。这些方法比以前花费了10%到300%的时间

我一直在玩弄测试代码,试图找出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 0.19.1和0.23.4尝试过了。我用gcc 4.8.2和icc 14.0.2编译了C代码,尝试了使用这两种格式的各种标志。

通常不赞成这种格式的重复字符串串联;一些解释器为此进行了优化(秘密地过度分配,并允许在已知安全的情况下对技术上不可变的数据类型进行变异),但Cython正试图对某些事情进行硬编码,这使得这变得更加困难

真正的答案是“不要一次又一次地连接不可变的类型。”(这在任何地方都是错误的,在Cython中更糟糕)。Cython可能会处理fine的一个完全合理的方法是为个人
str
创建一个
列表,然后调用
''。最后加入(listofstr)
,立即创建
str

无论如何,你不会给Cython任何打字信息,所以速度不会太快。试着用简单的东西来帮助它,那里的加速可能会弥补其他地方的损失。例如,
cdef
您的循环变量并使用
''。join
可能有助于:

cpdef str2():
    cdef int i
    val = []
    for i in xrange(100000):  # Maybe range; Cython docs aren't clear if xrange optimized
        val.append('a')
    val = ''.join(val)

值得一读:Pep 0008>编程建议:

代码的编写方式应该不会对其他Python实现(PyPy、Jython、IronPython、Cython、Psyco等)造成不利影响

例如,对于形式为a+=b或a=a+b的语句,不要依赖于CPython对就地字符串连接的高效实现。这种优化即使在CPython中也很脆弱(它只适用于某些类型),并且在不使用refcounting的实现中根本不存在。在库的性能敏感部分中,应改为使用“”。join()形式。这将确保在各种实现中以线性时间进行连接


参考:

cython
代码必须进行Python函数调用,以格式化整数并创建新字符串。它可以将迭代转换为C,但这只是操作的一小部分。谢谢你的回答。我意识到这种类型的连接是一种糟糕的做法,但我的印象是,如果没有优化,Cython代码将吐出与CPython基本相同的代码,但显然不是。但是我们的代码库相当成熟,所以我会震惊于在任何地方发现这种类型的代码;你有没有指向其他CPython可能正在优化但Cython不会优化的东西的指针?@rpmcnally:列举这些东西真的很难。Cython优化和CPython优化在某些方面几乎是对立的;Cython从使用大量低级“原语”(例如在范围内循环并索引列表)中获益,因为它很容易转换为C;相比之下,CPython直接迭代
列表
或在需要
列表时使用
枚举
要快得多。基本上,Cython在编写类似C的Python代码时是最快的,使用类似C的假设(其中一个假设是字符串连接很糟糕)。问题是,分析显示,经济全面放缓;几乎所有的东西都慢了。我想我必须想出一个更好的方法来分析这段代码。那么,“我问了一个糟糕的问题”的适当礼仪是什么呢?只需删除它或选择最接近答案的内容?@rpmcnally我会保留问题,并(可选)选择最能回答有关字符串连接的原始问题的答案。更一般的问题是“Cython与CPython相比做得差吗?”可能更难得到明确的答案,并且可能会因为“过于宽泛”而结束,因为知道CPython专门为此进行了优化。事实上,将第三个示例更改为'val=str(i)+val'会使CPython比Cython花费更长的时间(~24s)。所以,也许真正的问题是,我如何知道CPython优化了哪些,而其他实现可能没有?我确信字符串连接不是我们代码库中的真正问题。有关优化发生在何处的好奇,请参阅CPython解释程序源:。注意“pyunicode”的特殊情况检查(至少在python3中!)。相反,Cython只做
PyNumber\u inpeaceadd
,如果您想知道CPython在哪里做Cython没有做的优化,那么在该文件中搜索
\u CheckExact
可能是一个很好的开始,尽管它可能有点乏味。另一个主要原因显然是坦率的