Str.translate Python 3.5'te Python 3.4'e kıyasla neden çok daha hızlı?


116

text.translate()Python 3.4'ü kullanarak belirli bir dizeden istenmeyen karakterleri kaldırmaya çalışıyordum .

Asgari kod:

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

Beklendiği gibi çalışıyor. Ancak aynı program Python 3.4 ve Python 3.5'te çalıştırıldığında büyük bir fark yaratır.

Zamanlamaları hesaplamak için kod:

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 programı 1.3ms alırken Python 3.5'teki aynı program yalnızca 26.4μs alır .

Python 3.5'te Python 3.4'e kıyasla daha hızlı hale getiren nedir?


11
Biz Performans söz konusu olsa da, bu gibi mapper oluşturmak için daha iyi olmaz: dict.fromkeys(ord(c) for c in '@#$')?
Thomas K

1
@ThomasK Bunun önemli bir fark yarattığını öğrendim. Evet, senin yolun daha iyi.
Bhargav Rao

50 kat daha hızlı mı demek istediniz?
assylias

@assylias 1300 - 26.4 yaptım ve sonra 1300'e böldüm. Yaklaşık% 95 aldım, bu yüzden yazdım :) Aslında 50 kattan daha hızlı ... Ama hesaplamam yanlış mı? Matematikte biraz zayıfım. Yakında matematik öğreneceğim. :)
Bhargav Rao

3
bunu şu şekilde yapmalısınız: 26/1300 =% 2, böylece daha hızlı sürüm, yavaş sürümün harcadığı sürenin yalnızca% 2'sini alır => 50 kat daha hızlıdır.
assylias

Yanıtlar:


148

TL; DR - SAYI 21118


Uzun hikaye

Josh Rosenberg, str.translate()işlevin çok yavaş bytes.translateolduğunu öğrendi, bir sorunu gündeme getirerek şunları söyledi:

Python 3'te, str.translate()genellikle optimizasyon değil, bir performans pessimizasyonu söz konusudur.

Neden str.translate()yavaştı?

str.translate()Çok yavaş olmanın ana nedeni , aramanın bir Python sözlüğünde yapılmasıydı.

Kullanımı maketransbu sorunu daha da kötüleştirdi. bytesHızlı tablo araması için benzer yaklaşım kullanarak 256 öğelik bir C dizisi oluşturur. Dolayısıyla yüksek seviyeli Python kullanımı dictyapar str.translate()Python 3.4 çok yavaş.

Ne oldu şimdi?

İlk yaklaşım küçük bir yama eklemekti, translate_writer , Ancak hız artışı o kadar da hoş değildi. Yakında başka bir yama fast_translate test edildi ve% 55'e varan hızlanma ile çok güzel sonuçlar verdi.

Dosyadan görülebileceği gibi ana değişiklik, Python sözlük aramasının bir C seviyesi aramasına dönüştürülmesidir.

Şimdi hızlar neredeyse aynı bytes

                                unpatched           patched

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

Buradaki küçük bir not, performans geliştirmenin yalnızca ASCII dizelerinde öne çıkmasıdır.

JFSebastian'ın aşağıdaki bir yorumda bahsettiği gibi, 3.5'ten önce çeviri, hem ASCII hem de ASCII olmayan durumlarda aynı şekilde çalışıyordu. Ancak 3.5 ASCII durumundan çok daha hızlıdır.

Daha önceki ASCII ve non-ascii karşılaştırması eskiden neredeyse aynıydı, ancak şimdi performansta büyük bir değişiklik görebiliyoruz.

Bu görüldüğü gibi 2.33μs için 71.6μs gelen bir gelişme olabilir cevap .

Aşağıdaki kod bunu göstermektedir

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

Sonuçların çizelgesi:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117

13

not: ascii ve ascii dışı durum performans açısından önemli ölçüde farklılık gösterebilir. 55% İle ilgili değil : cevabınızın gösterdiği gibi, hız 1000% s olabilir .
jfs

karşılaştır: python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii) ile python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(ascii olmayan). İkincisi çok (10x) daha yavaştır.
jfs

@JF Oh, şimdi anladım. Kodunuzu hem 3.4 hem de 3.5 için çalıştırdım. Ascii olmayan şeyler için Py3.4'ü daha hızlı alıyorum. Tesadüfen mi? Sonuçlar dpaste.com/15FKSDQ
Rao

3.5'ten önce, hem ascii hem de ascii olmayan durumlar muhtemelen Unicode için aynıdır, .translate()yani ascii durumu yalnızca Python 3.5'te çok daha hızlıdır ( bytes.translate()orada performansa ihtiyacınız yoktur).
jfs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.