[open-webui] Open WebUI 채팅 파일 중복 제거 로직 최적화: O(n*m)에서 O(n+m)으로
PR 링크: open-webui/open-webui#23800 상태: Merged | 변경: +None / -None
들어가며
이번 PR은 Open WebUI 백엔드에서 채팅 파일 업로드 시 발생할 수 있는 중복 파일 처리 로직의 성능을 개선하는 데 초점을 맞추고 있습니다. 기존 코드에서는 채팅 메시지에 파일을 추가할 때, 이미 존재하는 파일 ID를 확인하는 과정에서 비효율적인 리스트 탐색을 사용하고 있었습니다. 이로 인해 파일 수가 많아질수록 성능 저하가 발생하는 문제가 있었습니다. 본 PR은 이 부분을 효율적인 세트(Set) 자료구조를 활용하여 O(n*m)의 시간 복잡도를 O(n+m)으로 개선하는 것을 목표로 합니다.
코드 분석
이번 변경은 backend/open_webui/models/chats.py 파일 내의 insert_chat_files 함수에서 이루어졌습니다.
insert_chat_files 함수 내 중복 제거 로직 개선
기존 코드에서는 채팅 메시지에 이미 존재하는 파일 ID 목록을 가져와 리스트(chat_message_file_ids)로 저장했습니다. 이후 새로운 파일 ID 목록(file_ids)에서 중복을 제거하기 위해, 각 file_id가 chat_message_file_ids 리스트에 포함되어 있는지 확인하는 Comprehension을 사용했습니다.
Before:
chat_message_file_ids = [
item.id for item in await self.get_chat_files_by_chat_id_and_message_id(chat_id, message_id, db=db)
]
# Remove duplicates and existing file_ids
file_ids = list(set([file_id for file_id in file_ids if file_id and file_id not in chat_message_file_ids]))
이 방식의 가장 큰 문제는 file_id not in chat_message_file_ids 부분입니다. chat_message_file_ids가 리스트일 경우, in 연산은 리스트의 모든 요소를 순차적으로 탐색해야 하므로 평균적으로 O(m)의 시간 복잡도를 가집니다 (여기서 m은 chat_message_file_ids의 길이). 이 연산이 file_ids의 각 요소(n개)에 대해 수행되므로, 전체 중복 제거 과정의 시간 복잡도는 O(n*m)이 됩니다.
또한, list(set([...]))와 같이 불필요하게 리스트를 세트로 변환 후 다시 리스트로 만드는 과정도 포함되어 있었습니다.
After:
chat_message_file_ids = {
item.id for item in await self.get_chat_files_by_chat_id_and_message_id(chat_id, message_id, db=db)
}
# Remove duplicates and existing file_ids
file_ids = list({file_id for file_id in file_ids if file_id and file_id not in chat_message_file_ids})
개선된 코드에서는 다음과 같은 변경이 이루어졌습니다:
chat_message_file_ids를 세트(Set)로 변경:[...]를{...}로 변경하여, 데이터베이스에서 가져온 기존 파일 ID들을 세트로 저장합니다. 세트에서 특정 요소의 포함 여부를 확인하는in연산은 평균적으로 O(1)의 시간 복잡도를 가집니다.- 세트 Comprehension 사용: 중복 제거 로직에서
file_id not in chat_message_file_ids부분을 세트 Comprehension{...}안으로 직접 포함시켰습니다. 이는 파이썬에서 더 간결하고 효율적인 세트 생성 방식입니다.
이 변경을 통해 file_id not in chat_message_file_ids 연산이 O(1)이 되므로, 전체 중복 제거 과정의 시간 복잡도는 O(n) (Comprehension 반복) + O(m) (초기 세트 생성) = O(n+m)으로 크게 개선됩니다.
왜 이게 좋은가?
성능 향상
이 PR의 핵심은 시간 복잡도를 O(n*m)에서 O(n+m)으로 개선한 것입니다. 이는 특히 chat_message_file_ids와 file_ids의 크기가 클 때 매우 큰 성능 향상을 가져옵니다. 예를 들어, 각 리스트의 크기가 1000개라면, 기존에는 약 1,000,000번의 비교가 필요했을 수 있지만, 개선 후에는 약 2,000번의 연산으로 줄어듭니다. 이는 응답 시간 단축 및 서버 부하 감소로 이어집니다.
코드 간결성 및 가독성
list(set([...]))와 같은 불필요한 변환 과정을 제거하고 세트 Comprehension을 사용하여 코드가 더 간결해졌습니다. 이는 코드의 가독성을 높이고 잠재적인 오류 발생 가능성을 줄입니다.
일반적인 교훈
- 자료구조의 중요성: 문제 해결에 적합한 자료구조를 선택하는 것이 성능에 얼마나 큰 영향을 미칠 수 있는지 보여줍니다. 리스트에서의 탐색(O(n)) 대신 세트에서의 탐색(O(1))을 활용하는 것은 흔한 성능 최적화 기법입니다.
- 시간 복잡도 분석: 코드 변경 전후의 시간 복잡도를 분석하고 이해하는 것은 성능 병목 현상을 식별하고 개선하는 데 필수적입니다.
- Pythonic한 코드 작성: 불필요한 중간 변환을 피하고, 파이썬의 내장 기능을 효율적으로 활용하는 것이 좋습니다.
리뷰 댓글 분석
PR 리뷰 과정에서 pr-validator-bot은 코드 자체에 대한 자동화된 오류를 발견하지 못했습니다. 이는 해당 변경이 문법적으로나 기본적인 로직 상으로 문제가 없음을 시사합니다. 다만, PR 제출 시 테스트 증빙에 대한 요구사항이 강조되었는데, 이는 오픈소스 프로젝트에서 코드 변경의 신뢰성을 확보하는 데 매우 중요함을 보여줍니다. 본 PR의 변경은 비교적 간단한 자료구조 변경이므로, 실제 기능에 영향을 미치지 않으면서 성능을 개선하는 데 초점을 맞춘 것으로 보입니다.
References
- Python Set Documentation - 세트 자료구조의 동작 방식 및 성능 특성에 대한 공식 문서입니다.
참고 자료
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
PR Analysis 의 다른글
- 이전글 [open-webui] Open WebUI 성능 개선: DB 세션 재사용으로 프로필 이미지 로딩 최적화
- 현재글 : [open-webui] Open WebUI 채팅 파일 중복 제거 로직 최적화: O(n*m)에서 O(n+m)으로
- 다음글 [open-webui] Open WebUI 성능 최적화: 불필요한 DB 중복 조회 제거하기
댓글