[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으로 묶어서 프레임당 한 번만 업데이트하고, 구조적 변경 이벤트는 즉시 반영합니다.
왜 이게 좋은가
- GPU/CPU 부하 감소: 초당 수백 번의 반응형 업데이트가 ~60회로 줄어듭니다.
- 화면 갱신과 동기화:
requestAnimationFrame은 브라우저가 실제로 화면을 그리기 직전에 실행되므로, 불필요한 중간 상태 계산이 사라집니다. - 사용자 경험 동일: 눈에 보이는 변화 없이 순수한 성능 개선입니다.
- 구조적 변경은 즉시 반영: 채팅 전환이나 새 메시지 같은 중요한 이벤트는 지연 없이 처리됩니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Ray Serve] SGLang 서버의 순차 배치 처리를 동시 실행으로 전환
- 현재글 : [Open WebUI] chatEventHandler의 히스토리 업데이트를 rAF로 배치 처리하기
- 다음글 [cpython] CPython JIT 최적화: Float 연산의 In-place 변환을 통한 성능 향상
댓글