바운드는 크게 두가지 cpu, i/o 바운드로 나뉜다.
CPU 바운드
프로그램이 실행될 때 실행 속도가 cpu 속도에 의해 제한됨을 의미한다. cpu가 계속 계산을하기 때문에 cpu에서 계산이 끝날때 까지 프로그램이 잠시 멈춘다.
I/O 바운드
I,O는 각각 input과 output을 의미하며, 프로그램이 실행될 때 실행 속도가 I/O에 의해 제한됨을 의미한다. 사용자가 숫자를 입력하는 경우 뿐만 아니라, 컴퓨터끼리의 통신을 할 때에도 I/O 바운드가 발생
# 예시코드 1. 사용자 I/O
import time
def io_bound_func():
print("값을 입력해주세요.")
input_value = input()
return int(input_value)*100
if __name__ == "__main__":
start_time = time.time()
result = io_bound_func()
end_time = time.time()
print(result)
print("실행시간:{} ".format(end_time - start_time))
#예시코드 2. 서버 통신시 I/O 바운드
import requests
def io_bound_func():
result = requests.get("https://google.com")
return result
if __name__ == "__main__":
start_time = time.time()
result = io_bound_func()
end_time = time.time()
print(result)
print("실행시간:{} ".format(end_time - start_time))
바운드에 의해 코드가 멈추게 되는 현상이 일어나는 것
def io_bound_func():
result = requests.get("https://google.com")
return result
if __name__ == "__main__":
start_time = time.time()
for i in range(10):
result = io_bound_func()
end_time = time.time()
print(result)
print("실행시간:{} ".format(end_time - start_time))
위 코드를 보면 총 10번의 블로킹이 발생한 것을 볼 수 있다.
- 동기(Snyc): 직렬로 작업을 수행하는 것, 예를 들어 코드가 동기적으로 동작한다는 것은 코드가 반드시 작성한 순서 그대로 실행 된다는 것이다.
import time
def delivery(name, mealtime):
print(f"{name}에게 배달 완료.")
time.sleep(mealtime)
print(f"{name} 식사완료, {mealtime}시간 소요...")
print(f"{name} 그릇 수거 완료")
def main():
delivery("A",3)
delivery("B",3)
delivery("C",3)
if __name__ == "__main__":
srt = time.time()
main()
end = time.time()
print(f"소요시간: {end -srt}")
- 비동기(Asnyc): 앞선 예와 반대로 비동기적으로 수행한다는 것은 코드가 작성된 순서대로 수행 되는 것이 아닌 것
- 코드예시
import asyncio
async def delivery(name, mealtime):
print(f"{name}에게 배달 완료.")
# await time.time()을 사용하면 time은 awaitable함수가 아니므로 에러가 발생
await asyncio.sleep(mealtime) #await을 기점으로 다른 코루틴 함수로 넘어간다
print(f"{name} 식사완료, {mealtime}시간 소요...")
print(f"{name} 그릇 수거 완료")
async def main():
#asyncio.gather: 동시에 태스크 실행 (동시성) - 단 병렬로 처리 되는 것은 아님
await asyncio.gather(
delivery("A",5),
delivery("B",3),
delivery("C",2),
)
if __name__ == "__main__":
srt = time.time()
asyncio.run(main()) #asyncio.run(): 코루틴 함수를 실행하는 함수
end = time.time()
print(f"소요시간: {end -srt}")
동기방식
delivery_func(A)가 종료되기 전까지 다음 함수를 호출하지 않고 기다린다.
비동기 방식
서브루틴의 일반화 된 상태
다양한 진입점과 다양한 탈출 점이 있는 루틴 아래 예시코드를 보면 delivery 루트는 await과 return으로 해단 루틴을 탈출할 수 있다. 물론 await기점으로 다시 진입 가능
파이썬 비동기 함수는 코루틴 함수로 만들 수 있다.
async def delivery(name, mealtime):
print(f"{name}에게 배달 완료.")
await asyncio.sleep(mealtime)
print(f"{name} 식사완료, {mealtime}시간 소요...")
print(f"{name} 그릇 수거 완료")
return
async def main():
await asyncio.gather(
delivery("A",5),
delivery("B",3),
delivery("C",2),
)
main()
"""
코루틴 활용
네이버 웹 데이터 가져오기
"""
import requests
import time
def fetcher(session, url):
with session.get(url) as response:
return response.text
def main():
urls = ["https://naver.com","https://google.com","https://instagram.com"]*10
with requests.Session() as session:
result = [fetcher(session,url) for url in urls]
print(result)
if __name__ == "__main__":
start = time.time()
main()
end = time.time()
print(f"소요시간: {end- start} 초") #10.17
소요시간: 10.467732906341553 초
import requests
import time
import aiohttp
import asyncio
async def fetcher(session, url):
async with session.get(url) as response:
return await response.text() #response는 awaitable 객체
async def main():
urls = ["https://naver.com","https://google.com","https://instagram.com"]*10
async with aiohttp.ClientSession() as session:
result = await asyncio.gather(*[fetcher(session,url) for url in urls]) #result에는 urls 내부의 주소의 정보가 순서대로 담겨있다
print(len(result))
if __name__ == "__main__":
start = time.time()
asyncio.run(main())
end = time.time()
print(f"소요시간: {end- start} 초") #0.84초
소요시간: 0.8458449840545654 초