개발/Python

[Python] time 모듈을 활용해서 성능 측정 하기 (feat. factorial)

growing-dev 2023. 2. 19. 22:24

Python에서 제공하는 time 모듈을 활용해서 성능을 측정해 보고 factorial에 대해서 공부해 본다.

 

 

time 모듈을 활용해서 성능 측정 하기 (feat. factorial)

 

Python time 모듈 활용방법

python에서 제공하는 time 모듈은 시간에 관련된 여러 가지 기능을 제공한다. 그중에서 쉽게 사용해 볼 수 있는 것이 현재 시간을 측정하는 것이고 이를 통해 내가 구현한 함수나 기능의 수행시간이 측정 가능해서 간단하게 성능 측정이 가능하다.

사용방법은 import 한 뒤 time.time으로 쉽게 사용할 수 있다. 또한 현재 연월일시분초 등을 알고 싶을 때는 localtime이라는 것을 활용한다.

 

import time

if __name__ == '__main__':
    print(time.time())
    print(time.localtime())

실행 결과

 

여기서 성능 측정을 위해서는 시작하는 시점에 time.time()을 호출해서 현재 시간을 저장한 다음 끝나는 시점에 다시 한번 time.time()을 호출해서 두 시간의 차이를 통해 수행시간을 측정할 수 있다. math 모듈의 factorial을 활용해서 10의 factorial을 구하여 출력해 보았다. 정답은 잘 나왔고 시간이 매우 짧게 걸린다.

 

import time
import math

if __name__ == '__main__':
    start = time.time()
    result = math.factorial(10)
    end = time.time()
    print("factorial result : ", result, '\n', "elapsed time : ",  end-start)

10 factorial

 

이번에는 그냥 100만이라는 큰 수를 돌려서 결과는 버리고 시간을 측정해 보았다. 대략 5.5초 정도 걸렸다.

import time
import math

if __name__ == '__main__':
    start = time.time()
    math.factorial(1000000)
    end = time.time()
    print("elapsed time : ",  end-start)

 

100만 factorial 시간

 

factorial에 대해서

이것에 대해서 공부해 보다가 문득 factorial에 대해 궁금해져서 math에서 활용하는 대신 직접 구현해 보았다.

import time

def my_factorial(n):
    if n == 0:
        return 1
    else:
        return n * my_factorial(n - 1)


if __name__ == '__main__':
    start = time.time()
    result = my_factorial(10)
    end = time.time()
    print("result : ", result, '\n', "elapsed time : ",  end-start)

 

my_factorial(10)

 

에러 발생

값을 늘려서 테스트해 보는 도중에 1000 정도의 factorial부터 maximum recursion depth error가 발생하였다.

검색해 보니 python에서는 stack overflow를 방지하고자 recursion을 제한한다고 한다.

에러 발생

현재 내 recursion 제한 횟수를 알고 싶으면 아래 코드로 확인하면 된다. 내 경우는 1000이 나왔다. 즉 1000회 이상 재귀가 호출되면 에러를 발생시킨다고 한다.

import sys
print("recursion limit : ", sys.getrecursionlimit(), '\n')

limit 개수

 

에러 해결

굳이 factorial과 같은 recursion이 필요하다고 하면 재귀적으로 호출하는 것이 아니라 아래처럼 while을 통해서 함수 자체에서 계산하는 방식으로 해결해야 한다.  하지만 이렇게 해서 성능을 측정해 보니 10만부터 2초가 걸리고 100만은 30초 이상 기다려도 결과가 나오지 않았다. 즉 math 모듈을 사용하는 것에 비해 성능이 매우 떨어지게 된다.

import time


def my_factorial(n):
    val = 1

    while n > 0:
        val *= n
        n -= 1

    return val


if __name__ == '__main__':
    start = time.time()
    my_factorial(1000000)
    end = time.time()

    print("elapsed time : ", end - start)

 

math와 내가 직접 구현한 factorial 비교하여 확인하는 코드이다. 10만의 n을 입력하자 200배 이상 성능 차이가 발생했다. 아마 n이 커질수록 더 차이가 커질 것으로 예상된다. 즉 math 모듈에서 제공하는 factorial이 훨씬 성능이 빨랐다.

import time
import math

def my_factorial(n):
    val = 1

    while n > 0:
        val *= n
        n -= 1

    return val


if __name__ == '__main__':
    start = time.time()
    my_factorial(100000)
    end = time.time()
    print("my function elapsed time : ", end - start)

    start = time.time()
    math.factorial(100000)
    end = time.time()
    print("time function elapsed time : ", end - start)

성능 비교

 

 

결론

time 모듈을 사용해서 성능 측정하는 방법을 알아보았고 더불어서 factorial과 recursion limit error에 대해서 확인해 볼 수 있었다. C/C++에서는 라이브러리보다는 직접 구현하는 게 대체로 성능이 빠를 수밖에 없는 것 같다. 하지만 python에서는 애매하게 구현하는 것보다는 공식 제공하는 최적화된 라이브러리를 활용하는 것이 오히려 성능에 좋을 수도 있다고 알 수 있는 계기가 되었다.

반응형