컴퓨터 공부/파이썬 공부

고성능 파이썬(2)

냥냥펀치데스 2021. 6. 27. 17:02
728x90

프로파일링으로 병목지점찾기

시스템 정확히 파악하기

어느부분이 느린지, 어느 RAM을 많이 쓰는지,

디스크 I/0 나 네트워크 I/O 를 과도하게 발생시키는 부분이 어디인지 확인

1. 시간측정(time.time(), decorator)

2. 어떤 함수가 오래걸리는지 측정(cProfile -> snakeviz 로 시각화)

3. 선택 함수를 한줄씩 프로파일링(line_profiler)

4. CPU에서 실행된 명령의수, CPU 캐시가 효율적으로 활용되었는지 검사(perf stat)

5. RAM 사용량 측정(memory_profiler, psutil)

파이썬은 문장을 왼쪽에서 오른쪽으로 검사하며 기회주의적이기 때문에 빨리 긑나는 검사를 등식의 왼쪽에 두는게 좋다

성능 향상을 위한 가설설정

1. 쉽게 검증할수 있는 가설 성립

2. 그 가설을 검증할수 있도록 코드수정, 한번에 한가지만 검사

3. 결론을 뒷받침할 근거 수집

접근 방법에 따라 달라지는 복잡도

def fn_expressive(upper = 1_000_000) :
    total = 0
    for n in range(upper):
        total += n
    return total

def fn_terse(upper = 1_000_000):
    return sum(range(upper))

%timeit fn_expressive()
%timeit fn_terse() # 두배 더 빠르게 작동 => 더 적은 파이썬 바이트코드 생성

바이트코드 명령어 확인 (fn_expressive vs fn_terse)

fn_expressive : 1. 지역변수 2,
                    2. for 문 : 리스트 순회 , StopIteration 예외 발생 검사, n 의 타입 검사하는 total.__add__ 호출

fn_terse :  C 리스트 표현식 함수 호출로 바로 최종 결과 생성 but still 객체 타입 검사

최적화 중에 단위 테스트하기

coverage.py 를 통하여 테스트한 부분 안한 부분 파악

line_profiler 나 memory_profiler 에서 @profile 을 사용하면 단위테스트에서 NameError 발생

@profile 데커레이터를 지역 네임 스페이스에 추가 안하기 때문 -> 아무일도 하지 않는 @profile 데커레이터 이용

import time

if 'line_profiler' not in dir() and 'profile' not in dir() :
    def profile(func):
        return func

def test_some_fn():
    assert some_fn(2) == 4
    assert some_fn(1) == 1
    assert some_fn(-1) == 1

@profile
def some_fn(useful_input):
    time.sleep(1)
    return useful_input ** 2

if __name__ == "__main__" :
    print(f"Example call 'some_fn(2)' == {some_fn(2)}")