[Open WebUI] 스트리밍 중 메시지 리스트 재구성을 프레임당 1회로 제한
PR #21885 - perf: throttle message list rebuild to once per animation frame during streaming
들어가며
Open WebUI의 Messages.svelte는 history.messages가 변경될 때마다 부모 체인을 순회하며 메시지 리스트를 재구성합니다. 스트리밍 중에는 매 토큰마다 이 작업이 실행되어 초당 수백 번의 리빌드가 발생했지만, 실제로 각 ResponseMessage는 자체 반응형 바인딩으로 콘텐츠를 업데이트하므로 리빌드의 대부분은 불필요했습니다.
핵심 코드 분석
Before
$: if (history.currentId) {
let _messages = [];
// 부모 체인 순회하며 메시지 배열 구성
// ...
messages = _messages;
}
history.messages의 모든 변경에 즉시 반응하여 리빌드합니다.
After
let pendingRebuild = null;
let lastCurrentId = null;
const buildMessages = () => {
// 기존 리빌드 로직
};
$: if (history.currentId) {
const currentIdChanged = history.currentId !== lastCurrentId;
lastCurrentId = history.currentId;
if (currentIdChanged) {
// 구조적 변경: 즉시 리빌드
cancelAnimationFrame(pendingRebuild);
pendingRebuild = null;
buildMessages();
} else if (history.messages) {
// 콘텐츠 업데이트(스트리밍): 프레임당 1회로 쓰로틀링
if (!pendingRebuild) {
pendingRebuild = requestAnimationFrame(() => {
pendingRebuild = null;
buildMessages();
});
}
}
}
onDestroy에서 정리 로직도 추가되었습니다:
onDestroy(() => {
cancelAnimationFrame(pendingRebuild);
});
왜 이게 좋은가
- 완전히 보이지 않는 변경: PR 작성자가 "free lunch"라고 표현할 만큼, 사용자 경험에 전혀 영향 없이 순수한 성능 개선입니다.
- 구조적 변경과 콘텐츠 변경 분리: 채팅 전환이나 새 메시지 같은 구조적 변경은 즉시 반영하고, 스트리밍 중 콘텐츠 업데이트만 쓰로틀링합니다.
- 리소스 정리:
onDestroy에서 대기 중인requestAnimationFrame을 취소하여 메모리 누수를 방지합니다. - ~60Hz로 제한: 초당 수백 번의 리빌드가 ~60번으로 줄어들어 CPU 사용량이 크게 감소합니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Open WebUI] Notes.svelte 메모리 누수 수정
- 현재글 : [Open WebUI] 스트리밍 중 메시지 리스트 재구성을 프레임당 1회로 제한
- 다음글 [Open WebUI] MentionList 컴포넌트 메모리 누수 수정
댓글