-
Notifications
You must be signed in to change notification settings - Fork 1
[Perf] 실제 BGE-M3 Batch Size 최적화 및 기본값 조정 #130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
4cf9b8b
docs: #128 BGE-M3 Batch Size 성능 비교 설계 추가
Gimini-3 da71594
test: #128 실제 BGE-M3 Batch Size Benchmark 추가
Gimini-3 2eb68f3
perf: #128 BGE-M3 문서 Batch 기본값 32 적용 및 결과 기록
Gimini-3 016b22f
docs: #128 설계 문서 형식 정리
Gimini-3 002c67d
fix: #128 BGE-M3 Benchmark 리뷰 지적 반영
Gimini-3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
docs/design/gimin-#128-bge-m3-batch-size-performance.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| # 실제 BGE-M3 Batch Size 성능 비교 설계 | ||
|
|
||
| ## 1. 배경 | ||
|
|
||
| 문서 임베딩 경로는 `BAAI/bge-m3`의 Batch API와 `1~64` 범위의 설정 가능한 Batch Size를 | ||
| 지원한다. 현재 기본값 `16`은 기능 검증을 위해 정한 값이며, 같은 입력과 같은 실행 환경에서 수집한 | ||
| 성능 근거는 없다. | ||
|
|
||
| 이번 작업은 제품 동작을 새로 추가하는 것이 아니라 실제 `/embed/batch` 경계를 반복 측정해 다음 | ||
| 질문에 답한다. | ||
|
|
||
| 1. Batch Size가 커질 때 HTTP 왕복 감소와 모델 내부 Batch 처리로 처리량이 얼마나 증가하는가? | ||
| 2. 처리량 증가와 요청 단위 p95·p99 지연, Embedding Server RSS 사이에 어떤 Trade-off가 있는가? | ||
| 3. 로컬 CPU 개발 환경에서 문서 Batch 기본값 `16`을 유지할 근거가 있는가? | ||
|
|
||
| ## 2. 범위 | ||
|
|
||
| ### 2.1 포함 | ||
|
|
||
| - 실제 `POST /embed/batch` HTTP 호출 | ||
| - Batch Size `1, 4, 8, 16, 32, 64` | ||
| - 같은 Text 집합과 같은 총 처리량을 사용한 순차 비교 | ||
| - Warm-up과 본 측정 분리 | ||
| - 처리량, 요청 지연 p50·p95·p99·max, Text당 평균 지연, 실패율 수집 | ||
| - Docker 실행 시 Embedding Server PID 1의 RSS 기준선·최대값 수집 | ||
| - Model명, 응답 순서·개수, 1024차원과 유한값 검증 | ||
| - Host·Container·Python·Torch·Transformers 환경 지문 수집 | ||
| - JSON 원본 결과와 Markdown 요약 결과 생성 | ||
| - 일반 테스트와 분리된 재현 명령 제공 | ||
|
|
||
| ### 2.2 제외 | ||
|
|
||
| - Mock Model 성능 비교 | ||
| - Worker·Parsing·Vector DB를 포함한 전체 Pipeline 처리량 | ||
| - 동시 HTTP 요청 부하 | ||
| - GPU·CUDA·양자화 최적화 | ||
| - OpenSQL 성능 측정 | ||
| - Query Embedding 단건 API 변경 | ||
| - BGE-M3 모델 교체 | ||
|
|
||
| ## 3. 측정 계약 | ||
|
|
||
| ### 3.1 입력 공정성 | ||
|
|
||
| - 모든 Batch Size는 같은 순서의 결정적 한국어 Text Corpus를 처리한다. | ||
| - 한 Profile이 처리하는 Text 수는 최대 Batch Size `64`의 배수여야 한다. | ||
| - 마지막 불완전 Batch가 결과를 왜곡하지 않도록 모든 요청은 Profile의 Batch Size와 같은 개수의 | ||
| Text를 포함한다. | ||
| - 기본값은 Profile당 `64`개 Text와 본 측정 `3`회다. 더 안정적인 수치가 필요하면 환경 변수로 | ||
| Text 수와 반복 횟수를 높인다. | ||
| - 본 측정 Round마다 Batch Size 실행 순서를 회전해 Model Cache와 Host 열 상태가 한 Profile에만 | ||
| 유리하게 작용하는 편향을 줄인다. | ||
|
|
||
| ### 3.2 Warm-up | ||
|
|
||
| - Health Check 성공 뒤 각 Batch Size를 한 번씩 호출한다. | ||
| - Warm-up Vector는 계약을 검증하지만 성능 통계에는 포함하지 않는다. | ||
| - Model Loading 시간은 제외한다. Cold Start는 별도 운영 지표이며 Batch Size 비교 대상이 아니다. | ||
|
|
||
| ### 3.3 통계 | ||
|
|
||
| 각 Batch Size별로 다음 값을 기록한다. | ||
|
|
||
| | 지표 | 계산 | | ||
| |---|---| | ||
| | 총 Text 수 | 성공 요청이 반환한 Vector 개수 합계 | | ||
| | 처리량 | 총 Text 수 / 성공 요청 지연 합계 | | ||
| | 요청 p50·p95·p99 | 선형 보간 Percentile | | ||
| | 요청 최대 지연 | 성공 요청 중 최대값 | | ||
| | Text당 평균 지연 | 성공 요청 지연 합계 / 총 Text 수 | | ||
| | 실패율 | 실패 요청 수 / 전체 요청 수 | | ||
| | RSS 기준선·최대값 | Profile 시간 구간의 `/proc/1/status` `VmRSS` 표본 | | ||
|
|
||
| 측정은 순차 요청으로 수행한다. 이번 결과는 Batch Size 자체의 효율을 비교하는 기준선이며 다중 | ||
| Worker 병렬 부하는 후속 Worker 처리량 작업에서 다룬다. | ||
|
|
||
| ## 4. 실행 경계 | ||
|
|
||
| 일반 `./gradlew test`는 실제 모델을 요구하지 않는다. 별도 Task가 실제 서버가 준비됐는지 확인하고 | ||
| Benchmark Script를 실행한다. | ||
|
|
||
| ```bash | ||
| docker compose up -d embedding-server | ||
| ./gradlew bgeBatchPerformanceTest | ||
| ``` | ||
|
|
||
| 기본 설정을 확장할 때만 다음 환경 변수를 사용한다. | ||
|
|
||
| ```bash | ||
| BGE_BENCHMARK_TOTAL_TEXTS=256 \ | ||
| BGE_BENCHMARK_ROUNDS=5 \ | ||
| ./gradlew bgeBatchPerformanceTest | ||
| ``` | ||
|
|
||
| 원본 JSON은 `build/reports/bge-m3-batch/` 아래에 생성해 Git에 포함하지 않는다. 재현 환경, 요약 수치, | ||
| 기본값 판단만 `docs/test-results/`에 기록한다. | ||
|
|
||
| ## 5. 실패 정책 | ||
|
|
||
| - Health Check 실패, Model명 불일치, 응답 순서·개수 불일치, 1024차원이 아닌 Vector, NaN·Infinity는 | ||
| 측정 실패다. | ||
| - 실패가 발생해도 완료된 Profile의 JSON 결과는 남기되 Process는 실패 Exit Code로 종료한다. | ||
| - RSS 수집은 Docker 외부 서버에서도 Benchmark를 실행할 수 있도록 선택 기능으로 둔다. Container가 | ||
| 없으면 성능 측정은 계속하고 RSS를 `null`로 기록한다. | ||
| - URL, Docker Container 이름 외의 접속 Secret은 입력·로그·결과에 기록하지 않는다. | ||
|
|
||
| ## 6. 기본값 판단 | ||
|
|
||
| 측정 전에 특정 Batch Size를 정답으로 고정하지 않는다. | ||
|
|
||
| 1. 실패율이 0이고 Vector 계약을 만족한 Profile만 비교한다. | ||
| 2. 최고 처리량 Profile을 먼저 확인한다. | ||
| 3. 현재 기본값 `16`이 최고 처리량의 95% 이상이면 변경하지 않는다. | ||
| 4. 더 큰 Batch가 의미 있는 처리량 개선을 보이더라도 p95 지연과 RSS 증가를 함께 기록한다. | ||
| 5. Apple Silicon Docker CPU 측정값은 로컬 개발 기준선으로만 사용하고 운영 SLO로 해석하지 않는다. | ||
|
|
||
| ## 7. 검증 | ||
|
|
||
| - Percentile·Corpus·응답 계약·요약 계산 Python 단위 테스트 | ||
| - 실제 BGE-M3 Smoke 규모 Benchmark | ||
| - 실제 BGE-M3 확장 규모 Benchmark | ||
| - 기존 Python Embedding API 계약 테스트 | ||
| - 전체 Java 회귀 테스트 | ||
|
|
||
| ## 8. 커밋 분할 | ||
|
|
||
| 1. `docs: #128 BGE-M3 Batch Size 성능 비교 설계 추가` | ||
| 2. `test: #128 실제 BGE-M3 Batch Size Benchmark 추가` | ||
| 3. `docs: #128 BGE-M3 Batch Size 측정 결과 기록` | ||
|
|
||
| ## 9. 완료 조건 | ||
|
|
||
| - Batch Size 6종을 같은 입력으로 한 명령에서 재현할 수 있다. | ||
| - 처리량·p50·p95·p99·Text당 지연·실패율·RSS가 구조화돼 기록된다. | ||
| - 실제 응답의 Model·개수·순서·1024차원·유한값을 측정마다 검증한다. | ||
| - 일반 테스트는 Docker나 실제 BGE-M3에 의존하지 않는다. | ||
| - 기본값 유지 또는 변경 판단이 측정 환경의 한계와 함께 문서화된다. |
167 changes: 167 additions & 0 deletions
167
docs/test-results/gimin-#128-bge-m3-batch-size-performance.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| # 실제 BGE-M3 Batch Size 성능 비교 결과 | ||
|
|
||
| ## 1. 결과 요약 | ||
|
|
||
| 2026-08-08 실제 `BAAI/bge-m3` HTTP Server에서 Batch Size `1, 4, 8, 16, 32, 64`를 같은 | ||
| 한국어 Text 집합으로 비교했다. 본 측정에서는 각 Profile이 256개 Text를 3회 처리해 총 768개 | ||
| Vector를 생성했다. | ||
|
|
||
| - 전체 본 측정: 1,140 HTTP 요청, 4,608 Vector | ||
| - 실패 요청: 0 | ||
| - Vector 계약 오류: 0 | ||
| - Batch 32 처리량: `17.08 texts/s` | ||
| - Batch 64 처리량: `17.38 texts/s` | ||
| - Batch 32는 최고 처리량의 `98.27%` | ||
| - Batch 32 p95: `1,975.70ms` | ||
| - Batch 64 p95: `3,719.82ms` | ||
| - Batch 64의 측정 최고 RSS는 Batch 32보다 약 `83.05MiB` 높음 | ||
|
|
||
| 처리량 차이가 1.76%에 불과한 반면 Batch 64의 p95는 Batch 32의 1.88배였다. 따라서 로컬 CPU | ||
| 기준 문서 임베딩 기본값을 `16`에서 `32`로 변경한다. | ||
|
|
||
| 설계와 통계 계약은 [실제 BGE-M3 Batch Size 성능 비교 설계](../design/gimin-%23128-bge-m3-batch-size-performance.md)에 | ||
| 기록했다. | ||
|
|
||
| ## 2. 실행 환경 | ||
|
|
||
| | 항목 | 값 | | ||
| |---|---| | ||
| | Host | macOS 26.5.2, Apple Silicon arm64 | | ||
| | Host 논리 CPU | 10 | | ||
| | Embedding Container | `docgrid-embedding`, Linux | | ||
| | Python | 3.11.15 | | ||
| | Torch | 2.4.1, Thread 10 | | ||
| | Transformers | 4.44.2 | | ||
| | FlagEmbedding | 1.2.11 | | ||
| | CUDA | 사용하지 않음 | | ||
| | Model | `BAAI/bge-m3` | | ||
| | Vector 차원 | 1024 | | ||
| | Warm-up | Batch Size별 1회 | | ||
| | 본 측정 | 256 Text × 3 Round × 6 Profile | | ||
| | RSS 표본 간격 | 1초와 Profile 경계 | | ||
|
|
||
| 이 결과는 Apple Silicon의 Docker CPU 실행 기준선이다. GPU 환경이나 공급사 운영 서버의 SLO로 | ||
| 사용하지 않는다. | ||
|
|
||
| ## 3. 실행 명령 | ||
|
|
||
| ```bash | ||
| docker compose up -d embedding-server | ||
|
|
||
| BGE_BENCHMARK_TOTAL_TEXTS=256 \ | ||
| BGE_BENCHMARK_ROUNDS=3 \ | ||
| ./gradlew bgeBatchPerformanceTest | ||
| ``` | ||
|
|
||
| 실행 시간은 6분 47초였다. 원본 Request 표본과 환경 지문은 다음 Git 제외 경로에 생성했다. | ||
|
|
||
| ```text | ||
| build/reports/bge-m3-batch/bge-m3-batch-latest.json | ||
| ``` | ||
|
|
||
| ## 4. 측정 결과 | ||
|
|
||
| | Batch | 요청 수 | 처리량 texts/s | p50 ms | p95 ms | p99 ms | 최대 ms | 평균 ms/Text | 최고 RSS MiB | 실패 | | ||
| |---:|---:|---:|---:|---:|---:|---:|---:|---:|---:| | ||
| | 1 | 768 | 5.85 | 166.03 | 222.97 | 254.01 | 362.37 | 170.81 | 2,176.80 | 0 | | ||
| | 4 | 192 | 11.12 | 356.99 | 409.23 | 442.71 | 459.94 | 89.93 | 2,176.80 | 0 | | ||
| | 8 | 96 | 13.57 | 585.59 | 637.61 | 681.90 | 683.95 | 73.69 | 2,176.68 | 0 | | ||
| | 16 | 48 | 15.15 | 1,027.70 | 1,224.44 | 1,498.55 | 1,543.40 | 66.02 | 2,185.02 | 0 | | ||
| | 32 | 24 | 17.08 | 1,857.51 | 1,975.70 | 1,985.38 | 1,988.18 | 58.56 | 2,176.68 | 0 | | ||
| | 64 | 12 | 17.38 | 3,681.53 | 3,719.82 | 3,726.36 | 3,728.00 | 57.55 | 2,259.72 | 0 | | ||
|
|
||
| 모든 Profile은 기대 요청 수인 `768 / Batch Size`와 실제 요청 수가 일치했다. 각 Profile의 성공 | ||
| Vector 수도 768개였고 응답 Model·Index·개수·1024차원·유한값 계약이 전 요청에서 유지됐다. | ||
|
|
||
| ## 5. 해석 | ||
|
|
||
| ### 5.1 Batch 16에서 32 | ||
|
|
||
| - 처리량 `15.15 → 17.08 texts/s`: `12.74%` 증가 | ||
| - 평균 Text 지연 `66.02 → 58.56ms`: 감소 | ||
| - p95 요청 지연 `1.22 → 1.98초`: Batch당 Text가 두 배이므로 증가 | ||
| - 최고 RSS 차이는 표본 변동 범위였고 Batch 32가 더 높지 않았다. | ||
|
|
||
| 문서 Worker는 한 Batch 응답을 받은 뒤 다음 요청을 보내는 순차 구조다. 따라서 전체 문서 처리량을 | ||
| 높이면서 단일 요청 지연을 2초 안팎으로 유지하는 Batch 32가 기존 16보다 낫다. | ||
|
|
||
| ### 5.2 Batch 32에서 64 | ||
|
|
||
| - 처리량 `17.08 → 17.38 texts/s`: `1.76%` 증가 | ||
| - Batch 32는 최고 처리량의 `98.27%` | ||
| - p95 요청 지연 `1.98 → 3.72초`: `1.88배` 증가 | ||
| - 최고 RSS `2,176.68 → 2,259.72MiB`: 약 `83.05MiB` 증가 | ||
|
|
||
| Batch 64는 처리량 최고값이지만 추가 이득이 작고 꼬리 지연과 메모리 증가가 크다. 기본값으로 | ||
| 선택하지 않고 더 큰 CPU·GPU 환경에서 재측정할 수 있는 상한으로 유지한다. | ||
|
|
||
| ## 6. 기본값 판정 | ||
|
|
||
| | 기준 | Batch 32 결과 | 판정 | | ||
| |---|---:|---| | ||
| | 실패율 0 | 0% | 통과 | | ||
| | 최고 처리량의 95% 이상 | 98.27% | 통과 | | ||
| | Batch 64 대비 p95 절감 | 약 46.89% | 통과 | | ||
| | Batch 64 대비 최고 RSS 절감 | 약 83.05MiB | 통과 | | ||
|
|
||
| 최종 판정은 다음과 같다. | ||
|
|
||
| ```text | ||
| EMBEDDING_DOCUMENT_BATCH_SIZE 기본값: 16 → 32 | ||
| 허용 범위: 1~64 유지 | ||
| Query Embedding 단건 API: 변경 없음 | ||
| ``` | ||
|
|
||
| ## 7. 자동 검증 | ||
|
|
||
| ### 7.1 Python 계약 테스트 | ||
|
|
||
| ```bash | ||
| python3 -m pytest -p no:cacheprovider \ | ||
| embedding-server/test_benchmark_batch_size.py \ | ||
| embedding-server/test_main.py | ||
| ``` | ||
|
|
||
| 일회성 Embedding Container에서 `28 passed`를 확인했다. | ||
|
|
||
| 검증 범위: | ||
|
|
||
| - 결정적 Corpus와 Batch Size 설정 검증 | ||
| - Percentile 선형 보간 | ||
| - Round별 Profile 순서 회전 | ||
| - Model·Index·개수·차원·유한값 검증 | ||
| - 처리량·p50·p95·p99·RSS 집계 | ||
| - 현재 기본값 유지·변경 신호 계산 | ||
| - 기존 `/embed`, `/embed/batch` 계약 회귀 | ||
|
|
||
| ### 7.2 실제 모델 Smoke | ||
|
|
||
| 64개 Text·1회 Smoke에서도 Batch Size 6종, 95개 요청이 모두 성공했다. 이 결과는 실행기와 RSS | ||
| 수집 경계 확인에만 사용하고 기본값 판단은 256개 Text·3회 확장 결과로 수행했다. | ||
|
|
||
| ### 7.3 Java 전체 회귀 | ||
|
|
||
| 첫 실행은 현재 PostgreSQL Container가 Host `5432`에 노출된 것과 Test 기본 포트 `55432`가 달라 | ||
| DB 기반 Test Context가 초기화되지 않았다. 저장소 설정을 변경하지 않고 Test Process에 실제 로컬 | ||
| 포트와 SSL Mode, 일회성 JWT를 주입해 다시 실행했다. | ||
|
|
||
| ```bash | ||
| DB_PORT=5432 \ | ||
| DB_SSLMODE=disable \ | ||
| JWT_SECRET=<test-only-value> \ | ||
| ./gradlew test | ||
| ``` | ||
|
|
||
| 결과: | ||
|
|
||
| ```text | ||
| tests=709 failures=0 errors=0 skipped=0 | ||
| ``` | ||
|
|
||
| ## 8. 한계와 후속 작업 | ||
|
|
||
| - CPU 단일 요청 Benchmark이므로 여러 Worker의 동시 요청 처리량은 포함하지 않는다. | ||
| - RSS는 1초 간격과 Profile 경계의 Container PID 1 표본이며 순간 Peak를 놓칠 수 있다. | ||
| - 입력은 결정적 한국어 문장 집합이지만 실제 운영 문서의 Token 길이 분포 전체를 대표하지 않는다. | ||
| - GPU 환경은 처리량·메모리 곡선이 다르므로 같은 명령으로 다시 측정해야 한다. | ||
| - Worker 수평 확장과 Queue Backpressure는 별도 성능 작업에서 검증한다. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.