Python 성능 최적화 20 가지 제안

9927 단어
작성 자: 개원
1. 최적화 알고리즘 시간 복잡 도
알고리즘 의 시간 복잡 도 는 프로그램의 실행 효율 에 가장 큰 영향 을 미친다. Python 에서 적당 한 데이터 구 조 를 선택 하여 시간 복잡 도 를 최적화 할 수 있다. 예 를 들 어 list 와 set 가 특정한 요 소 를 찾 는 시간 복잡 도 는 각각 O (n) 와 O (1) 이다.서로 다른 장면 은 서로 다른 최적화 방식 이 있다. 어쨌든 말하자면 일반적으로 분할, 분기 경계, 욕심, 동태 기획 등 사상 이 있다.
2. 불필요 한 데 이 터 를 줄인다.
예 를 들 어 상 삼각형 이나 하 삼각형 의 방식 으로 큰 대칭 행렬 을 저장 합 니 다.0 요소 가 대부분 을 차지 하 는 행렬 에서 희소 행렬 로 표시 한다.
3. copy 와 deepcopy 를 합 리 적 으로 사용 합 니 다.
dict 와 list 등 데이터 구조의 대상 에 대해 직접 할당 은 인용 방식 을 사용 합 니 다.어떤 경우 에는 전체 대상 을 복사 해 야 하 는데 이 때 는 copy 가방 의 copy 와 deepcopy 를 사용 할 수 있 습 니 다. 이 두 함수 의 차이 점 은 후자 가 재 귀적 으로 복사 한 것 입 니 다.효율 도 다르다. (아래 프로그램 은 ipython 에서 실 행 됩 니 다)
import copy
a = range(100000)
%timeit -n 10 copy.copy(a) #   10  copy.copy(a)
%timeit -n 10 copy.deepcopy(a)
10 loops, best of 3: 1.55 ms per loop
10 loops, best of 3: 151 ms per loop

timeit 뒤의 - n 은 실행 횟수 를 표시 하고, 뒤의 두 줄 은 두 개의 timeit 출력 에 대응 하 며, 아래 는 같 습 니 다.이 를 통 해 알 수 있 듯 이 후 자 는 수량 급 이 느리다.
4. dict 또는 set 로 요소 찾기
python dict 와 set 는 모두 hash 표를 사용 하여 이 루어 집 니 다 (c + 11 표준 라 이브 러 리 에서 unordered map 와 유사). 요 소 를 찾 는 시간 복잡 도 는 O (1) 입 니 다.
a = range(1000)
s = set(a)
d = dict((i,1) for i in a)
%timeit -n 10000 100 in d
%timeit -n 10000 100 in s
10000 loops, best of 3: 43.5 ns per loop
10000 loops, best of 3: 49.6 ns per loop
dict 의 효율 이 약간 높다.
5. 생 성기 (generator) 와 yield 를 합 리 적 으로 사용 합 니 다.
%timeit -n 100 a = (i for i in range(100000))
%timeit -n 100 b = [i for i in range(100000)]
100 loops, best of 3: 1.54 ms per loop
100 loops, best of 3: 4.56 ms per loop

