본문으로 건너뛰기

[Open WebUI] chatEventHandler의 히스토리 업데이트를 rAF로 배치 처리하기

PR #22947 - perf: batch history reactive updates to rAF in chatEventHandler

들어가며

Open WebUI의 채팅 인터페이스에서 빠른 모델이 토큰을 스트리밍할 때, 매 소켓 이벤트마다 history.messages[event.message_id] = message가 실행되어 Svelte 반응형 업데이트가 트리거됩니다. 빠른 모델의 경우 이 업데이트 빈도가 브라우저의 페인트 속도(~60fps)를 훨씬 초과하여, 대부분의 업데이트 사이클이 실제 화면에 반영되지 않는 낭비가 발생합니다.

핵심 코드 분석

Before

// 모든 이벤트 타입에 대해 무조건 history 업데이트 트리거
history.messages[event.message_id] = message;

매 소켓 이벤트마다 Svelte 반응형 시스템이 전체 컴포넌트 트리를 다시 계산합니다.

After

if (type === 'chat:message:delta' || type === 'message' || type === 'status') {
    scheduleHistoryFlush();
} else {
    cancelAnimationFrame(historyRAF);
    historyRAF = null;
    history.messages[event.message_id] = message;
}
let historyRAF = null;
const scheduleHistoryFlush = () => {
    if (!historyRAF) {
        historyRAF = requestAnimationFrame(() => {
            historyRAF = null;
            history = history;
        });
    }
};

고빈도 이벤트(delta, message, status)는 requestAnimationFrame으로 묶어서 프레임당 한 번만 업데이트하고, 구조적 변경 이벤트는 즉시 반영합니다.

왜 이게 좋은가

  1. GPU/CPU 부하 감소: 초당 수백 번의 반응형 업데이트가 ~60회로 줄어듭니다.
  2. 화면 갱신과 동기화: requestAnimationFrame은 브라우저가 실제로 화면을 그리기 직전에 실행되므로, 불필요한 중간 상태 계산이 사라집니다.
  3. 사용자 경험 동일: 눈에 보이는 변화 없이 순수한 성능 개선입니다.
  4. 구조적 변경은 즉시 반영: 채팅 전환이나 새 메시지 같은 중요한 이벤트는 지연 없이 처리됩니다.

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글