我通过将字符串表示从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}
改进的方法甚至比天真的方法慢得多!
这没有意义,因为naive方法正在创建新的绑定并且在每个ITERATION上按字符连接字符串连接字符串,这个方法应该采用O(n ^ 2),这应该慢得多比其他方法O(n)。
那么是什么让改进的方法如此缓慢?我能想到的唯一原因是append方法,但根据this article,append方法需要O(1),所以它绝对不是原因。那么在ImprovedConcat()中需要这么长时间?谢谢。
答案 0 :(得分:2)
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.4s的开销来进行函数调用。