-
Notifications
You must be signed in to change notification settings - Fork 0
Qdrant에서 disk에 리뷰 17273개 업로드 시 CPU 500% 스레드 32개 사용
Q. Qdrant에서 disk에 리뷰 17273개 올리는데 CPU 500%, 스레드 32개 사용. 왜지?
A.
disk는 ‘저장 위치’일 뿐이고, 업로드 과정에서 보통 아래 작업들이 같이 일어나:
- 임베딩 생성(가장 흔한 원인)
- 배치/병렬 업서트(클라이언트 단에서 동시 요청)
- 토크나이저/BLAS/OpenMP 같은 네이티브 라이브러리의 멀티스레딩
- (별개로) Qdrant 서버 쪽에서도 인덱스(HNSW) 구축으로 CPU 사용
그런데 너는 “파이썬 프로세스가 CPU 500%”라고 했지? → 보통 1) 또는 2) + 3) 조합이야.
파이썬에서 이런 코드 패턴 있지 않아?
asyncio.to_thread(...)loop.run_in_executor(None, ...)-
concurrent.futures.ThreadPoolExecutor()를 명시 안 하고 기본값 사용 - 혹은 내부 라이브러리가 threadpool을 씀
이때 기본 스레드 수가 보통:
min(32, (os.cpu_count() or 1) + 4)
이라서 딱 32개가 자주 나온다.
즉, 너의 파이프라인이 **“업로드를 비동기/병렬로 밀어넣는 구조”**면 클라이언트가 스레드를 32개까지 만들고, 그중 일부가 계속 CPU를 쓸 수 있어.
500% = 5코어 풀가동. 파이썬은 GIL 때문에 “원래” CPU 병렬이 안 된다고들 하지만…
- 토크나이저(HuggingFace tokenizers): Rust/네이티브로 GIL 해제 + 내부 병렬
- NumPy/BLAS(MKL/OpenBLAS): 내부적으로 스레드 사용
- PyTorch CPU 연산: intra-op / inter-op thread 사용
- 압축/해시/JSON 등 네이티브 확장: GIL 해제 가능
그래서 “파이썬 프로세스가 멀티코어를 쓰는 것처럼” 보일 수 있어.
Qdrant에 올릴 때 보통 흐름이:
- 리뷰 텍스트 전처리
- 임베딩 모델로 벡터화
- 배치로 upsert
여기서 CPU를 가장 많이 태우는 건 대개 2)야. 특히:
- CPU 임베딩(또는 토크나이징이 병목)
- 배치가 크고 동시성이 크면 토크나이저가 스레드를 많이 씀
이건 Qdrant 서버 프로세스 쪽 CPU가 오르는 이유고, 네가 본 “파이썬 CPU 500%”랑은 별개일 수 있지만 같이 발생할 수 있어.
- 업서트가 들어오면 Qdrant는 세그먼트/인덱스 업데이트를 함
- 특히 HNSW 인덱싱(설정에 따라) CPU 사용이 큼
업서트 직전/직후에 로그로 시간 찍어봐:
- “임베딩 생성 시간”
- “Qdrant upsert 시간”
임베딩이 대부분이면 원인 확정.
-
ps -o nlwp <pid>로 스레드 수 확인 -
htop에서 Threads 보기(스레드 이름이 보이면 더 좋음) - 파이썬 코드에
to_thread/run_in_executor/ threadpool 사용 흔적 찾기
import asyncio
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=8)
loop = asyncio.get_event_loop()
loop.set_default_executor(executor)또는 to_thread를 남발하지 말고, 동시 작업 수를 Semaphore로 제한.
환경변수:
export TOKENIZERS_PARALLELISM=falseexport OMP_NUM_THREADS=4
export MKL_NUM_THREADS=4
export OPENBLAS_NUM_THREADS=4(이거 하나로 CPU 폭주가 깔끔히 잡히는 경우 많아.)
- batch size를 너무 크게 잡으면 클라이언트/서버 둘 다 부하
- 동시 업서트 요청을 2~8 정도로 제한해도 총 처리시간이 오히려 좋아질 때가 많음(큐 폭주 방지)
기본 ThreadPoolExecutor(최대 32) + 임베딩/토크나이저/BLAS 멀티스레딩 조합.
- 스레드가 딱 32인 게 너무 강한 힌트야.
원하면, 네가 “업로드 코드(embedding 생성 + upsert 부분)”를 그대로 붙여줘. 그러면 어느 라인이 스레드/CPU를 만드는지랑 동시성/배치 파라미터를 어디까지 줄이면 좋은지를 딱 짚어서 수정안을 바로 써줄게.
Q. 정상인가?
A.
지금 정보만 놓고 보면 “이상 현상”이라기보단, 흔히 나오는 정상 범주일 가능성이 큽니다. 다만 언제는 괜찮고, 언제는 문제인지 기준이 딱 있어요. 그 기준으로 바로 판정해볼게요.
아래 중 2~3개 이상이면 정상/의도된 부하로 보는 게 맞아요.
- 업로드(임베딩 생성 + upsert)가 진행되는 동안에만 CPU 500%가 올라갔다가 끝나면 떨어진다
- 전체 처리 시간이 예상대로 줄거나(또는 합리적), CPU를 더 쓰는 만큼 속도가 나왔다
- 머신이 8코어 이상이고, 500% = 5코어 사용이라서 “풀로드”가 아니다
- 메모리 스왑이 없고(메모리 여유), 시스템이 끊기지 않는다
- Qdrant도 정상 응답하고, 오류/타임아웃이 없다
👉 이 경우는 “리소스를 써서 일을 빨리 끝내는 배치 작업”이라 괜찮은 설계야.
아래 중 하나라도 해당하면 튜닝/제어가 필요해요.
- 업로드가 끝났는데도 CPU 500%가 계속 유지
- 요청 수가 많지 않은데도 CPU가 지속적으로 고정
- CPU만 올라가고 처리 속도는 안 나옴 (throughput/latency 개선 없음)
- 다른 서비스(서버, DB, 모니터링)가 같이 돌고 있는데 응답이 느려지거나 타임아웃
- 스레드 32가 매번 생기면서 시스템 전체가 불안정 (context switch 폭증, load average 급등)
- 스왑 발생, iowait 상승, 디스크 대기 증가(특히 on-disk에서)
👉 이건 보통
- 스레드풀 과다
- 토크나이저/BLAS/OpenMP 스레딩 중첩(“스레드 안에 스레드”)
- 업서트 동시성 과다 중 하나야.
리뷰 17,373개 업로드는 배치 작업이고, CPU 500% + 스레드 32는 “기본 스레드풀 + 네이티브 병렬화” 패턴이라서
- 배치 중에만 발생했고
- 서비스를 안 망가뜨렸고
- 업로드가 정상 종료됐다면
➡️ 괜찮은 상태(정상 범주) 로 보는 게 맞아.
-
업로드 끝난 뒤 CPU가 내려갔나?
- 내려갔다 ✅ → 정상
- 안 내려간다 ❌ → 문제 가능성 큼
-
처리 시간이 “CPU를 쓴 만큼” 줄었나?
- 줄었다 ✅ → 정상 최적화
- 안 줄었다 ❌ → 스레드 폭주/병목 재조정 필요
-
iowait / swap이 발생했나?
- 없다 ✅ → 괜찮음
- 있다 ❌ → on-disk/배치/동시성 튜닝 필요
(코드 크게 안 바꾸고 잡히는 경우가 많음)
- 토크나이저 병렬 끄기
export TOKENIZERS_PARALLELISM=false- 네이티브 라이브러리 스레드 제한
export OMP_NUM_THREADS=4
export MKL_NUM_THREADS=4
export OPENBLAS_NUM_THREADS=4- 배치 업로드 중에만 CPU 500%/스레드 32가 뜬 거면: 괜찮다(정상).
- 업로드 끝나도 지속되거나, 속도 이득 없이 시스템만 흔들면: 문제다(튜닝 필요).