str.translate가 Python 3.4에 비해 Python 3.5에서 훨씬 빠른 이유는 무엇입니까?


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

Python 3.4 프로그램은 1.3ms걸리는 반면 Python 3.5의 동일한 프로그램은 26.4μs걸립니다 .

Python 3.4에 비해 Python 3.5에서 개선 된 점은 무엇입니까?


11
성능에 대해 이야기하는 동안 다음과 같이 매퍼를 생성하는 것이 더 낫지 dict.fromkeys(ord(c) for c in '@#$')않을까요?
Thomas K

1
@ThomasK 나는 이것이 중요한 차이를 만들었다는 것을 알았습니다. 네 방법이 더 좋습니다.
Bhargav Rao

50 배 더 빠르다는 뜻입니까?
assylias

@assylias 1300-26.4를 한 다음 1300으로 나눕니다. 거의 95 %를 얻었으므로 썼습니다. :) 실제로 50 배 이상 빠릅니다 ...하지만 내 계산이 잘못 되었나요? 나는 수학이 약간 약하다. 곧 수학을 배우겠습니다. :)
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은 빠른 테이블 조회를 위해 256 개 항목의 C 배열을 구축합니다. 따라서 더 높은 수준의 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 케이스에서 훨씬 빠릅니다.

이전의 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
이것은 커밋 중 하나입니다 : github.com/python/cpython/commit/…
filmor dec

참고 : ascii 대 비 ASCII 대소 문자는 성능면에서 크게 다를 수 있습니다. 55% 에 관한 것이 아닙니다 . 답변에서 알 수 있듯이 속도 향상은 1000s % 일 수 있습니다 .
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). 후자는 훨씬 (10 배) 느립니다.
jfs

@JF 아, 이제 이해했습니다. 3.4와 3.5 모두에 대해 코드를 실행했습니다. ASCII가 아닌 항목의 경우 Py3.4가 더 빨라집니다. 우연입니까? 결과 dpaste.com/15FKSDQ
Bhargav Rao

3.5 이전에는 ascii 케이스와 non-ascii 케이스 모두 유니 코드에서 동일 할 것입니다. .translate()즉, ascii 케이스는 Python 3.5에서만 훨씬 빠릅니다 ( bytes.translate()성능이 필요하지 않음 ).
jfs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.