【问题标题】:Why is str.translate much faster in Python 3.5 compared to Python 3.4?为什么 Python 3.5 中的 str.translate 比 Python 3.4 快得多?
【发布时间】:2016-03-21 03:57:42
【问题描述】:

我试图在 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 程序需要 1.3ms,而 Python 3.5 中的相同程序只需要 26.4μs

与 Python 3.4 相比,Python 3.5 的哪些改进使其更快?

【问题讨论】:

  • 当我们谈论性能时,像这样生成映射器不是更好吗:dict.fromkeys(ord(c) for c in '@#$')
  • @ThomasK 我发现这有很大的不同。是的,你的方式更好。
  • 你的意思是快 50 倍?
  • @assylias 我做了 1300 - 26.4,然后除以 1300。我得到了将近 95%,所以我写了 :) 它实际上快了 50 倍以上……但是我的计算错了吗?我数学有点弱。我很快就会学数学。 :)
  • 你应该这样做:26 / 1300 = 2% 所以更快的版本只需要慢版本的 2% => 它快 50 倍。

标签: python string python-3.x python-internals python-3.5


【解决方案1】:

TL;DR - ISSUE 21118


长篇大论

Josh Rosenberg 发现 str.translate() 函数与 bytes.translate 相比非常慢,他提出了一个 issue,指出:

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

为什么str.translate() 很慢?

str.translate() 非常慢的主要原因是查找曾经在 Python 字典中。

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

现在发生了什么?

第一种方法是添加一个小补丁,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 字符串中突出。

正如 J.F.Sebastian 在下面的 comment 中提到的那样,在 3.5 之前,translate 在 ASCII 和非 ASCII 情况下的工作方式相同。但是从 3.5 ASCII 大小写开始要快得多。

早期的 ASCII 与非 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

【讨论】:

  • 注意:ascii 与非 ascii 大小写在性能上可能存在显着差异。这与55% 无关:如your answer shows, the speed up can be 1000s%
  • 比较: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)"(非ascii)。后者要慢得多(10 倍)。
  • @J.F.哦,我现在明白了。我为 3.4 和 3.5 运行了您的代码。对于非 ascii 的东西,我让 Py3.4 更快。是巧合吗?结果dpaste.com/15FKSDQ
  • 在 3.5 之前,对于 Unicode .translate(),ascii 和非 ascii 大小写可能相同,即仅在 Python 3.5 中 ascii 大小写要快得多(您不需要 bytes.translate() 来获得性能)。
猜你喜欢
  • 2018-07-22
  • 2020-11-17
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2022-06-12
  • 1970-01-01
  • 1970-01-01
  • 2015-10-11
相关资源
最近更新 更多