Python缓冲区复制速度-为什么数组比字符串慢? < P> C++中有一个缓冲对象,它继承自 STD::vector < /代码>。我想将这个缓冲区转换成Python字符串,这样我就可以通过Twisted的protocol.transport.write通过网络发送它
我想到的两种方法是(1)制作一个字符串并逐字符填充:Python缓冲区复制速度-为什么数组比字符串慢? < P> C++中有一个缓冲对象,它继承自 STD::vector < /代码>。我想将这个缓冲区转换成Python字符串,这样我就可以通过Twisted的protocol.transport.write通过网络发送它,python,arrays,string,performance,stdvector,Python,Arrays,String,Performance,Stdvector,我想到的两种方法是(1)制作一个字符串并逐字符填充: def scpychar(buf, n): s = '' for i in xrange(0, n): s += buf[i] return s (2)制作一个字符数组(因为我知道缓冲区有多大),填充它并将其转换为字符串 def scpyarr(buf, n): a = array.array('c','0'*n) for i in xrange(0, n): a[i]
def scpychar(buf, n):
s = ''
for i in xrange(0, n):
s += buf[i]
return s
(2)制作一个字符数组(因为我知道缓冲区有多大),填充它并将其转换为字符串
def scpyarr(buf, n):
a = array.array('c','0'*n)
for i in xrange(0, n):
a[i] = buf[i]
return a.tostring()
我认为(1)必须在每次调用s+=buf[I]
时创建一个新的string对象,并复制旧字符串的内容。所以我希望(2)比(1)快。但是如果我用timeit测试这个,我发现(1)的速度实际上是(2)的两倍
我想知道是否有人能解释为什么(1)更快
从
std::vector
转换为Python字符串的更有效方法的额外积分。导入dis并查看dis.dis(scpyarr)和dis.dis(scpychar)。
scpychar的解释器操作数较少
>>> import dis
>>> def scpyarr(buf, n):
... a = array.array('c','0'*n)
... for i in xrange(0, n):
... a[i] = buf[i]
... return a.tostring()
...
>>> dis.dis(scpyarr)
2 0 LOAD_GLOBAL 0 (array)
3 LOAD_ATTR 0 (array)
6 LOAD_CONST 1 ('c')
9 LOAD_CONST 2 ('0')
12 LOAD_FAST 1 (n)
15 BINARY_MULTIPLY
16 CALL_FUNCTION 2
19 STORE_FAST 2 (a)
3 22 SETUP_LOOP 37 (to 62)
25 LOAD_GLOBAL 1 (xrange)
28 LOAD_CONST 3 (0)
31 LOAD_FAST 1 (n)
34 CALL_FUNCTION 2
37 GET_ITER
>> 38 FOR_ITER 20 (to 61)
41 STORE_FAST 3 (i)
4 44 LOAD_FAST 0 (buf)
47 LOAD_FAST 3 (i)
50 BINARY_SUBSCR
51 LOAD_FAST 2 (a)
54 LOAD_FAST 3 (i)
57 STORE_SUBSCR
58 JUMP_ABSOLUTE 38
>> 61 POP_BLOCK
5 >> 62 LOAD_FAST 2 (a)
65 LOAD_ATTR 2 (tostring)
68 CALL_FUNCTION 0
71 RETURN_VALUE
>>> def scpychar(buf, n):
... s = ''
... for i in xrange(0, n):
... s += buf[i]
... return s
...
>>> dis.dis(scpychar)
2 0 LOAD_CONST 1 ('')
3 STORE_FAST 2 (s)
3 6 SETUP_LOOP 37 (to 46)
9 LOAD_GLOBAL 0 (xrange)
12 LOAD_CONST 2 (0)
15 LOAD_FAST 1 (n)
18 CALL_FUNCTION 2
21 GET_ITER
>> 22 FOR_ITER 20 (to 45)
25 STORE_FAST 3 (i)
4 28 LOAD_FAST 2 (s)
31 LOAD_FAST 0 (buf)
34 LOAD_FAST 3 (i)
37 BINARY_SUBSCR
38 INPLACE_ADD
39 STORE_FAST 2 (s)
42 JUMP_ABSOLUTE 22
>> 45 POP_BLOCK
5 >> 46 LOAD_FAST 2 (s)
49 RETURN_VALUE
>>>
比较一下:
51 LOAD_FAST 2 (a)
54 LOAD_FAST 3 (i)
57 STORE_SUBSCR
加上
vs
加载速度慢。调用函数的速度很慢
一个月前,我在问题中看到,
”。join(b)
是将字符数组连接到一个字符串中的最快方法。CPython有时可以优化字符串+=
,如果它可以确定没有人保留对旧字符串的引用,那么它就可以就位。算法(1)可能触发了优化,因此它不会受到二次运行时间的影响。但是,这种行为并不能保证,其他Python实现可能不支持它
试一试
它应该在任何Python实现上提供线性时间性能,不像(1),而且比(2)更快。我没有否决,但可以理解为什么会这样。指令的数量与性能的先验相关性很弱。这实际上不是指令的数量,而是内存消耗和CPU时间。@SeanMcLaughlin但我看到scpyarr中的重指令数量更多。这个答案没有说明算法(1)的预期二次运行时@user2357112两倍的繁重指令-两倍的时间。怎么了?我不认为join会接受向量,但它接受。酷!有趣的是,你有关于cpython就地修改的参考资料吗?@RyanHaining:见注6。
>> 62 LOAD_FAST 2 (a)
65 LOAD_ATTR 2 (tostring)
68 CALL_FUNCTION 0
38 INPLACE_ADD
39 STORE_FAST
''.join(buf)