我通过将字符串表示形式从 1 连接到一个大数字(在我的例子中是 20000000)来测试不同速度连接方法的速度。我正在测试的三种方法是:
import cProfile
count = 20000000
def profileWrapper(f):
def wrapper(*args, **argv):
pr = cProfile.Profile()
pr.enable()
string = f(*args, **argv)
pr.create_stats()
pr.print_stats()
return string
return wrapper
@profileWrapper
def naiveConcat():
global count
string = ''
for i in xrange(count):
string += `i`
return string
@profileWrapper
def improvedConcat():
global count
string = []
for i in xrange(count):
string.append(`i`)
return ''.join(string)
@profileWrapper
def fastestConcat():
global count
return ''.join([`i` for i in xrange(count)])
print 15 * "=", "naiveConcat", 15 * "="
naiveConcat()
print 15 * "=", "improvedConcat", 15 * "="
improvedConcat()
print 15 * "=", "fastestConcat", 15 * "="
fastestConcat()
我期待看到改进后的方法比原始方法更快,而且它不应该比最快的方法慢很多,但结果似乎不是那样:
=============== naiveConcat ===============
3 function calls in 3.951 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 cProfile.py:90(create_stats)
1 3.951 3.951 3.951 3.951 str_concat.py:18(naiveConcat)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=============== improvedConcat ===============
20000004 function calls in 6.990 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 cProfile.py:90(create_stats)
1 5.196 5.196 6.990 6.990 str_concat.py:26(improvedConcat)
20000000 1.322 0.000 1.322 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.473 0.473 0.473 0.473 {method 'join' of 'str' objects}
=============== fastestConcat ===============
4 function calls in 3.043 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 cProfile.py:90(create_stats)
1 2.539 2.539 3.043 3.043 str_concat.py:34(fastestConcat)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.504 0.504 0.504 0.504 {method 'join' of 'str' objects}
改进后的方法甚至比原始方法慢得多!
这没有意义,因为天真的方法正在创建新的绑定(bind)并在每次迭代时逐个字符地复制先前的字符串和连接的字符串,此方法应该采用 O(n^2),这应该慢得多比其他方法复杂度为 O(n)。
那么是什么让改进后的方法如此缓慢呢?我能想到的唯一原因是 append 方法,但根据 this article ,append方法需要O(1),所以肯定不是这个原因。那么在 improvedConcat() 中为什么需要这么长时间?谢谢。
最佳答案
improvedConcat 的 ncalls 列显示您进行了 20000004 次函数调用,而其他算法只有少数。函数调用在 Python 中并不便宜,所以尽量限制这些调用。为了演示,我按照您称为 nop 的模式做了一个新测试,它只调用一个空函数定义:
def foo():
pass
@profileWrapper
def nop():
for i in xrange(count):
foo()
return ''
我得到的计时结果与您的其他测试类似,而此 NOP(无操作)测试结果为
=============== nop ===============
20000003 function calls in 4.386 seconds
所以您有 4.4 秒的开销进行函数调用。
关于Python字符串连接速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21588074/