05의 개발 계발

[TIL] 230322 [python] 함수 실행 시간 측정 본문

TIL

[TIL] 230322 [python] 함수 실행 시간 측정

생각하는 코댕이 2023. 3. 22. 16:46
728x90

TIL 학습목표

  • python 에서 함수 실행 시간을 측정하는 방법을 안다.
  • sort 함수의 type과 사용법을 안다.

1) 문제점 (Problem)

지난 TIL에서 알고리즘 문제를 풀고, 코드를 축약해보았다.
단순히  컴프리헨션을 사용하여 축약한 것이라 알고리즘이 변화한 것 같진 않지만,
그래도 축약했던 함수의 성능을 확인해보고 싶어졌다.

그래서 time() 을 사용하여 함수 실행 시간을 측정을 해보려 했다.


2) 시도해본 것들 (Try)

▶함수 실행 시간 측정 코드 짜기

한 파일에서 실행하기 위해 기존함수를  solution1 축약함수를 solution2 라고 정의했다.

더보기

기존함수==(solution1)  / 축약함수==(solution2)

import time

answers=[1,2,3,4,5]

def solution1(answers):
    
    #수포자 1,2,3을 a,b,c로 표현 
    a = [1,2,3,4,5]*2000         	#10,000개의 배열 만들기 5*2000
    b = [2,1,2,3,2,4,2,5]*1250		#10,000개의 배열 만들기 8*1250
    c = [3,3,1,1,2,2,4,4,5,5]*1000	#10,000개의 배열 만들기 10*1000
    count_a=0  #수포자1의 맞춘 갯수
    count_b=0  #수포자2의 맞춘 갯수
    count_c=0  #수포자3의 맞춘 갯수
    
    for i, answer in enumerate(answers):   #반복문 for 구문. 인덱스(i)로 순차배열 i는 0부터 시작한다
        if answer == a[i]:                 #answers의 요소와 a의 요소를 비교하고, 맞을 경우 
            count_a += 1		   #count_a에 1씩 더해준다
        if answer == b[i]:
            count_b += 1     
        if answer == c[i]:
            count_c += 1
    total=[count_a,count_b,count_c]		#수포자1,2,3의 맞춘 개수를 total이라는 리스트로 저장
    result=[]					#문제에서 원하는 결과를 result 라고 표현
    for i in range(0,3):	#1,2,3 세 명이니 3번만 반복
        if max(total) == total[i]:	#공동이든 단독이든, 3명의 결과 중 가장 많이 맞춘경우
            result.append(i+1)		#수포자 번호에 맞는 번호를 리스트에 추가해라
    result.sort()		#리스트를 오름차순 정렬해라
    return result

def solution2(answers):
    spj1 = [1,2,3,4,5]*2000             #수포자1 
    spj2 = [2,1,2,3,2,4,2,5]*1250       #수포자2
    spj3 = [3,3,1,1,2,2,4,4,5,5]*1000   #수포자3
    spj = [spj1,spj2,spj3]      #수포자리스트
    score=[0,0,0]   #수포자 점수 리스트
    
    # 축약 코드 리스트 컴프리헨션
    score=[sum(score[j] + 1 for i,answer in enumerate(answers) if answer == spj[j][i]) for j in range(0,3)]
            #맞춘 문제수를 score에 리스트로 저장
    No1=[k+1 for k in range(0,3) if max(score) == score[k]]
            #맞춘 문제수를 비교하여 최고점의 수포자번호를 No1에 리스트로 저장 
    No1.sort() #No1 내림차순 정렬
    return No1

 

# 기존함수 동작시간
start_time1 = time.time()	#시작시간1 저장

solution1(answers)			#기존함수동작

end_time1 = time.time()		#종료시간1 저장

# 축약함수 동작시간
start_time2 = time.time() 	#시작시간2 저장

solution2(answers)			#축약함수동작

end_time2 = time.time()		#종료시간2 저장

# 코드가 종료된 시간 - 코드가 시작된 시간으로 실행 시간 구하기 (단위 : 초)
print(f"기존코드 실행 시간 : {end_time1-start_time1:.4f} 초") 
print(f"축약코드 실행 시간 : {end_time2-start_time2:.4f} 초")

▶함수 실행 시간 측정하기

첫 번째 시도 : 입력값 5개 짜리 리스트 + 시간 소수 4째자리까지 출력

answers=[1,2,3,4,5]
# 코드가 종료된 시간 - 코드가 시작된 시간으로 실행 시간 구하기 (단위 : 초)
print(f"기존코드 실행 시간 : {end_time1-start_time1:.4f} 초") #소수점 4자리 까지
print(f"축약코드 실행 시간 : {end_time2-start_time2:.4f} 초") #소수점 4자리 까지

 

결과 : 비교가 불가하다. 자릿수를 늘려보자.


두 번째 시도: 입력값 5개 짜리 리스트 + 시간 소수 400째자리까지 출력

answers=[1,2,3,4,5]
# 코드가 종료된 시간 - 코드가 시작된 시간으로 실행 시간 구하기 (단위 : 초)
print(f"기존코드 실행 시간 : {end_time1-start_time1:.400f} 초") #소수점 400자리 까지
print(f"축약코드 실행 시간 : {end_time2-start_time2:.400f} 초") #소수점 400자리 까지

 



결과
 : 비교가 불가하다. 입력값을 바꿔보자


세 번째 시도: 입력값 1,000,000개 짜리 리스트 + 시간 소수 4째자리까지 출력

