Tại sao str.translate nhanh hơn nhiều trong Python 3.5 so với Python 3.4?


116

Tôi đã cố gắng loại bỏ các ký tự không mong muốn khỏi một chuỗi đã cho bằng cách sử dụng text.translate()trong Python 3.4.

Mã tối thiểu là:

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

Nó hoạt động như mong đợi. Tuy nhiên, cùng một chương trình khi được thực thi trong Python 3.4 và Python 3.5 cho một sự khác biệt lớn.

Mã để tính thời gian là

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)"

Chương trình Python 3.4 mất 1,3ms trong khi chương trình tương tự trong Python 3.5 chỉ mất 26,4 giây .

Điều gì đã được cải thiện trong Python 3.5 giúp nó nhanh hơn so với Python 3.4?


11
Trong khi chúng ta đang nói về hiệu suất, sẽ tốt hơn nếu tạo trình ánh xạ của bạn như thế này : dict.fromkeys(ord(c) for c in '@#$')?
Thomas K

1
@ThomasK Tôi phát hiện ra rằng điều này tạo ra một sự khác biệt đáng kể. Yep theo cách của bạn là tốt hơn.
Bhargav Rao

Ý bạn là nhanh hơn 50 lần?
assylias

@assylias Tôi đã làm 1300 - 26.4 và sau đó chia cho 1300. Tôi đã nhận được gần 95%, vì vậy tôi đã viết :) Nó thực sự nhanh hơn 50 lần ... Nhưng tính toán của tôi có sai không? Tôi hơi yếu môn toán. Tôi sẽ học toán sớm. :)
Bhargav Rao

3
bạn nên thực hiện theo cách vòng: 26/300 = 2% để phiên bản nhanh hơn chỉ mất 2% thời gian của phiên bản chậm hơn => nó nhanh hơn 50 lần.
assylias

Câu trả lời:


148

TL; DR - VẤN ĐỀ 21118


Câu chuyện dài

Josh Rosenberg phát hiện ra rằng str.translate()chức năng này rất chậm so với bytes.translate, ông đã nêu ra một vấn đề , nói rằng:

Trong Python 3, str.translate()thường là sự bi quan về hiệu năng, không phải tối ưu hóa.

Tại sao str.translate()chậm?

Lý do chính cho str.translate()việc rất chậm là việc tra cứu được sử dụng trong từ điển Python.

Việc sử dụng maketranslàm cho vấn đề này tồi tệ hơn. Cách tiếp cận tương tự bằng cách sử dụng bytesxây dựng một mảng C gồm 256 mục để tra cứu bảng nhanh. Do đó việc sử dụng Python cấp cao hơn dictlàm cho str.translate()Python 3,4 rất chậm.

Chuyện gì đã xảy ra bây giờ?

Cách tiếp cận đầu tiên là thêm một bản vá nhỏ, translate_writer , Tuy nhiên, việc tăng tốc độ không phải là điều dễ chịu. Ngay sau đó, một bản vá khác fast_translate đã được thử nghiệm và nó mang lại kết quả rất tốt khi tăng tốc lên tới 55%.

Thay đổi chính có thể thấy từ tệp là tra cứu từ điển Python được thay đổi thành tra cứu cấp độ C.

Tốc độ bây giờ gần giống như bytes

                                unpatched           patched

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

Một lưu ý nhỏ ở đây là việc tăng cường hiệu năng chỉ nổi bật trong các chuỗi ASCII.

Như JFSebastian đề cập trong một bình luận bên dưới, Trước 3.5, dịch được sử dụng để hoạt động theo cùng một cách cho cả trường hợp ASCII và không phải ASCII. Tuy nhiên từ trường hợp 3.5 ASCII nhanh hơn nhiều.

Trước đây ASCII so với non-ascii trước đây gần như giống nhau, tuy nhiên bây giờ chúng ta có thể thấy một sự thay đổi lớn trong hiệu suất.

Nó có thể là một sự cải tiến từ 71,6μ đến 2,33 as như đã thấy trong câu trả lời này .

Các mã sau đây chứng minh điều này

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

Lập bảng kết quả:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117

13
Đây là một trong những cam kết: github.com/python/cpython/commit/
mẹo

lưu ý: trường hợp ascii so với trường hợp không ascii có thể khác nhau đáng kể về hiệu suất. Đây không phải là về 55%: như câu trả lời của bạn cho thấy, tốc độ tăng có thể là 1000s% .
JFS

so sánh: python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii) so với python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(không phải ascii). Cái sau chậm hơn nhiều (10).
JFS

@JF ơi, giờ thì tôi đã hiểu. Tôi đã chạy mã của bạn cho cả 3,4 và 3,5. Tôi đang nhận được Py3.4 nhanh hơn cho những thứ không phải ascii. Có phải do sự trùng hợp? Kết quả dpaste.com/15FKSDQ
Bhargav Rao

Trước 3.5, cả trường hợp ascii và không ascii có thể giống nhau đối với Unicode .translate(), nghĩa là trường hợp ascii chỉ nhanh hơn nhiều trong Python 3.5 (bạn không cần bytes.translate()hiệu năng ở đó).
jfs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.