为什么Python 3.5中的str.translate比Python 3.4更快?
我试图从Python 3.4中使用text.translate()
从给定的string中删除不需要的字符。
最小的代码是:
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程序需要1.3ms,而Python 3.5中的相同程序只需要26.4μs 。
在Python 3.5中有哪些改进,使得它比Python 3.4更快?
TL; DR – ISSUE 21118
漫长的故事
Josh Rosenberg发现str.translate()
函数与bytes.translate
相比非常慢,他提出了一个问题 ,指出:
在Python 3中,
str.translate()
通常是一种性能悲观,而不是优化。
为什么str.translate()
慢?
str.translate()
非常慢的主要原因是查找过去在Python字典中。
maketrans
的使用使这个问题变得更糟。 使用bytes
的类似方法构build了256个项目的C数组来快速查找表。 因此,使用更高级别的Python dict
使得Python 3.4中的str.translate()
非常慢。
现在发生什么事?
第一种方法是添加一个小补丁, translate_writer ,但是速度的提高并不令人满意。 不久,另一个补丁fast_translate进行了testing,并取得了高达55%的加速非常好的结果。
从文件中可以看出主要的变化是Python字典查找被改为C级查找。
速度现在几乎与bytes
相同
unpatched patched str.translate 4.55125927699919 0.7898181750006188 str.translate from bytes trans 1.8910855210015143 0.779950579000797
这里的一个小提示是性能增强只在ASCIIstring中显着。
正如JFSebastian在下面的注释中提到的,在3.5之前,翻译用于以ASCII和非ASCII的情况同样的方式工作。 但是从3.5的ASCII码情况要快得多。
早期的ASCII与非ascii以前几乎一样,但是现在我们可以看到性能发生了很大的变化。
从这个答案可以看出,它可以从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