langchain / / 2024. 8. 25. 16:56

[langchain] llm의 invoke, ainvoke, stream, astream 실행시간 테스트

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개의 호출이 끝나면 다음 로직을 호출하는 방식이다.

참고

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유