TL;DR

  • VEX의 유틸리티 기능과 메인 애플리케이션에서 콜드 스타트가 약 4초였으며, 초기화 우회와 임포트 최적화로 메인 시작 시간을 MCP 도구 없이 약 2.1초, 도구를 포함해 약 2.7초로 단축함.
  • --help, --ls 같은 유틸리티 기능은 메인 프로그램 초기화를 건너뛰도록 분리해 약 400ms에 실행됨.
  • TYPE_CHECKING 가드로 타입 검사 전용 임포트를 런타임에서 제외해 불필요한 모듈 로딩을 방지함.
  • 무거운 모듈을 함수 내부에서 필요할 때 임포트하고, MCPManager는 CONFIG.toml에 설정된 경우에만 임포트함.
  • END 상수만 가져오기 위해 전체 모듈을 임포트하던 코드를 상수 문자열로 대체해 1.36초를 절약함.

아키텍처 심층 분석: 시작 시간과 임포트 최적화

  • 유틸리티 함수와 메인 VEX 프로그램 모두 콜드 스타트에 약 4초가 걸리는 상태에서 최적화를 시작함.

메인 앱과 유틸리티 함수의 차이

  • 유틸리티 함수는 대개 메인 프로그램 기능을 실행하지 않는 단축 경로임.
  • --help: 도움말만 표시함.
  • --ls: 세션 목록을 확인하며 sqlite 데이터베이스에만 접근함.
  • 이런 기능은 거의 즉시 실행되어야 하므로, 유틸리티 인자를 확인한 뒤 메인 프로그램 초기화를 건너뛰도록 구성함.
  • args.init이면 작업 공간을 초기화하고 반환하며, args.list_sessions이면 세션 목록을 불러오고 반환함.
  • 메인 에이전트 실행에 필요한 vex_shell.engine.core와 vex_shell.engine.agent.Agent 같은 무거운 모듈은 유틸리티 인자 확인 이후에 임포트함.

메인 앱 시작 최적화

  • 빠른 유틸리티 기능과 별도로 메인 앱 시작 시간도 최적화 대상으로 삼음.
  • tuna와 -X importtime을 사용해 시작 시간을 늦추는 모듈을 조사함.
  • tuna는 각 모듈 임포트에 걸리는 시간을 시각화해 코드베이스의 병목을 확인하는 데 활용됨.

타입 주석 전용 임포트

  • 개발 중 타입 주석만을 위해 모듈 수준에서 임포트한 항목도 해당 모듈 전체를 로드할 수 있음.
  • Settings를 타입 힌트에만 사용하더라도 설정 모듈 전체가 로드되는 문제가 있음.
  • from __future__ import annotations와 typing.TYPE_CHECKING을 사용하고, TYPE_CHECKING 블록 안에 Agent, Console, Settings 임포트를 두면 정적 타입 검사에만 사용됨.
  • TYPE_CHECKING은 런타임에서 False로 평가되므로 실행 중 리소스 오버헤드가 발생하지 않음.

함수 범위 임포트

  • 일반적으로 파일 상단에 모듈을 임포트하지만, 이 방식은 애플리케이션 시작 시 임포트 비용을 발생시킴.
  • 무거운 모듈을 함수 내부에서 임포트하면 해당 함수가 호출될 때까지 로딩을 미룰 수 있음.
  • 이 방식은 함수의 첫 호출을 느리게 할 수 있지만 시작 시간을 줄이며, 첫 호출 이후에는 임포트된 모듈이 캐시에 남음.
  • build_graph가 호출될 때 StateGraph, ToolNode, 노드 함수와 상태 정의를 임포트하고 그래프를 구성하도록 변경함.

참고할 점

  • nodes.py에서 END 상수만 사용하기 위해 langgraph.graph.state를 임포트했지만, 이로 인해 전체 모듈 임포트에 1.36초가 걸림.
  • 해당 임포트를 END = "__end__"로 대체해 이 시간을 절약함.

대략적인 결과

  • MCPManager: CONFIG.toml에 정의된 경우에만 임포트해 1초 절약함.
  • 타입 주석 전용 임포트를 TYPE_CHECKING 가드 안으로 이동함.
  • ChatOpenAI, langchain.messages 등 무거운 임포트를 함수 내부로 이동함.

측정 시간

  • 유틸리티 기능 --ls, --help, --reset: 약 400ms임.
  • MCP 도구가 없는 메인 앱 시작 시간: 약 2.1초임.
  • MCP 도구가 있는 메인 앱 시작 시간: 약 2.7초임.

출처