추론 시간 부하테스트 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
1. 테스트 목적
a. 가정
- 현재 모든 주요 작업(
embedding
,categories
,quality
,duplicates
,score
)은 세마포어 없이 실행되고 있음 - 별도의 세마포어 없이 병렬 요청이 들어오면 시스템 자원이 포화될 가능성이 있으므로, 특정
vus
이상부터는 리소스 병목이 발생하여 응답 시간과 실패율이 증가할 것
b. 예상 결과
- VUs가 낮을 경우에는 응답 시간과 CPU 부하가 안정적으로 유지될 것
- VUs가 증가하면 CPU 경합과 ThreadPool 포화로 인해 응답 시간이 급격히 증가
- CPU 처리량 한계가 가까워질수록
total_duration
,embedding_duration
,http_req_duration
의 p95, max 값이 급격히 증가할 것
- CPU 처리량 한계가 가까워질수록
- 세마포어를 도입하지 않으면 과도한 동시 요청에서 처리 지연 및 서버 과부하 발생
- 이로 인해
total_duration
의 급증과http_req_failed
비율 증가
- 이로 인해
2. 테스트 설계
a. 도구
- k6 (부하 테스트 및 성능 측정 CLI 도구)
- GCP Monitoring (CPU 사용률 확인)
b. 방식
- 테스트 대상 API 흐름:
POST /api/albums/embedding
(10장의 이미지 목록 포함)- 201 응답 시, 병렬로:
POST /api/albums/categories
POST /api/albums/quality
POST /api/albums/duplicates
/categories
응답에서images
추출 →POST /api/albums/score
- Test 조건:
--vus N
(1분동안 N명의 사용자가 동시에 요청)vus
값을 10, 12, 14, 16, 18, 20, 25, 30, 50까지 증가시키며 병목 시점 탐색
- 측정 항목:
- 평균 응답 시간 (
http_req_duration
) - 단계별 요청 응답 시간 (
embedding_duration
,score_duration
등) - 전체 처리 시간 (
total_duration
) - 대기 시간 (
http_req_blocked
,http_req_waiting
) - 시스템 CPU 부하 (GCP Monitoring 기준)
- 실패율 및 dropped iterations
- 평균 응답 시간 (
3. 테스트 결과
- 결과 요약 표
-
주요 지표
VUs embedding
(avg / p95 / max)categories
(avg / p95 / max)duplicates
(avg / p95 / max)quality
(avg / p95 / max)score
(avg / p95 / max)total
(avg / p95 / max)http_req_failed
iteration_duration
(avg)10 3.71s / 5.06s / 5.95s 336ms / 1.15s / 2.61s 342ms / 1.51s / 2.68s 362ms / 1.51s / 2.64s 163ms / 472ms / 523ms 5.15s / 6.97s / 10.75s 1.33% 8.27s 12 4.72s / 5.86s / 6.95s 631ms / 1.79s / 2.02s 629ms / 1.77s / 2.06s 627ms / 1.76s / 2.02s 218ms / 511ms / 573ms 6.82s / 9.60s / 10.29s 0% 9.52s 16 6.16s / 9.11s / 9.21s 2.05s / 4.56s / 5.07s 2.03s / 4.51s / 5.09s 2.04s / 4.53s / 5.05s 233ms / 613ms / 753ms 12.59s / 18.24s / 19.33s 0.24% 12.57s 18 6.56s / 9.27s / 10.45s 2.19s / 4.86s / 5.80s 2.17s / 4.86s / 5.84s 2.18s / 4.85s / 5.79s 157ms / 245ms / 249ms 13.33s / 19.28s / 21.46s 0.22% 12.98s 20 7.30s / 9.86s / 11.41s 2.80s / 5.80s / 6.61s 2.79s / 5.75s / 6.57s 2.79s / 5.76s / 6.57s 343ms / 623ms / 699ms 16.09s / 22.48s / 24.48s 0.19% 14.52s 25 9.18s / 13.65s / 14.80s 4.24s / 8.35s / 8.69s 4.15s / 8.32s / 8.69s 4.17s / 8.34s / 8.73s 293ms / 475ms / 514ms 22.04s / 30.89s / 31.52s 0% 17.71s 30 10.39s / 16.54s / 17.14s 5.78s / 11.06s / 11.39s 5.72s / 11.03s / 11.38s 5.74s / 11.04s / 11.42s 299ms / 445ms / 497ms 28.17s / 39.47s / 40.23s 0.44% 20.40s 50 16.43s / 26.98s / 28.68s 10.44s / 20.41s / 20.87s 10.40s / 20.37s / 20.83s 10.42s / 20.45s / 20.82s 527ms / 864ms / 952ms 48.70s / 68.78s / 69.88s 0.59% 31.14s -
자원 사용률
항목 관측값 최대 CPU 사용률 약 76% CPU 사용률 경향 VUs 수와 거의 무관, 항상 70% 이상 유지됨 최대 메모리 사용률 약 80% 메모리 사용률 경향 VUs 수가 증가할수록 비례 증가 (이미지 임베딩 결과 캐싱의 영향으로 추정)
-
a. 자원 소모 & 병목 구간
- CPU 사용률은 이미 10 VUs에서 70% 이상 고정되어 있음 → 스레드풀 과점유의 여지 없음
- 메모리 사용률은 점진적으로 증가하며, 캐시 적중률이 높아질수록 전체 메모리 요구량 증가
- VU 20~25 이후부터 응답 시간이 급격히 증가하고 요청 실패가 소수 발생하기 시작함
주요 병목 요인
embedding
연산이 가장 오래 걸리며 → 연산 병렬화에 한계가 있음- 후속 API(
categories
,quality
,duplicates
)들도 캐싱 이후 CPU 자원을 많이 소모함
b. 병목 구간 포착
항목 | 평균 duration (s) | p95 duration (s) | 분석 |
---|---|---|---|
embedding | VU 10: 3.7s → VU 30: 10.4s → VU 50: 16.4s |
최대 26.9s |
연산 자체가 가장 오래 걸리며, 자원 한계 도달 시 급격한 지연 발생 |
categories | VU 10: 0.29s → VU 30: 5.8s → VU 50: 10.4s |
최대 20.9s |
캐시 기반 조회라도 VUs가 많아지면 병렬 처리 경쟁이 심화됨 |
duplicates | VU 10: 0.29s → VU 30: 5.7s → VU 50: 10.4s |
최대 20.8s |
유사도 비교 연산이 많은 태스크라 병목 발생 |
quality | VU 10: 0.3s → VU 30: 5.7s → VU 50: 10.4s |
최대 20.8s |
CLIP 기반 품질 추론 + blur 검사로 인해 CPU 부하 발생 |
score | VU 10: 0.16s → VU 30: 0.3s → VU 50: 0.52s |
최대 0.95s |
상대적으로 가벼움. score는 병목 요소 아님 |
total | VU 10: 5.1s → VU 30: 28s → VU 50: 48s |
최대 ~69s |
전체 시스템 병목이 누적되어 발생하는 결과 |
http_req_failed
비율
c. - 16~25 VUs까지는 0.2% 이하의 낮은 실패율.
vus=30
부터 실패율이 0.44%,vus=50
은 **0.59%**까지 상승 → 시스템이 오류 처리 임계점에 근접.
d. 작업별 부하
embedding
은 확실한 병목 요소 (최대 17s 이상 걸림).- 후속 작업(
categories
,duplicates
,quality
)도 점점 지연 → 대부분 5~10s 이상 (avg 기준). score_duration
은 비교적 가볍지만 함께 밀림.
4. 결론
인사이트 | 근거 |
---|---|
✅ 병목은 embedding 연산과 후속 태스크가 동시에 병렬 처리될 때 가중됨 | embedding avg: 16.4s (VU 50), categories/quality 등 10s+ |
✅ 12 vus까지는 안정적으로 유지 | embedding p95 기준 ~5s , total ~7s |
⚠️ 16 VUs 이상에서는 p95가 10초 이상으로 상승하며 지연 시작 | total_duration p95: 18.2s (VU 16), 22.4s (VU 20) |
⚠️ 25 이상에서는 병목이 심해지고 max 69초 응답 발생 | total_duration max: 31s (VU 25), 40s (VU 30), 69s (VU 50) |
5. 병렬 처리 제한 설정 기준 제안
작업 종류 | 병목 유형 | 제안 세마포어 크기 (기준: 30 VUs) |
---|---|---|
embedding |
가장 무거움 (CPU + I/O) | 3~4 |
categories |
중간 (CPU) | 6~8 |
duplicates |
중간 (CPU) | 6~8 |
quality |
중간 (CPU) | 6~8 |
score |
가벼움 | 10~12 |
- 이 수치는 CPU core 수, torch 스레드 설정, 메모리 상태에 따라 조정 필요
6. 세마포어가 터지지 않아도 필요한 이유
a. 처리량 대비 응답 시간 안정성 보장
항목 | 세마포어 없음 | 세마포어 도입 시 |
---|---|---|
VU 10 | 평균 응답 5~6초 수준 (안정적) | 비슷 |
VU 16+ | 평균 응답 12~48초 급증 → 불안정 | 병렬 제한으로 폭주 방지 → 일정한 응답 유지 가능 |
-
즉, "터지진 않더라도" 응답시간이 기하급수적으로 증가 → 사용자 체감 성능 하락
→ 세마포어는 자원을 분산해서 응답 시간 분산을 평탄하게 유지함
b. 우선순위가 다른 작업 간 공정성 확보
- 현재 구조는 embedding / categories / duplicates / quality / score 등 모두 같은 스레드풀 사용
- 특정 순간 embedding 작업이 몰리면, 후속 경량 작업이 밀리게 됨
- 세마포어로 태스크 종류별 병렬 개수 조절 시:
- heavy한 embedding은 제한
- 경량 태스크들은 여유롭게 동시 처리 가능 → 실시간성 유지
- 예: score_duration은 0.3초 작업인데도 embedding 대기 때문에 3~5초 걸릴 수 있음
- 세마포어 없이는 짧은 작업이 긴 작업에 인질로 잡히는 구조
c. 서버 부하 분산 및 예측 가능한 운영 가능
- GCP 서버 기준 CPU 76%, RAM 80% 사용
- 이 수치는 *“위험선 직전”*이며, 백그라운드 프로세스나 기타 요청에 따라 폭주 가능
- 세마포어는 "고삐" 역할:
- 순간적인 요청 몰림을 자체적으로 제어
- 스케일 아웃 없이도 안정성 확보
d. 오류율 관리 및 장애 예방
- VU 30 이상부터 HTTP 422, 5xx 발생률 증가
- 이들은 대부분 과도한 병렬 처리 → 캐시 미적용/DB 지연/락 경합에서 발생
- 세마포어는 장애를 예방하고 평균 실패율을 낮추는 가장 간단한 컨트롤 수단
7. 결론 요약
항목 | 세마포어 없이도 가능? | 세마포어 있으면 더 좋은 점 |
---|---|---|
시스템이 터짐? | ❌ 아니었음 | ✅ 더 안정적 |
응답 시간 | ❌ 급증함 (VU↑) | ✅ 예측 가능하게 유지 |
경량 작업 보장 | ❌ embedding에 밀림 | ✅ 공정하게 처리됨 |
확장성 | ❌ 제한적 | ✅ 동시성 제어 가능 |
오류율 | ⬆️ VU 30~50에서 증가 | ✅ 줄일 수 있음 |