我有一个长度为5*10^6
的大字符串。
我必须通过将其分成16个字符的块来进行一些处理。我使用自定义函数来分割字符串,假设它的性能优于拼接方法。
功能如下:
def spliceSplitter(s):
sum = 0
while len(s) > 0:
block = s[:16]
# Assuming the process to be done with data block is calculating its length.
sum += len(block)
s = s[16:]
return sum
自定义功能:
def normalSplitter(s):
sum = 0
l = len(s)
data =""
for i in xrange(l):
if i%16 == 0:
# Assuming the process to be done with data block is calculating its length.
sum += len(data)
data = ""
data += s[i]
return sum+len(data)
我在他们两个上都使用了cProfiler,结果如下(以秒为单位的时间):
String Length | Splice Splitter | Normal Splitter
---------------------------------------------------------
5000000 | 289.0 | 1.274
500000 | 0.592 | 0.134
50000 | 0.25 | 0.28
5000 | 0.001 | 0.003
我按如下方式生成字符串:
s = ''.join([str(random.randint(1,9)) for x in xrange(5000000)])
我的问题:
注意:我必须执行的process(data)
没有返回值。
使用Yield和改进的Splice Splitter,性能得到以下结果:
String Length | Splice Splitter | Normal Splitter | Yield/Generator
-------------------------------------------------------------------------------
5000000 | 0.148 | 1.274 | 0.223
500000 | 0.016 | 0.134 | 0.29
50000 | 0.003 | 0.28 | 0.005
5000 | ~0.000 | 0.003 | ~0.000
代码:
def pythonicSplitter(s):
gen = (s[i:i+16] for i in xrange(0,len(s),16))
sum = 0
for data in gen:
sum += len(data)
return sum
def spliceSplitter(s):
sum = 0
for x in xrange(0, len(s), 16):
block = s[x:x+16]
# Assuming the process to be done with data block is calculating its length.
sum += len(block)
return sum
提高效果的原因:
s = s[16:]
。这导致时间复杂度为~O(N^2)
。 s
替换了字符串s[x:x+16]
的重复创建,代码复杂性就会降低到O(N*16)
,从而大幅提升其性能。 Yield / Generator函数执行相同的操作(pythonicSplitter()
),但由于生成器(迭代器)的大量调用,完成操作所需的时间略大于Splice Splitter中使用的那个。答案 0 :(得分:4)
我猜这行:s = s[16:]
导致s
被覆盖每次循环迭代,复制整个字符串。 block = s[:16]
也在复制一个字符串,所以你基本上在每次循环迭代时将字符串写入内存两次。 data = ""
中的normalSplitter()
也可能确保您一次不会在内存中保留超过16个字符串的字符,并且您永远不会对整个字符串执行任何复制操作。
这推动了很多数据,并且,我希望,导致你开始在最大的字符串上获得缓存未命中(尽管显然较小的字符串能够舒适地适应缓存)。尝试使用解决方案like this one。
def newSplitter(s, n=16):
for i in xrange(0, len(s), n):
yield l[i:i+n]