사용 () 은 generator 대상 을 얻 었 습 니 다. 필요 한 메모리 공간 은 목록 의 크기 와 무관 하기 때문에 효율 이 높 습 니 다.구체 적 인 응용 에 있어 서 set (i for i in range (100000) 는 set ([i for i in range (100000)] 보다 빠르다.그러나 순환 이 필요 한 경우:
%timeit -n 10 for x in (i for i in range(100000)): pass
%timeit -n 10 for x in [i for i in range(100000)]: pass
10 loops, best of 3: 6.51 ms per loop
10 loops, best of 3: 5.54 ms per loop

후자 의 효율 은 오히려 높 지만 순환 에 break 가 있 으 면 generator 를 사용 하 는 것 이 좋다.yield 도 generator 를 만 드 는 데 사 용 됩 니 다:
def yield_func(ls):
    for i in ls:
        yield i+1

def not_yield_func(ls):
    return [i+1 for i in ls]

ls = range(1000000)
%timeit -n 10 for i in yield_func(ls):pass
%timeit -n 10 for i in not_yield_func(ls):pass
10 loops, best of 3: 63.8 ms per loop
10 loops, best of 3: 62.9 ms per loop

메모리 가 그리 큰 list 가 아 닙 니 다. list 를 직접 되 돌 릴 수 있 지만 가 독성 yield 가 더 좋 습 니 다.python 2. x 내 장 된 generator 기능 은 xrange 함수, itertools 패키지 등 이 있 습 니 다.
6. 순환 최적화
순환 외 에 할 수 있 는 일 은 순환 안에 두 지 마 세 요. 예 를 들 어 아래 의 최적화 가 두 배 빠 를 수 있 습 니 다.
a = range(10000)
size_a = len(a)
%timeit -n 1000 for i in a: k = len(a)
%timeit -n 1000 for i in a: k = size_a
1000 loops, best of 3: 569 µs per loop
1000 loops, best of 3: 256 µs per loop

7. 여러 판단 식 을 포함 하 는 순 서 를 최적화
and 에 대해 서 는 만족 조건 이 적은 것 을 앞 에 두 고 or 에 대해 서 는 만족 조건 이 많은 것 을 앞 에 두 어야 합 니 다.예:
a = range(2000)  
%timeit -n 100 [i for i in a if 10 < i < 20 or 1000 < i < 2000]
%timeit -n 100 [i for i in a if 1000 < i < 2000 or 100 < i < 20]     
%timeit -n 100 [i for i in a if i % 2 == 0 and i > 1900]
%timeit -n 100 [i for i in a if i > 1900 and i % 2 == 0]
100 loops, best of 3: 287 µs per loop
100 loops, best of 3: 214 µs per loop
100 loops, best of 3: 128 µs per loop
100 loops, best of 3: 56.1 µs per loop

8. join 병합 교체 기 에 있 는 문자열 사용 하기
In [1]: %%timeit
   ...: s = ''
   ...: for i in a:
   ...:         s += i
   ...:
10000 loops, best of 3: 59.8 µs per loop

In [2]: %%timeit
s = ''.join(a)
   ...:
100000 loops, best of 3: 11.8 µs per loop

join 은 누적 방식 에 대해 약 5 배 정도 향상 되 었 다.
9. 적당 한 포맷 문자 선택
s1, s2 = 'ax', 'bx'
%timeit -n 100000 'abc%s%s' % (s1, s2)
%timeit -n 100000 'abc{0}{1}'.format(s1, s2)
%timeit -n 100000 'abc' + s1 + s2
100000 loops, best of 3: 183 ns per loop
100000 loops, best of 3: 169 ns per loop
100000 loops, best of 3: 103 ns per loop

세 가지 상황 중 % 방식 이 가장 느 리 지만 세 사람의 차 이 는 크 지 않다 (모두 매우 빠르다).(개인 적 으로 는% 가 독성 이 가장 좋다 고 생각한다)
10. 중간 변 수 를 빌려 두 변수의 값 을 교환 하지 않 는 다.
In [3]: %%timeit -n 10000
    a,b=1,2
   ....: c=a;a=b;b=c;
   ....:
10000 loops, best of 3: 172 ns per loop

In [4]: %%timeit -n 10000
a,b=1,2
a,b=b,a
   ....:
10000 loops, best of 3: 86 ns per loop

c = a 가 아 닌 a, b = b 를 사용 합 니 다.a=b;b=c;a, b 의 값 을 교환 하면 1 배 이상 빠 를 수 있 습 니 다.
11. if is 사용
a = range(10000)
%timeit -n 100 [i for i in a if i == True]
%timeit -n 100 [i for i in a if i is True]
100 loops, best of 3: 531 µs per loop
100 loops, best of 3: 362 µs per loop

if is True 를 사용 하면 if = = True 보다 배가 빠르다.
12. 사용 등급 비교 x < y < z
x, y, z = 1,2,3
%timeit -n 1000000 if x < y < z:pass
%timeit -n 1000000 if x < y and y < z:pass
1000000 loops, best of 3: 101 ns per loop
1000000 loops, best of 3: 121 ns per loop

x < y < z 효율 이 약간 높 고 가 독성 이 더 좋다.
13. while 1 은 while True 보다 빠르다.
def while_1():
    n = 100000
    while 1:
        n -= 1
        if n <= 0: break
def while_true():
    n = 100000
    while True:
        n -= 1
        if n <= 0: break    

m, n = 1000000, 1000000 
%timeit -n 100 while_1()
%timeit -n 100 while_true()
100 loops, best of 3: 3.69 ms per loop
100 loops, best of 3: 5.61 ms per loop

while 1 은 while true 보다 훨씬 빠 릅 니 다. 그 이 유 는 python 2. x 에서 True 는 키워드 가 아 닌 전역 변수 이기 때 문 입 니 다.
14. pow 대신 * * 사용
%timeit -n 10000 c = pow(2,20)
%timeit -n 10000 c = 2**20
10000 loops, best of 3: 284 ns per loop
10000 loops, best of 3: 16.9 ns per loop

* * 10 배 이상 빠르다!
15. cProfile, cStringIO, cPickle 등 을 사용 하여 c 로 같은 기능 (각각 profile, StringIO, pickle 에 대응) 을 수행 하 는 가방
import cPickle
import pickle
a = range(10000)
%timeit -n 100 x = cPickle.dumps(a)
%timeit -n 100 x = pickle.dumps(a)
100 loops, best of 3: 1.58 ms per loop
100 loops, best of 3: 17 ms per loop

c 로 이 루어 진 가방, 속도 가 10 배 이상 빨 라 요!
16. 최고의 반 직렬 화 방식 을 사용한다.
다음은 eval, cPickle, json 방식 의 세 가지 문자열 에 대한 역 직렬 화 효율 을 비교 했다.
import json
import cPickle
a = range(10000)
s1 = str(a)
s2 = cPickle.dumps(a)
s3 = json.dumps(a)
%timeit -n 100 x = eval(s1)
%timeit -n 100 x = cPickle.loads(s2)
%timeit -n 100 x = json.loads(s3)
100 loops, best of 3: 16.8 ms per loop
100 loops, best of 3: 2.02 ms per loop
100 loops, best of 3: 798 µs per loop

이 를 통 해 알 수 있 듯 이 제 이 슨 은 cPickle 보다 3 배 가까이 빠 르 고 eval 보다 20 배 이상 빠르다.
17. C 확장 사용 (확장)
현재 주로 CPython (python 에서 가장 흔히 볼 수 있 는 실현 방식) 원생 API, ctypes, Cython, cffi 세 가지 방식 이 있 습 니 다. 이들 의 역할 은 Python 프로그램 이 C 로 컴 파일 된 동적 링크 라 이브 러 리 를 호출 할 수 있 도록 하 는 것 입 니 다. 그 특징 은 CPython 원생 API: Python. h 헤더 파일 을 도입 하여 해당 하 는 C 프로그램 에서 Python 의 데이터 구 조 를 직접 사용 할 수 있 습 니 다.실현 과정 은 상대 적 으로 번 거 롭 지만 비교적 큰 적용 범위 가 있다.ctypes: 보통 패키지 (wrap) C 프로그램 에 사용 되 며, 순수 Python 프로그램 에서 동적 링크 라 이브 러 리 (Windows 의 dll 또는 Unix 의 so 파일) 의 함 수 를 호출 합 니 다.python 에서 이미 C 라 이브 러 리 를 사용 하려 면 ctypes 를 사용 하 는 것 이 좋 습 니 다. 일부 기준 테스트 에서 python 2 + ctypes 는 성능 이 가장 좋 은 방법 입 니 다.Cython: Cython 은 CPython 의 초 집합 으로 C 확장 과정 을 간소화 하 는 데 사 용 됩 니 다.Cython 의 장점 은 문법 이 간결 하고 numpy 등 대량의 C 확장 을 포함 하 는 라 이브 러 리 를 잘 호 환 할 수 있다 는 것 이다.Cython 의 경우 장면 은 일반적으로 프로젝트 중의 특정한 알고리즘 이나 과정 에 대한 최적화 이다.어떤 테스트 에 서 는 수백 배의 성능 을 향상 시 킬 수 있다.cffi: cffi 는 ctypes 가 pypy (아래 글 참조) 에서 이 루어 지 는 것 입 니 다. 같은 진도 CPython 을 호 환 합 니 다.cffi 는 python 에서 C 라 이브 러 리 를 사용 하 는 방식 을 제공 합 니 다. python 코드 에서 C 코드 를 직접 작성 할 수 있 고 기 존 C 라 이브 러 리 로 연결 할 수 있 습 니 다.이러한 최적화 방식 을 사용 하 는 것 은 일반적으로 기 존 프로젝트 의 성능 병목 모듈 에 대한 최적화 로 기 존 프로젝트 를 소량 변경 한 상황 에서 전체 프로그램의 운행 효율 을 대폭 높 일 수 있다.
18. 병렬 프로 그래 밍
GIL 의 존재 로 파 이 썬 은 다 핵 CPU 의 장점 을 충분히 활용 하기 어 려 운 상황 이다.단, 내 장 된 모듈 multiprocessing 을 통 해 다음 과 같은 몇 가지 병행 모드 를 실현 할 수 있 습 니 다. 다 중 프로 세 스: CPU 밀집 형 프로그램 에 대해 서 는 multiprocessing 의 process, Pool 등 포 장 된 클래스 를 사용 하여 다 중 프로 세 스 를 통 해 병행 계산 할 수 있 습 니 다.그러나 프로 세 스 의 통신 원가 가 비교적 크기 때문에 프로 세 스 간 에 대량의 데이터 상호작용 이 필요 한 프로그램의 효율 이 반드시 크게 향상 되 는 것 은 아니다.다 중 스 레 드: IO 밀집 형 프로그램 에 대해 multiprocessing. dummy 모듈 은 multiprocessing 의 인터페이스 로 threading 을 밀봉 하여 다 중 스 레 드 프로 그래 밍 도 매우 쉬 워 집 니 다 (예 를 들 어 Pool 의 map 인 터 페 이 스 를 사용 할 수 있 고 간결 하고 효율 적 입 니 다).분산 식: multiprocessing 의 Managers 류 는 서로 다른 프로 세 스 에서 데 이 터 를 공유 할 수 있 는 방식 을 제공 합 니 다. 이 를 바탕 으로 분포 식 프로그램 을 열 수 있 습 니 다.서로 다른 업무 장면 은 그 중의 하나 또는 몇 가지 조합 을 선택 하여 프로그램 성능 의 최적화 를 실현 할 수 있다.
19. 최종 대 살 기: PyPy
PyPy 는 RPython (CPython 의 부분 집합) 으로 구 현 된 Python 으로, 홈 페이지 의 기준 테스트 데이터 에 따 르 면 CPython 이 구현 한 Python 보다 6 배 이상 빠르다.빠 른 이 유 는 Just - in - Time (JIT) 컴 파일 러, 즉 동적 컴 파일 러 를 사 용 했 기 때 문 입 니 다. 정적 컴 파일 러 (예 를 들 어 gcc, javac 등) 와 달리 프로그램 이 실행 되 는 과정의 데 이 터 를 이용 하여 최적화 되 었 습 니 다.역사적 인 이유 로 현재 pypy 에는 GIL 이 남아 있 지만 진행 중인 STM 프로젝트 는 PyPy 를 GIL 이 없 는 Python 으로 만 들 려 고 시도 하고 있 습 니 다.python 프로그램 에 C 확장 (cffi 가 아 닌 방식) 이 포함 되 어 있 으 면 JIT 의 최적화 효 과 는 크게 할인 되 고 심지어 CPython 보다 느리다 (Numpy 보다).그래서 PyPy 에 서 는 순수 Python 이나 cffi 로 확장 하 는 것 이 좋 습 니 다.STM, Numpy 등 프로젝트 가 완 선 됨 에 따라 PyPy 가 CPython 을 대체 할 것 이 라 고 믿 습 니 다.
20. 성능 분석 도구 사용
위 에서 ipython 에서 사용 한 timeit 모듈 외 에 도 cProfile 이 있 습 니 다.cProfile 의 사용 방식 도 매우 간단 합 니 다. python - m cProfile filename. py, filename. py 는 프로그램 을 실행 할 파일 이름 입 니 다. 표준 출력 에서 모든 함수 가 호출 된 횟수 와 실행 시간 을 볼 수 있 고 프로그램의 성능 병목 을 찾 은 다음 에 목적 성 있 게 최적화 할 수 있 습 니 다.
PyChina 는 JetBrain (PyCharm 을 만 든 회사) 과 함께 베 이 징 에서 Python 살롱 행 사 를 개최한다.
시간: 11 월 26 일 저녁 19: 00 - 21: 00
장소: 과학기술 사 북 신교 베 이 징 시 동성 구 동사 북 거리 107 호 코 이 린 빌딩 B 좌 107 실 (근 북 신교 지하철역)
이번 행사 에 참가 신청 을 하신 것 을 환영 합 니 다. 특히 자원 봉사자 가 이번 행 사 를 조직 하 는 것 을 도와 야 합 니 다.
자세 한 내용 은 여 기 를 클릭 하 세 요.

좋은 웹페이지 즐겨찾기