Python字符串“加入”比“+”更快(?),但这里有什么问题?

问题:

我在前面的文章中询问了用于大规模动态字符串连接的最有效的方法,并且我建议使用加入方法,这是最好的,最简单和最快速的方法(正如大家所说的那样)。但是当我玩字符串连接时,我发现了一些奇怪的(?)结果。我确定有些事情正在进行,但我不能完全理解。这是我做的:
我定义了这些函数:

import timeit
def x():
    s=[]
    for i in range(100):
        # Other codes here...
        s.append("abcdefg"[i%7])
    return ''.join(s)

def y():
    s=''
    for i in range(100):
        # Other codes here...
        s+="abcdefg"[i%7]
    return s

def z():
    s=''
    for i in range(100):
        # Other codes here...
        s=s+"abcdefg"[i%7]
    return s

def p():
    s=[]
    for i in range(100):
        # Other codes here...
        s+="abcdefg"[i%7]
    return ''.join(s)

def q():
    s=[]
    for i in range(100):
        # Other codes here...
        s = s + ["abcdefg"[i%7]]
    return ''.join(s)

我试图让其他的东西(除了连接)在整个函数中保持一致。然后我用下面的结果进行了测试(在Windows 32位机器上使用Python 3.1.1 IDLE):

timeit.timeit(x) # 31.54912480500002
timeit.timeit(y) # 23.533029429999942 
timeit.timeit(z) # 22.116181330000018
timeit.timeit(p) # 37.718607439999914
timeit.timeit(q) # 108.60377576499991

这意味着它显示strng = strng + dyn_strng是最快的。虽然时代的差异并不显着(除了最后一个),但我想知道为什么会发生这种情况。那是因为我使用的是Python 3.1.1,并且提供了“+”效率最高?我应该使用“+”替代join吗?或者,我做了一件非常愚蠢的事情吗?或者是什么?请解释清楚。

回答:

我们中的一些Python提交者,我相信大多数Rigo和Hettinger,都是为了优化某些特殊情况下的s += something 枯萎病这个太常见的s += something 枯萎病事实证明,初学者永远不会确信''.join是正确的选择,+=的可怕缓慢可能会给Python带来一个坏名声。我们其他人并不那么热,因为他们不可能把每一次事件(甚至只是大多数事件)都优化成体面的表现。但是我们对这个问题感到不够热情,试图积极阻止。
我相信这个线程证明我们应该更严厉地反对他们。就像现在一样,他们在某些难以预测的案例子集中对+=进行了优化,使得对于特殊的愚蠢的案例比正确的方法(IS仍然是''.join)要快20%完美的方式,通过使用错误的成语,让初学者陷入追求20%的不相关的收益……不惜一次又一次地从他们的POV中突然冒出200%的性能损失(或更多,因为非线性行为仍然潜伏在Hettinger和Rigo打扮并放入花朵的角落之外 – 一个重要的东西,一个会使他们悲惨的东西。这与Python的“理想情况下只有一个显而易见的方式”是一致的,对我来说,就像我们总体上对初学者有一个陷阱 – 最好的一样……那些不仅仅接受他们被“更好的”所告知,但好奇地去质疑和探索。
啊,我放弃了。 OP,@mshsayem,继续前进,在任何地方使用+ =,享受你无关紧要的20%加速,微不足道的情况下,你最好享受他们的剑柄 – 因为有一天,当你看不到它在一个重要的,大的操作中,你会被200%减速的迎面而来的拖车卡车撞击(除非你运气不好,这是一个2000%的;)。只要记住:如果你觉得“Python非常慢”,请记住,它更可能是你心爱的+=循环之一转身,咬着喂它的手。
对于我们其他人来说,那些明白We should forget about small efficiencies, say about 97% of the time意味着什么的人,我会一直坚持推荐''.join,所以我们都可以睡得很安宁,知道我们不会遇到超线性的放缓。我们最不期待,至少可以负担得起你。但是对你来说,Armin Rigo和Raymond Hettinger(后两位亲爱的私人朋友,不止是共同委托人;-) – 可能你的+=顺利,你的大O永远不会比N更坏! – )
所以,对于我们其他人来说,这里有一个更有意义和有趣的测量:

$ python -mtimeit -s'r=[str(x)*99 for x in xrange(100,1000)]' 's="".join(r)'
1000 loops, best of 3: 319 usec per loop

每个297个字符的900个字符串,直接加入列表当然是最快的,但是在这之前OP被吓坏了。但:

$ python -mtimeit -s'r=[str(x)*99 for x in xrange(100,1000)]' 's=""' 'for x in r: s+=x'
1000 loops, best of 3: 779 usec per loop
$ python -mtimeit -s'r=[str(x)*99 for x in xrange(100,1000)]' 'z=[]' 'for x in r: z.append(x)' '"".join(z)'
1000 loops, best of 3: 538 usec per loop

…有一个半重要的数据量(很少有几百个KB–每个方法都要花费一个毫秒),即使是纯粹的旧的.append也是优越的。另外,显然并且容易优化:

$ python -mtimeit -s'r=[str(x)*99 for x in xrange(100,1000)]' 'z=[]; zap=z.append' 'for x in r: zap(x)' '"".join(z)'
1000 loops, best of 3: 438 usec per loop

在平均循环时间内再削减十分之一毫秒。每个人(至少每个完全沉迷于表现的人)显然知道,HOISTING(取代内部循环,反复计算,否则会反复执行)是优化中的关键技术 – Python不会代表您提升,所以你必须在每微秒重要的罕见场合做你自己的提升。

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Python string ‘join’ is faster(?) than ‘+’, but what’s wrong here?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

32 − = 26