LangChain
에서 자주 사용되는 네 가지 메서드인 invoke
, ainvoke
, stream
, astream
를 실행할 때의 실행시간에 얼마나 차이가 있는지 테스트 해보았다.
기본 코드
실행시간을 측정하기 위한 데코레이터 함수를 두 개 만들었다.
import time
from functools import wraps
def elapsed_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
print("Sync execution time:", (end_time - start_time))
return result
return wrapper
def elapsed_time_async(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = await func(*args, **kwargs)
end_time = time.perf_counter()
print("Async execution time:", (end_time - start_time))
return result
return wrapper
elapsed_time
는 동기 함수 실행 시 실행시간을 측정하기 위한 것이고, elapsed_time_async
는 비동기 함수 실행 시 실행시간을 측정하기 위한 것이다.
그리고 아래 코드는 4가지 유형에 대한 테스트를 위한 기본코드이다.
llm = ChatOpenAI(
temperature=0, # 창의성 (0.0 ~ 2.0)
model_name="gpt-4o", # 모델명
)
template = "한국의 수도는?"
request_count = 10
모든 요청은 10번
실행하고 실행시간을 각각 측정한다. (request_count = 10)
1. invoke
: 동기적 요청 처리
invoke
는 LangChain에서 가장 기본적인 메서드로, 동기적으로 언어 모델에 요청을 보내고 결과를 반환한다.
사용 예시
@elapsed_time
def invoke_sync():
for i in range(request_count):
result = llm.invoke(template)
print(result.content)
invoke_sync()
응답결과
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 많은 인구와 다양한 명소를 자랑합니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
Sync execution time: 7.011766356998123
10번
실행한 시간이 7초
정도 걸린 것을 확인할 수 있다.
2. ainvoke
: 비동기적 요청 처리
ainvoke
는 비동기 방식으로 언어 모델에 요청을 보내고 결과를 반환한다. 아래 코드 중 asyncio.gather
는 비동기 작업을 한번에 실행하고자 할 때 사용할 수 있다.
사용 예시
async def ainvoke():
result = await llm.ainvoke(template)
print(result)
@elapsed_time_async
async def invoke_async():
tasks = [ainvoke() for _ in range(request_count)]
await asyncio.gather(*tasks)
asyncio.run(invoke_async())
응답결과
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 많은 인구와 다양한 명소를 자랑합니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
한국의 수도는 서울입니다. 서울은 대한민국의 정치, 경제, 문화의 중심지로서 중요한 역할을 하고 있습니다.
Async execution time: 0.7999031220097095
10번
실행한 시간이 0.8초
정도 걸린 것을 확인할 수 있다.
[참고] 위의 코드를 아래와 같이 실행하면 ainvoke()의 실행이 끝날 때 다음을 호출하는 방식인 동기방식과 동일하게 동작한다. 그래서 asyncio.gather를 통해 작업을 한번에 실행해야 한다.
@elapsed_time_async async def invoke_async(): for i in range(request_count): await ainvoke()
What is asyncio.gather(): https://www.educative.io/answers/what-is-asynciogather
3. stream
: 동기적 스트리밍
stream
은 동기적으로 언어 모델의 응답을 스트리밍 방식으로 처리할 수 있게 된다.
사용 예시
@elapsed_time
def stream_sync():
for i in range(request_count):
for chunk in llm.stream(template):
print(chunk.content)
stream_sync()
응답결과
한국
의
수도
는
서울
입니다
.
.... <계속>
.
Sync execution time: 6.138112443964928
10번
실행한 시간이 6초
정도 걸린 것을 확인할 수 있다.
4. astream
: 비동기적 스트리밍
astream
은 비동기 방식으로 스트리밍 데이터를 처리할 수 있게 한다. 아래 코드 중 asyncio.gather
는 비동기 작업을 한번에 실행하고자 할 때 사용할 수 있다.
사용 예시
async def astream():
async for chunk in llm.astream(template):
print(chunk.content)
@elapsed_time_async
async def stream_async():
tasks = [astream() for _ in range(request_count)]
await asyncio.gather(*tasks)
응답결과
한국
의
수도
는
서울
입니다
.
.... <계속>
.
Async execution time: 1.4240893060341477
10번
실행한 시간이 1.4초
정도 걸린 것을 확인할 수 있다.
요약
함수명 | 동기/비동기 | 실행시간 (초) |
---|---|---|
invoke |
동기 | 7.011766356998123 |
ainvoke |
비동기 | 0.7999031220097095 |
stream |
동기 | 6.138112443964928 |
astream |
비동기 | 1.4240893060341477 |
위 결과는 실행환경에 따라 시간이 달라질 수 있다.
동기 호출은 응답을 받고 나서 다음 코드를 실행할 수 있지만 비동기의 경우 동시에 실행할 수 있으므로 빠르게 실행할 수 있다. 즉, 10개의 호출을 비동기는 동시에 실행할 수 있지만, 동기의 경우 1개의 호출이 끝나면 다음 로직을 호출하는 방식이다.