Python에서 ''.join ()이 + =보다 빠른 이유는 무엇입니까?
나는 온라인에서 (Stack Overflow 등에서) 사용하여 연결 +
하거나 사용하는 것이 얼마나 비효율적 인 나쁜 습관 인지에 +=
대한 많은 정보를 사용할 수 있습니다 .
왜 +=
그렇게 비효율적이라는 것을 알 수 없습니다 . 여기 에서 "특정 사례에서 20 % 개선을 위해 최적화되지 않았습니다"라는 언급되지 않았습니다 (아직도 해당 사례가 무엇인지 명확하지 않은 사례가 있음).
''.join()
다른 연결 방법보다 우월 하게 만드는 더 수준에서 무슨 일이 일어나고 있습니까?
세 개의 노드에서 암호화를 구성하는 코드가 가정 해 보겠습니다.
x = 'foo'
x += 'bar' # 'foobar'
x += 'baz' # 'foobarbaz'
이 경우 Python은 먼저 할당하고 생성 'foobar'
해야 'foobarbaz'
합니다.
따라서 +=
호출 되는 각각 에 대해 문자열의 전체 내용과 여기에 추가되는 모든 내용을 완전히 새로운 메모리 버퍼에 복사해야 우리합니다. 즉, N
결합 할 문자열이있는 경우 대략 N
임시 문자열 을 할당해야 우리 하며 첫-th 하위 문자열은 ~ N 번 복사됩니다. 부분 문자열은 마지막 한 번만 복사되지만 평균적으로 각 부분 문자열은 여러 ~N/2
번 복사 됩니다.
사용하면를 .join
중간 문자열을 만들 필요가 없기 때문에 파이썬은 여러 가지 트릭을 수행 할 수 있습니다 . CPython은 미리 필요한 메모리 양을 한 다음 올바른 크기의 버퍼를 할당합니다. 마지막으로 각 조각을 새 버퍼에 복사합니다. 즉, 각 조각은 한 번만 복사됩니다.
+=
어떤 경우에는 더 나은 성능으로 이어질 수있는 다른 실행 가능한 접근 방식이 있습니다. 예를 들어 내부 언어 표현이 실제로 사용하거나
rope
그러나 CPython은 확실히 최적화를 안정적으로 수행 하지 않습니다 ( 몇 가지 코너 경우 에 해당 할 수 있습니다 ) 가장 일반적인 작동 방법을 기반으로하는 모범 사례. 표준화 된 표준 세트를 사용하면 다른 구현 작업에 더 쉽게 집중할 수 있습니다.
이 동작은 Lua의 쿠키 버퍼 장 에서 가장 잘 설명되어 있다고 생각합니다 .
Python의 맥락 에서이 설명을 다시 작성하기 위해 무고한 코드 조각 (Lua의 문서에서 파생 된 코드)으로 시작해 보겠습니다.
s = ""
for l in some_list:
s += l
각각 l
이 20 바이트이고 s
는 이미 50KB 크기로 구문 분석이 가정합니다 . 파이썬이 연결되면 s + l
50,020 바이트의 새 문자열을 만들고 50킬로바이트 s
를이 새 문자열로 복사 합니다. 즉, 새 줄마다 프로그램이 50KB의 메모리를 이동하고 증가합니다. 100 개의 새 줄 (2KB에 불고기)을 읽은 후 스 니펫은 이미 5MB 이상의 메모리를 이동했습니다. 상황을 더 악화 시키려면 임무를 마치고
s += l
이전은 이제 쓰레기입니다. 두 번의 루프주기 후에 총 100KB 이상의 쓰레기를 만드는 두 개의 오래된 것입니다. 따라서 언어 컴파일러는 가비지 수집기를 실행하기로 결정하고 해당 100KB를 해제합니다. 문제는 이것이 두주기마다 발생하고 프로그램이 전체 목록을 읽기 전에 가비지 수집기를 2,000 번 실행한다는 것입니다. 이 모든 작업을 수행 한 메모리는 목록 크기의 배수가 될 것입니다.
그리고 마지막에 :
이 문제는 Lua에만 국한되지 않습니다. 진정한 가비지 컬렉션을 사용하고 언어가 변경 불가능한 결과 인 다른 언어는 동작을 나타냅니다. Java가 가장 유명한 예입니다. (Java는
StringBuffer
문제를 개선 하는 구조 를 제공합니다 .)
는 또한 또한 불변 객체 입니다.
참고 URL : https://stackoverflow.com/questions/39312099/why-is-join-faster-than-in-python
'ProgramingTip' 카테고리의 다른 글
Eclipse에서 사용할 때 matplotlib의 기존 그림 닫기 (0) | 2020.11.28 |
---|---|
Coq에 비해 Isabelle 증명 보조의 강점과 약점은 무엇입니까? (0) | 2020.11.28 |
Serializer의 create () 및 ModelViewset의 create () perform_create () 사용시기 (0) | 2020.11.28 |
페이지 새로 고침시 Vuex 상태 (0) | 2020.11.28 |
Mercurial에서 기본 diff 도구를 사용합니까? (0) | 2020.11.28 |