เหตุใด str.translate จึงเร็วกว่าใน Python 3.5 มากเมื่อเทียบกับ 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)"

ธ 3.4 ใช้โปรแกรม1.3msขณะที่โปรแกรมเดียวกันในหลาม 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

@assylias ฉันได้ 1300 - 26.4 แล้วหารด้วย 1300 ฉันได้เกือบ 95% ดังนั้นฉันจึงเขียน :) มันเร็วกว่า 50x จริง ๆ ... แต่การคำนวณของฉันผิดหรือเปล่า? ฉันอ่อนคณิตศาสตร์นิดหน่อย ฉันจะเรียนคณิตศาสตร์เร็ว ๆ นี้ :)
Bhargav Rao

3
คุณควรทำแบบรอบ: 26/1300 = 2% ดังนั้นเวอร์ชันที่เร็วกว่าจะใช้เวลาเพียง 2% ของเวลาที่ใช้โดยเวอร์ชันที่ช้ากว่า => เร็วกว่า 50 เท่า
assylias

คำตอบ:


148

TL; DR - ปัญหา 21118


เรื่องยาว

Josh Rosenberg พบว่าstr.translate()ฟังก์ชั่นนี้ช้ามากเมื่อเทียบกับbytes.translateเขายกปัญหาโดยระบุว่า:

ใน Python 3 str.translate()มักจะเป็นการมองในแง่ร้ายของประสิทธิภาพไม่ใช่การเพิ่มประสิทธิภาพ

ทำไมถึงstr.translate()ช้า?

สาเหตุหลักที่ทำให้str.translate()ช้ามากคือการค้นหาเคยอยู่ในพจนานุกรม Python

การใช้งานmaketransทำให้ปัญหานี้แย่ลง วิธีการที่คล้ายกันโดยใช้bytesสร้างอาร์เรย์ C จำนวน 256 รายการเพื่อค้นหาตารางอย่างรวดเร็ว ดังนั้นการใช้ 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 case นั้นเร็วกว่ามาก

ASCII กับ non-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
นี่คือหนึ่งใน
คอมมิต

หมายเหตุ: ascii กับ non-ascii case อาจแตกต่างกันอย่างมีนัยสำคัญในด้านประสิทธิภาพ มันไม่ได้เกี่ยว55%: เป็นแสดงให้เห็นว่าคำตอบของคุณความเร็วขึ้นสามารถ1000 s%
jfs

เปรียบเทียบ: 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) อย่างหลังช้ากว่ามาก (10x)
jfs

@JF โอ้ฉันเข้าใจแล้ว ฉันรันโค้ดของคุณทั้ง 3.4 และ 3.5 ฉันได้รับ Py3.4 เร็วขึ้นสำหรับสิ่งที่ไม่ใช่ ascii มันเป็นเรื่องบังเอิญ? ผลลัพธ์dpaste.com/15FKSDQ
Bhargav Rao

ก่อนหน้า 3.5 ทั้งกรณี ascii และ non-ascii อาจเหมือนกันสำหรับ Unicode .translate()กล่าวคือ ascii case นั้นเร็วกว่ามากใน Python 3.5 เท่านั้น (คุณไม่ต้องการbytes.translate()ประสิทธิภาพที่นั่น)
jfs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.