요즘들어 대형언어모델 추론 API 서버를 활용해 코딩하는 일이 잦아졌다. 이 때, API 서버에 병렬적으로 요청을 날리면 실험을 빠르게 수행할 수 있다. 파이썬에서 병렬적으로 요청을 날리는 2가지 방식이 있다.
1. 파이썬 threading 패키지를 사용한 멀티스레드 프로그래밍
2. 파이썬 asyncio 패키지를 사용한 비동기 프로그래밍
예전에는 주로 멀티스레드를 사용했는데 요즘은 비동기 프로그래밍이 더 좋다고 해서 한번 어떻게 쓰는지 조사해보았다.
기본 사용법
먼저, 가장 간단한 비동기 프로그래밍 코드의 구조를 살펴보자.
import asyncio
async def main():
print("Hello")
await asyncio.sleep(1)
print("World")
asyncio.run(main())
해당 코드의 특징은 다음과 같다.
1. 비동기 함수를 정의할 때, async라는 키워드를 사용해 해당 함수가 비동기 함수임을 명시한다.
2. 비동기 함수를 호출할 때, asyncio.run이라는 함수를 함께 활용해 실행시킨다.
3. 시간이 많이 필요한 함수를 비동기 함수 내에서 실행할 때, await라는 키워드를 활용해 기다리게 구성한다.
따라서 위의 코드는 Hello를 출력하고 1초간 기다린 뒤, World를 출력하게 된다.
두 개의 코루틴 동시에 실행시키기
이러한 비동기 함수의 실행 플로우를 코루틴이라고 부르는 것 같은데 이런 코루틴들은 병렬적으로 실행될 수 있다. 아래 코드를 보자.
import asyncio
async def perform_task(worker_idx: int):
await asyncio.sleep(4)
print(f"worker {idx} done!")
async def main():
print(f"started at {time.strftime('%X')}")
task1 = asyncio.create_task(perform_task(0))
task2 = asyncio.create_task(perform_task(1))
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
두 코루틴을 만들고, 둘 다 끝날 때까지 기다린 뒤 시작 시간과 끝 시간을 비교할 수 있는 코드이다. 각각의 코루틴은 1초의 기다림을 가지지만, 병렬적으로 수행되기 때문에 8초가 걸리지 않고 4초가 걸리게 된다.
API 서버에 요청하는 비동기 프로그래밍 구성하기
이제 이 코루틴을 병렬적으로 구성해서, API에 병렬적으로 요청을 수행하는 프로그램을 구현해보자. 이걸 구현할 때 어려웠던 점은, 편하게 사용했던 requests 패키지가 비동기 함수로 구성되어 있지 않아서 바로 사용할 수 없었던 점이다. 찾아보면 requests 패키지를 비동기 함수에서 사용하는 방법이 나올 것 같지만 보편적인 방법은 requests 패키지를 사용하는 대신 aiohttp 패키지를 사용하는 방법인 것 같아 이를 활용했다. aiohttp 공식 문서의 빠르게 사용하는 예제를 참고해 API에 요청을 제공하는 비동기 함수를 구현하고 이를 메인 함수에서 호출해서 사용했다.
import asyncio
import aiohttp
async def fetch_url(url: str):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
text = await response.text()
return text
async def main():
url = 'https://jsonplaceholder.typicode.com/todos/1'
task1 = asyncio.create_task(fetch_url(url))
task2 = asyncio.create_task(fetch_url(url))
text1 = await task1
text2 = await task2
print(text1)
print(text2)
asyncio.run(main())
결론
비동기 프로그래밍을 사용해보았다. 병렬적으로 API를 요청하는 방법에 대해서 알아보았고, 나름 쉽게 사용할 수 있었다고 생각한다. asyncio 패키지에 다른 유용한 함수들 (예. asyncio.gather, asyncio.Queue)도 적재적소에 활용하면 도움이 될 것이라고 생각한다.
'Engineering' 카테고리의 다른 글
허깅페이스 데이터셋 업로드하기 (1) | 2023.11.21 |
---|---|
파이썬 비동기 프로그래밍에서 작업 큐 사용하기 (0) | 2023.11.20 |
GPT로 날짜 생성하기 (Prefix-Tuning 으로 학습) (0) | 2023.02.04 |
GPT로 날짜 만들어보기 (0) | 2023.02.04 |
Windows에서 WSL2를 이용해서 Docker 설치하기 (3) | 2021.03.14 |