[Python] 속도 개선 -Loop편

반응형
    반응형

    Loop의 퍼포먼스 개선입니다.

    순수 파이썬, numpy, numda와 cython으로 평균을 구하는 루프를 만들어 시간 비교를 해보겠습니다.

     

    시간 측정을 위해서 %time%timeit을 쓰려고 합니다.

    %time은 한 줄의 코드 실행시간을 측정할 때 사용할 수 있고

    %timeit은 코드를 여러 번 시도를 한 후 시간 측정을 해서 오차범위를 알려줍니다.

     

    Python

    파이썬의 내장함수로만 평균을 구해보겠습니다.

    %time은 코드 한 줄로 해야되니 평균을 구하는 함수를 만들어서 진행을 하겠습니다.

     

    import random
    
    def average_python(n):
        s = 0
        for i in range(n):
            s += random.random()
        return s/n
        
    n= 1000000
    %time average_python(n)

     

    %timeit을 쓰면 다음과 같습니다.

     

    %timeit average_python(n)

     

    list comprehension 으로도 평균을 만들 수 있으니 시간측정을 해보겠습니다.

     

    %time sum([random.random() for _ in range(n)])/n

     

    위에서 본 것처럼 약 164 밀리초정도 시간이 소요됩니다. list comprehension을 사용하면 180초정도 되니 약간 느리긴하네요.

     

    Numpy

    느린 파이썬을 numpy로 아주 많이 개선을 했다고 알고 있는데 실제로 그런지 확인해보겠습니다. 

    numpy로 벡터화를 해서 계산하는 건데요.

    인공지능이나 머신러닝을 쓸 때 numpy로 많이 하는데 그 이유를 조금은 알 수 있을겁니다.

     

    import numpy as np
    
    def average_np(n):
        s = np.random.random(n)
        return s.mean()
        
    %time average_np(n)

     

    %timeit 으로 좀 더 정확히 측정을 해보겠습니다.

     

    %timeit average_np(n)

     

    약 16.6 밀리초로 파이썬만 사용하는 것보다 10배정도 빨라진 걸 볼 수 있습니다. 굉장히 빨라졌습니다.

     

     

    Numba

    이번에는 캐싱을 해서 불러오는 형식으로 코드를 읽어내는 numba를 이용해 보겠습니다.

    맨처음에 만든 average_python 함수를 캐싱을 해서 사용합니다.

     

    import numba
    
    average_nb = numba.jit(average_python)
    
    %time average_nb(n)

     

    처음 시도할 때 114 밀리초가 나옵니다. numpy에 비하면 빠르진 않습니다.

    두번째부터는 캐싱을 해놨기 때문에 속도가 빨라집니다.

    다시 시도하겠습니다.

     

    %time average_nb(n)

     

    두번째 시도에 8 밀리초가 나옵니다.

    numpy를 사용하지 않았음에도 8밀리초로 엄청나게 빠른 속도로 계산을 해내게 됩니다.

     

    그렇다고 numpy를 넣는다고 무조건 빨라지는 건 아닙니다. 

     

    average_nb_np = numba.jit(average_np)
    %time average_nb_np(n)

     

    %time average_nb_np(n)

     

    큰 차이는 없지만 numpy를 넣으면 약간 늦어지는 경향이 있긴 합니다.

     

     

    Cython

    이번엔 python 기반으로 C 루틴을 호출하는 Cython을 사용해 보겠습니다.

    Cython은 컴퓨터 내에 C나 C++이 설치되어 있어야 합니다. 설치가 되어있지 않으면 오류가 나옵니다.

    Visual Studio를 통해서 C/C++ 번들을 설치하시면 Cython 사용에 문제가 없으니 오류가 나오면 번들 설치를 해보셨으면 합니다.

     

    %load_ext Cython
    
    %%cython -a
    import random
    def average_cy(int n):
        cdef int i
        cdef float s=0
        for i in range(n):
            s += random.random()
        return s/n
        
    %time average_cy(n)

     

    Cython을 사용하면 52.8 밀리초정도 걸립니다. 파이썬보다는 확실히 빠른 속도를 낼 수 있습니다.

     

     

    결론

    loop를 사용할 때에 파이썬 내장함수, numpy, numba, cython을 이용해서  퍼포먼스 성능을 살펴보았는데 

    파이썬 내장함수 < cython < numpy < numba 순으로 성능이 좋아지는 걸 알 수 있었습니다.

    물론 numba는 캐싱을 쓰는 방법이라서 한번 돌려놓고 다시 돌릴 때부터 성능이 발휘되는 것이라서 완벽하다고 볼 수는 없지만 반복적으로 쓰는 함수가 있다면 numba를 써보는 것도 속도 개선을 하는데 도움이 되지 않을까 싶습니다.

    반복적으로 쓰는 게 아닌 일반적인 계산이라면 numpy를 쓰는게 가장 좋아보이는 것 같습니다. 

    loop일 때만 그런지는 알 수가 없어서 다른 것도 해볼까 합니다.

     

     

    관련 포스팅

    [Python/기초] - [Python] 퍼포먼스 개선 방법

    댓글

    Designed by JB FACTORY

    ....