Mengapa str.translate jauh lebih cepat di Python 3.5 dibandingkan dengan Python 3.4?

116

Saya mencoba untuk menghapus karakter yang tidak diinginkan dari string tertentu menggunakan text.translate()Python 3.4.

Kode minimalnya adalah:

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

Ini bekerja seperti yang diharapkan. Namun program yang sama ketika dijalankan dengan Python 3.4 dan Python 3.5 memberikan perbedaan yang besar.

Kode untuk menghitung pengaturan waktu adalah

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

Program Python 3.4 membutuhkan 1,3ms sedangkan program yang sama dengan Python 3.5 hanya membutuhkan 26,4μs .

Apa yang telah ditingkatkan dalam Python 3.5 yang membuatnya lebih cepat dibandingkan dengan Python 3.4?

Bhargav Rao
sumber
11
Sementara kita sedang berbicara tentang kinerja, tidak akan lebih baik untuk menghasilkan mapper Anda seperti ini: dict.fromkeys(ord(c) for c in '@#$')?
Thomas K
1
@ ThomasK Saya menemukan bahwa ini membuat perbedaan yang signifikan. Ya jalanmu lebih baik.
Bhargav Rao
Apakah maksud Anda 50x lebih cepat?
assylias
@assylias Saya melakukan 1300 - 26,4 dan kemudian dibagi 1300. Saya mendapatkan hampir 95%, jadi saya menulis :) Sebenarnya lebih dari 50x lebih cepat ... Tetapi apakah perhitungan saya salah? Saya agak lemah dalam matematika. Saya akan segera belajar matematika. :)
Bhargav Rao
3
Anda harus melakukannya secara terbalik: 26/1300 = 2% jadi versi yang lebih cepat hanya membutuhkan 2% dari waktu yang dibutuhkan oleh versi yang lebih lambat => itu 50x lebih cepat.
assylias

Jawaban:

148

TL; DR - MASALAH 21118


Cerita yang panjang

Josh Rosenberg menemukan bahwa str.translate()fungsinya sangat lambat dibandingkan dengan bytes.translate, dia mengangkat masalah , menyatakan bahwa:

Dalam Python 3, str.translate()biasanya merupakan pesimisasi kinerja, bukan pengoptimalan.

Mengapa str.translate()lambat?

Alasan utama untuk str.translate()menjadi sangat lambat adalah karena pencariannya menggunakan kamus Python.

Penggunaan maketransmemperburuk masalah ini. Pendekatan serupa menggunakan bytesmembangun larik C yang terdiri dari 256 item untuk pencarian tabel cepat. Karenanya penggunaan Python level yang lebih tinggi dictmembuat str.translate()dalam Python 3.4 sangat lambat.

Apa yang terjadi sekarang?

Pendekatan pertama adalah menambahkan patch kecil, translate_writer , Namun peningkatan kecepatannya tidak begitu menyenangkan. Segera patch lain fast_translate diuji dan menghasilkan hasil yang sangat bagus dengan speedup hingga 55%.

Perubahan utama seperti yang dapat dilihat dari file ini adalah pencarian kamus Python diubah menjadi pencarian tingkat C.

Kecepatannya sekarang hampir sama dengan bytes

                                unpatched           patched

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

Catatan kecil di sini adalah bahwa peningkatan kinerja hanya menonjol dalam string ASCII.

Seperti yang disebutkan JFSebastian dalam komentar di bawah, Before 3.5, translate digunakan untuk bekerja dengan cara yang sama untuk kasus ASCII dan non-ASCII. Namun dari kasus 3.5 ASCII jauh lebih cepat.

Sebelumnya ASCII vs non-ascii biasanya hampir sama, namun sekarang kita dapat melihat perubahan besar dalam performanya.

Ini bisa menjadi peningkatan dari 71,6μs menjadi 2,33μs seperti yang terlihat dalam jawaban ini .

Kode berikut menunjukkan ini

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

Tabulasi hasil:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117
Bhargav Rao
sumber
13
Ini adalah salah satu komitmen: github.com/python/cpython/commit/…
filmor
catatan: kasus ascii vs. non-ascii mungkin berbeda secara signifikan dalam performanya. Ini bukan tentang 55%: seperti yang ditunjukkan oleh jawaban Anda, kecepatannya bisa 1000s% .
jfs
bandingkan: python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii) vs. python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(non-ascii). Yang terakhir ini jauh (10x) lebih lambat.
jfs
@ JF Oh, saya mengerti sekarang. Saya menjalankan kode Anda untuk 3.4 dan 3.5. Saya mendapatkan Py3.4 lebih cepat untuk hal-hal non-ascii. Apakah ini kebetulan? Hasilnya dpaste.com/15FKSDQ
Bhargav Rao
Sebelum 3.5, kasus ascii dan non-ascii mungkin sama untuk Unicode .translate()yaitu, kasus ascii jauh lebih cepat di Python 3.5 saja (Anda tidak perlu bytes.translate()performanya di sana).
jfs