为什么在 Python 3.5中 str.trans 比在 Python 3.4中快得多?

我试图在 Python 3.4中使用 text.translate()从给定的字符串中删除不需要的字符。

最小的代码是:

import sys
s = 'abcde12345@#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

它的工作原理和预期的一样,但是在 Python 3.4和 Python 3.5中执行相同的程序会产生很大的不同。

计算时间的代码是

python3 -m timeit -s "import sys;s = 'abcde12345@#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

Python 3.4程序采用 < em > 1.3 ms ,而 Python 3.5中的同一程序只采用 < em > 26.4 μs

与 Python 3.4相比,Python 3.5有哪些改进使其运行速度更快?

15825 次浏览

DR-第21118期


说来话长

Josh Rosenberg 发现,与 bytes.translate相比,str.translate()的功能非常缓慢,他提出了 问题,并指出:

在 Python3中,str.translate()通常是性能悲观化,而不是优化。

为什么 str.translate()慢?

str.translate()非常慢的主要原因是查找过去是在 Python 字典中进行的。

maketrans的使用使这个问题变得更糟。使用 bytes的类似方法构建一个由256个项组成的 C 数组来快速查找表。因此,使用更高级别的 Python dict会使 Python 3.4中的 str.translate()变得非常慢。

又怎么了?

第一种方法是添加一个小补丁,翻译作者,但是速度的提高并不那么令人满意。不久,另一个补丁 快速翻译测试,它产生了非常好的结果,高达55% 的加速。

The main change as can be seen from the file is that the Python dictionary lookup is changed into a C level lookup.

现在的速度几乎与 bytes相同

                                unpatched           patched


str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

这里需要注意的是,性能增强仅在 ASCII 字符串中显著。

正如 J.F.Sebastian 在下面的 评论中提到的,在3.5之前,对于 ASCII 和非 ASCII 的情况,翻译过去的工作方式是相同的。然而,从3.5 ASCII 的情况下是快得多。

早期的 ASCII 和 non-ASCII 几乎是一样的,但是现在我们可以看到性能的巨大变化。

answer中可以看出,从71.6 μs 到2.33 μs 是一个改进。

下面的代码演示了这一点

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop


python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

结果表:

         Python 3.4    Python 3.5
Ascii     91.2          2.3
Unicode   101           117