为什么在Python 3.5中str.translate比Python 3.4更快?


116

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

最小的代码是:

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.3毫秒,而Python 3.5中的同一程序仅花费26.4μs

Python 3.5中有哪些改进使其比Python 3.4更快?


11
虽然我们谈论的性能,岂不是更好地生成您的映射是这样的:dict.fromkeys(ord(c) for c in '@#$')
Thomas K

1
@ThomasK我发现这有很大的不同。是的,您的方法更好。
巴尔加夫(Bhargav Rao)

您是说快50倍吗?
assylias 2015年

@assylias我做了1300-26.4,然后除以1300。我得到了将近95%,所以我写道:)实际上快50倍以上...但是我的计算错了吗?我的数学有点虚弱。我会尽快学习数学。:)
Bhargav Rao

3
您应该采用以下方式:26/1300 = 2%,因此较快版本仅花费较慢版本所花费时间的2%=>它快50倍。
assylias 2015年

Answers:


148

TL; DR- 问题21118


长篇故事

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

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

为什么str.translate()慢呢?

str.translate()速度很慢的主要原因是查找曾经在Python字典中进行。

使用maketrans此问题使情况变得更糟。类似的方法是使用bytes256个项目构建一个C数组以快速查找表。因此,较高级别的Python的使用dict使str.translate()Python 3.4中的速度非常慢。

现在发生什么事?

第一种方法是添加一个小的补丁,translate_writer,但是速度的提高并不令人满意。很快又测试了另一个补丁fast_translate,它产生了非常好的结果,加速了55%。

从文件中可以看到的主要变化是Python字典查找已更改为C级查找。

现在的速度几乎与 bytes

                                unpatched           patched

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

这里需要注意的一点是,性能增强仅在ASCII字符串中突出。

正如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

13
这是提交的一个:github.com/python/cpython/commit/...
filmor

注意:ASCII与非ASCII情况在性能上可能有很大差异。它与55%无关:如您的答案所示,加速可以为1000s%
jfs 2015年

比较: python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii)与python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(non-ascii)。后者要慢很多(10倍)。
jfs 2015年

@JF哦,我现在明白了。我为3.4和3.5运行了您的代码。我正在为非ascii的东西更快地获取Py3.4。是巧合吗?结果dpaste.com/15FKSDQ
Bhargav Rao

在3.5之前,ASCII的ascii和non-ascii情况可能都相同,.translate()即,仅在Python 3.5中,ascii情况要快得多(您不需要bytes.translate()那里的性能)。
jfs 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.