- M1: ProcessingQueue throughput baseline (GPU DB pkm, read-only) - M2: MLX gemma-4 26b-a4b 동시 처리 capacity (Mac mini :8801) - M3: bge-m3 batch embedding throughput (GPU Ollama :11434) 3 보고서 모두 4.0 가드 준수 (compose/migration/queue/worker restart/source_channel insert/SearXNG 도입 0건). trade-in 직전 untracked sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.0 KiB
M3 — bge-m3 batch 임베딩 throughput Bench
측정일: 2026-05-02
대상: GPU server Ollama :11434 bge-m3 (latest, 1.2GB, 1024d)
범위: read-only /api/embed 호출만. 4.0 가드 준수 (compose / migration / queue / worker restart / source_channel insert / SearXNG 도입 모두 0건). Ollama 모델/컨테이너 변경 없음.
bench 스크립트: /tmp/bench_M3_embed.py (격리, GPU 서버 내 실행)
plan 참조: ~/.claude/plans/users-hyungiahn-library-cloudstorage-sy-binary-dongarra.md §4.
1. 사전 점검
| 항목 | 상태 |
|---|---|
Ollama /api/tags healthcheck |
OK (gemma4:e4b-it-q8_0, bge-m3:latest 2개) |
| 진행 중 embed worker (사전) | embed pending 17건, processing 0건 — 운영 worker idle 직전 |
| 측정 방식 | 단발 호출 3회씩, batch 1/8/32 — 운영 영향 최소 |
embed worker 와 시간대 분리 — 측정 호출이 ~1초 미만이라 worker idle 시간에 끼어들지 않음.
2. 시나리오
| 항목 | 값 |
|---|---|
| 입력 텍스트 | ~250 토큰 한·영 mixed (ASME B16.5 / KGS Code / 가스기사 sample), chunk #N 으로 변형해서 unique 32 inputs 생성 |
| API | Ollama /api/embed, body {model, input: [...]} |
| measure | 각 batch_size 당 3회 반복 |
| batch_size | 1, 8, 32 (plan 기본 범위) — 128 미실시 |
응답: 각 batch 마다 embeddings: [[1024d], ...], dim=1024 일관.
3. 측정 결과
batch=1 (3회)
repeat 1: ok elapsed=0.10s embeddings=1 dim=1024 throughput=10.7 chunks/s
repeat 2: ok elapsed=0.08s embeddings=1 dim=1024 throughput=12.7 chunks/s
repeat 3: ok elapsed=0.08s embeddings=1 dim=1024 throughput=12.6 chunks/s
batch=8 (3회)
repeat 1: ok elapsed=0.12s embeddings=8 dim=1024 throughput=67.9 chunks/s
repeat 2: ok elapsed=0.12s embeddings=8 dim=1024 throughput=65.2 chunks/s
repeat 3: ok elapsed=0.12s embeddings=8 dim=1024 throughput=68.6 chunks/s
batch=32 (3회)
repeat 1: ok elapsed=0.26s embeddings=32 dim=1024 throughput=122.6 chunks/s
repeat 2: ok elapsed=0.25s embeddings=32 dim=1024 throughput=126.4 chunks/s
repeat 3: ok elapsed=0.25s embeddings=32 dim=1024 throughput=130.5 chunks/s
| batch | n | avg (s) | min (s) | max (s) | throughput (chunks/s) | error |
|---|---|---|---|---|---|---|
| 1 | 3 | 0.088 | 0.079 | 0.106 | 11.4 | 0 |
| 8 | 3 | 0.119 | 0.117 | 0.123 | 67.2 | 0 |
| 32 | 3 | 0.253 | 0.245 | 0.261 | 126.4 | 0 |
관찰:
- 모든 batch 0 error.
- batch 32 가 batch 1 대비 throughput 11배 — GPU batch 가속 큰 효과.
- batch 8 → 32 는 1.88×만 더 빨라짐 → batch 32 부근에서 throughput 증가 곡선 완만 (batch 128 측정 굳이 필요 없음을 시사).
4. plan 4.1 게이트 판정 (M3 관련)
게이트 4 — 150 chunks/일 임베딩 ≤ 1분 (batch 32)
| 항목 | 값 |
|---|---|
| 150 chunks 처리에 필요한 batch 수 | ⌈150 / 32⌉ = 5 batches |
| 1 batch 평균 시간 | 0.253s |
| 150 chunks 총 시간 | 5 × 0.253s = 1.27s |
| 임계값 | ≤ 60s |
| 판정 | ✓ PASS (여유 47×) |
압도적 여유. embed 자원은 LocalResearch 도입의 병목이 아님.
5. 게이트 5 — 1C/1D 충돌 점검 (read-only schema 분석)
-- documents.source_channel 분포
news 9024, manual 638, law_monitor 206, drive_sync 95, inbox_route 6, memo 6, NULL 1
-- documents.content_origin 분포
extracted 9976
-- process_stage enum values
extract, classify, embed, preview, summarize, chunk, stt, thumbnail, deep_summary, markdown
| 점검 | 현황 | LocalResearch 영향 | 판정 |
|---|---|---|---|
documents.source_channel='web' 값 |
미사용 (현재 6개 값 + NULL 1건) | 신규 값 추가 필요. CHECK 제약 없음. 충돌 0 | ✓ |
documents.content_origin='imported' 값 |
0건 (extracted 9976만) | Phase 1A 에서 정의된 'imported' 슬롯이 비어있어 LocalResearch 가 자연스럽게 활용 가능 | ✓ |
documents.md_content (Phase 1A) |
nullable, optional-use | LocalResearch가 web→markdown 변환 후 동일 컬럼 활용 → Phase 1C <MarkdownDoc> 렌더러 자동 재사용 |
✓ (오히려 시너지) |
document_lineage 테이블 (Phase 1A) |
빈 테이블, RESTRICT FK | LocalResearch web 문서의 출처 추적 (search_query → urls)에 자연 활용 | ✓ (오히려 시너지) |
process_stage enum |
10개 stage. fetch/web_extract 부재 | LocalResearch Phase 2 (수집기) 진입 시 ADD VALUE migration 필요. 본 feasibility 단계에선 변경 0. Phase 1C/1D 와는 stage 충돌 0 (markdown stage 공존 가능) | ✓ |
Phase 1C <MarkdownDoc> UI |
미배포 (다음 차례) | LocalResearch 와 같은 컴포넌트 재사용 — 충돌 0, 시너지 | ✓ |
| Phase 1D pilot (30건 quality 측정) | 미시작 | LocalResearch 와 측정 시간대 분리만 하면 무관 | ✓ |
판정: ✓ PASS. 인접 트랙과의 schema/queue/UI 충돌 0. Phase 1A markdown canonical layer 가 오히려 LocalResearch 진입 기반 인프라 역할.
6. LocalResearch Phase 1 진입 게이트 종합 판정
| # | 게이트 | 측정값 | 임계값 | 판정 |
|---|---|---|---|---|
| 1 | queue capacity 여유 (M1) | classify peak +1.5%p, 전체 LLM stage +2.5%p (peak day) | ≤ 10% 추가 부하 | ✓ PASS |
| 2 | Gemma 30건/일 (M2) | 30 × 3.51s = 1.76분 (concurrency 1) | ≤ 10분 | ✓ PASS |
| 3 | concurrency 2 마진 (M2) | error_rate 0% (단 latency 9.5×) | error_rate = 0 | ✓ PASS |
| 4 | 임베딩 150 chunks/일 (M3) | 5 × 0.253s = 1.27s (batch 32) | ≤ 1분 (60s) | ✓ PASS |
| 5 | 1C/1D 충돌 (M3) | source_channel/content_origin/md_content/lineage 모두 비충돌 | 충돌 없음 | ✓ PASS |
종합: PASS (5/5)
추가 관찰:
- M2 N=2 latency 9.5× 증가 는 본질적 MLX concurrency=1 특성으로 새로운 위험 아니지만, Phase 1 본격 plan 단계에서 web 요약 워커는 background queue + concurrency 1 직렬 (실시간 아님) 로 설계 권장. ask 트래픽과 동일 시간대 경합 시 양쪽 모두 5~10× 지연 발생.
- content_origin='imported' 슬롯이 0건으로 비어있는 점이 인상적 — Phase 1A 가 LocalResearch 진입을 의식하지 않고 설계됐는데도 자연스러운 자리 확보. document_lineage 테이블도 동일.
- process_stage 'fetch' / 'web_extract' / 'summarize_web' 추가는 LocalResearch Phase 2 본격 plan 단계 작업. 기존 enum 10개와 충돌 없음. ORM enum 동시 갱신 필요 (메모리
feedback_orm_db_enum_sync.md).
7. STOP — 사용자 검토 대기
plan §4.2 명시된 Stop Point 도달:
M3 보고서 + 게이트 판정 표 작성 직후 작업 정지. LocalResearch Phase 1 상세 plan 작성, SearXNG 도입, 코드 변경, plan 모드 재진입 등 일체 금지. 사용자가 게이트 판정 검토 후 다음 액션을 명시적으로 지시할 때까지 대기.
본 reports 3개 (local_research_M1_throughput.md + _M2_mlx_capacity.md + _M3_embedding_throughput.md) 가 dashboard 산출물. 코드/스키마 변경 0, inventory soft lock 위반 0, queue stage 변경 0.
다음 액션은 사용자 결정.