gpt4 book ai didi

python成员str性能太慢

转载 作者:太空狗 更新时间:2023-10-29 20:58:35 25 4
gpt4 key购买 nike

我在 python 2.7.3 中添加到 str 类成员时遇到了一个奇怪的性能问题。我知道访问局部变量更快,但是,在下面的问题中,两个循环之间的速度差异超过 100 倍。访问 a.accum_ 的开始快但慢,就好像 str iadd 是 O(n^2) 和 str 的长度。

有谁知道原因吗?

# Fast ( < 1sec):
accum = str()
for ii in range(1000000):
if (ii % 10000) == 0:
print 'fast cnt', ii
accum += 'zzzzz\n'

# Much slower ( > 5 mins):
class Foo:
pass
a = Foo()
a.accum_ = str()
for ii in range(1000000):
if (ii % 10000) == 0:
print 'slow cnt', ii
a.accum_ += 'zzzzz\n'

最佳答案

对于第一个例子,很明显它是单引用优化的情况(实际上有两个引用:一个来自对象本身,一个 LOAD_FAST ; unicode_concatenate 将在传递控制之前尝试将其减少到 1到 PyUnicode_Append ) 由 CPython 使用此 unicode_modifiable 完成功能:

static int
unicode_modifiable(PyObject *unicode)
{
assert(_PyUnicode_CHECK(unicode));
if (Py_REFCNT(unicode) != 1)
return 0;
if (_PyUnicode_HASH(unicode) != -1)
return 0;
if (PyUnicode_CHECK_INTERNED(unicode))
return 0;
if (!PyUnicode_CheckExact(unicode))
return 0;
#ifdef Py_DEBUG
/* singleton refcount is greater than 1 */
assert(!unicode_is_singleton(unicode));
#endif
return 1;
}

但在第二种情况下,因为实例数据存储在 Python dict 中而不是简单的变量中,所以事情没有什么不同。

a.accum_ += 'foo'

实际上需要预取a.accum_ 的值并将其存储到堆栈中。所以,现在字符串有至少三个引用:一个来自实例字典,一个来自DUP_TOP。和一个来自 PyObject_GetAttrLOAD_ATTR 使用.因此,Python 无法优化这种情况,因为就地修改其中一个也会影响其他引用。

>>> class A:
pass
...
>>> a = A()
>>> def func():
a.str = 'spam'
print a.str
return '_from_func'
...
>>> a.str = 'foo'
>>> a.str += func()
spam

您可能希望此处的输出为 'spam_from_func',但它会有所不同,因为 a.str 的原始值在 之前由 Python 存储code>func() 被调用了。

>>> a.str
'foo_from_func'

字节码:

>>> import dis
>>> def func_class():
a = Foo()
a.accum = ''
a.accum += 'zzzzz\n'
...
>>> dis.dis(func_class)
2 0 LOAD_GLOBAL 0 (Foo)
3 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
6 STORE_FAST 0 (a)

3 9 LOAD_CONST 1 ('')
12 LOAD_FAST 0 (a)
15 STORE_ATTR 1 (accum)

4 18 LOAD_FAST 0 (a)
21 DUP_TOP
22 LOAD_ATTR 1 (accum)
25 LOAD_CONST 2 ('zzzzz\n')
28 INPLACE_ADD
29 ROT_TWO
30 STORE_ATTR 1 (accum)
33 LOAD_CONST 0 (None)
36 RETURN_VALUE

请注意,此优化是在 around 2004 中完成的(CPython 2.4) 以防止用户a += ba = a + b 的速度较慢,因此它主要用于简单变量并且仅在下一条指令为 STORE_FAST 时才有效>(局部变量)、STORE_DEREF(闭包)和 STORE_NAME。这不是一个通用的解决方案,the best way to do this in Python is to create a list and join its items using str.join .

CPython implementation detail: If s and t are both strings, some Python implementations such as CPython can usually perform an in-place optimization for assignments of the form s = s + t or s += t. When applicable, this optimization makes quadratic run-time much less likely. This optimization is both version and implementation dependent. For performance sensitive code, it is preferable to use the str.join() method which assures consistent linear concatenation performance across versions and implementations.

关于python成员str性能太慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30698187/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com