answers = [1, 2, 3, 4, 5] * 200000  # 100만개의 요소를 가진 리스트
    a = [1, 2, 3, 4, 5] * 200000  # 1,000,000개의 배열 만들기 5*200000
    b = [2, 1, 2, 3, 2, 4, 2, 5] * 125000  # 1,000,000개의 배열 만들기 8*125000
    c = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5] * 100000  # 1,000,000개의 배열 만들기 10*100000

 

결과 : 유의미한 차이가 나타났다. 비율을 내보자!

코드추가!

ratio = (end_time1 - start_time1) / (end_time2 - start_time2)

print(f"기존코드가 축약코드보다 {1/ratio:.4f}배 빠릅니다.")

 

<고찰>
*CPU의 성능에 따라 실행시마다 차이가 존재했다. 1.6~2.2배 정도 차이가 났으며, 기존코드가 더 빠르게 구동됐다.
* 같은 알고리즘을 축약했다고 생각했는데, 차이를 내는 요소가 있었던것 같다.
* 입력수가 작을 때는 CPU의 순간 성능이 큰 변수로 작용되어 기존코드가 더 느리게 실행되기도 했다.

3) 해결 (Solution)

Time() 과 입력값을 크게함으로, 함수 성능 차 확인!

▶전체코드

더보기

 

import time

answers = [1, 2, 3, 4, 5] * 200000  # 100만개의 요소를 가진 리스트


def solution1(answers):

    # 수포자 1,2,3을 a,b,c로 표현
    a = [1, 2, 3, 4, 5] * 200000  # 1,000,000개의 배열 만들기 5*200000
    b = [2, 1, 2, 3, 2, 4, 2, 5] * 125000  # 1,000,000개의 배열 만들기 8*125000
    c = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5] * 100000  # 1,000,000개의 배열 만들기 10*100000
    count_a = 0  # 수포자1의 맞춘 갯수
    count_b = 0  # 수포자2의 맞춘 갯수
    count_c = 0  # 수포자3의 맞춘 갯수

    for i, answer in enumerate(answers):  # 반복문 for 구문. 인덱스(i)로 순차배열 i는 0부터 시작한다
        if answer == a[i]:  # answers의 요소와 a의 요소를 비교하고, 맞을 경우
            count_a += 1  # count_a에 1씩 더해준다
        if answer == b[i]:
            count_b += 1
        if answer == c[i]:
            count_c += 1
    total = [count_a, count_b, count_c]  # 수포자1,2,3의 맞춘 개수를 total이라는 리스트로 저장
    result = []  # 문제에서 원하는 결과를 result 라고 표현
    for i in range(0, 3):  # 1,2,3 세 명이니 3번만 반복
        if max(total) == total[i]:  # 공동이든 단독이든, 3명의 결과 중 가장 많이 맞춘경우
            result.append(i + 1)  # 수포자 번호에 맞는 번호를 리스트에 추가해라
    result.sort()  # 리스트를 오름차순 정렬해라
    return result


def solution2(answers):
    spj1 = [1, 2, 3, 4, 5] * 200000  # 수포자1
    spj2 = [2, 1, 2, 3, 2, 4, 2, 5] * 125000  # 수포자2
    spj3 = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5] * 100000  # 수포자3
    spj = [spj1, spj2, spj3]  # 수포자리스트
    score = [0, 0, 0]  # 수포자 점수 리스트

    # 축약 코드 리스트 컴프리헨션
    score = [
        sum(score[j] + 1 for i, answer in enumerate(answers) if answer == spj[j][i])
        for j in range(0, 3)
    ]
    # 맞춘 문제수를 score에 리스트로 저장
    No1 = [k + 1 for k in range(0, 3) if max(score) == score[k]]
    # 맞춘 문제수를 비교하여 최고점의 수포자번호를 No1에 리스트로 저장
    No1.sort()  # No1 내림차순 정렬
    return No1


# 기존함수 동작시간
start_time1 = time.time()	#시작시간1 저장

solution1(answers)			#기존함수동작

end_time1 = time.time()		#종료시간1 저장

# 축약함수 동작시간
start_time2 = time.time() 	#시작시간2 저장

solution2(answers)			#축약함수동작

end_time2 = time.time()		#종료시간2 저장

# 코드가 종료된 시간 - 코드가 시작된 시간으로 실행 시간 구하기 (단위 : 초)
print(f"기존코드 실행 시간 : {end_time1-start_time1:.4f} 초") 
print(f"축약코드 실행 시간 : {end_time2-start_time2:.4f} 초")

ratio = (end_time1 - start_time1) / (end_time2 - start_time2)

print(f"기존코드가 축약코드보다 {1/ratio:.4f}배 빠릅니다.")

 


4) 알게 된 점  (Learnd)

Time() 을 활용하여 함수 실행 시간을 측정하는 법을 익혔다.
단순한 함수일수록 실행시간이 매우 짧아 성능을 비교하기 힘들었다.
하나의 방법으로는, 입력값을 매우 크게하면 비교할 만큼 실행시간이 측정된다.

또 issue로는 입력값이 작을 때는, 실행 시 마다 기존코드가 빠를 때도, 축약코드가 빠를 때도 있었다.
구글링을 통해 원인을 찾아보니 순간순간의 CPU 성능에 따라 오차가 발생하는 부분이라, 이를 무시하기 위해서는 확실한 시간차가 존재하도록 (큰 입력값or 큰 반복수) 를 사용해야한다고 한다.

결과적으로 기존코드가 더 빠르기도하고, 코드리뷰 시 가독성 면에서도 좋다.

[결론]
성능면에서도 가독성면에서도, 무조건 짧다고 좋은 코딩이 아니다..!!   
728x90