요약
Metal offload를 -ngl 34로 유지한 Qwen 임베딩의 동시 요청 시험에서 76개 응답이 모두 유한한 벡터를 반환했다. 이후 호환 API에 실행 동시성·대기열·시간 제한과 취소 처리를 적용하고 별도 후보 및 최종 경로를 검증했다. 결과는 제한된 요청 수용과 수치 유효성의 근거이지, 최대 처리량이나 속도 향상을 입증하는 자료는 아니다.
방법과 수집 시점
2026년 10월에 직접 호출과 호환 API 경로를 비교했다. 이 연구는 Metal 수치 검증의 후속 확장이다. native 설정은 -ngl 34, context 131072, batch와 ubatch 각각 32768, 실행 슬롯 4개였다. 런타임의 공개 배경은 llama.cpp를 참고할 수 있다 [1].
짧은 입력은 약 16 tokens, 중간 입력은 2562 tokens인 같은 문장을 반복했다. 각 경로에서 동시 요청 수를 1·4·10으로 바꾸고, 짧은 입력 4·8·10개와 중간 입력 2·4·10개를 보냈다. 경로당 38개, 합계 76개 요청이었다. 다른 GPU 작업도 함께 실행됐다.
결과
수치 유효성과 응답 시간은 별개였다
모든 baseline 응답이 유한한 벡터를 반환했다. 다만 반복 입력과 cache 상태, 공유 부하가 섞였으며 결과가 동시 요청 수에 따라 단조롭게 변하지 않았다. 예를 들어 호환 API의 중간 입력 p95는 동시 요청 4개에서 0.074초였지만 10개에서는 18.964초였다. 직접 호출의 중간 입력 p95도 각각 30.179초와 17.206초였다. 이 차이로 일반적인 성능 우열이나 적정 동시성을 단정할 수 없다.
Connection pool과 실행 동시성을 분리했다
기존 호환 API의 HTTP pool은 최대 연결 100개와 keepalive 20개였지만, 실제 임베딩 실행 동시성은 2개였다. Pool 크기를 늘리는 것은 모델이 동시에 처리할 수 있는 요청을 늘리는 것과 다르다.
적용한 요청 수용 정책은 다음과 같다.
| 제어 항목 | 값 또는 동작 |
|---|---|
| 실행 중인 임베딩 요청 | 최대 2개 |
| 대기열 | 최대 64개, FIFO |
| 대기 시간 | 최대 120초 |
| HTTP 시간 제한 | connect 5초, pool 5초, write 30초, read 300초 |
| Upstream HTTP 전체 시간 | 최대 300초, 대기 시간과 별도 |
| 과부하 | HTTP 429, Retry-After: 1 |
| 확인 대상 Qwen float 벡터가 유효하지 않음 | HTTP 502 |
| 취소 | 대기 요청 제거; 실행 요청은 upstream HTTP 작업이 끝날 때까지 실행 허가 유지 |
대기 120초와 upstream 300초는 별도이므로 client의 전체 시간 예산은 read timeout과 같지 않다. HTTP 중단이 native 계산의 즉시 종료까지 보장하지는 않는다. 입력 byte 수나 파일 확장자에 따른 새로운 제한은 추가하지 않았다.
flowchart TD
R["임베딩 요청"] --> S{"실행 허가 사용 가능? 최대 2개"}
S -->|"예"| U["Upstream HTTP 작업: 최대 300초"]
S -->|"아니요"| Q{"대기열 수용 가능? 최대 64개"}
Q -->|"예"| W["FIFO 대기: 최대 120초"]
Q -->|"아니요: 과부하"| E["HTTP 429: Retry-After 1"]
W -->|"실행 허가 획득"| U
U --> V{"확인 대상 Qwen float 벡터 유효?"}
V -->|"아니요"| B["HTTP 502"]
후보 검증과 최종 경로 검증
첫 중간 입력 시험은 client의 90초 deadline을 넘겨 실패했다. 이를 성공으로 처리하지 않고 후보를 중단한 뒤, client deadline을 240초로 늘려 서로 다른 중간 입력 10개로 재검증했다.
| 격리 후보 시험 | 요청 수 | 전체 경과(초) | p50(초) | p95(초) |
|---|---|---|---|---|
| 짧은 입력 | 10 | 7.607 | 1.009 | 7.602 |
| 서로 다른 중간 입력, 재검증 | 10 | 114.822 | 55.302 | 114.819 |
두 시험의 응답은 모두 유한했다. 중간 입력의 최대 대기 시간은 91.2477초였고 종료 시 실행·대기 요청은 모두 0이었다. 240초는 이 시험에는 충분했지만 대기와 upstream의 최대 합계인 420초를 모두 포괄하지는 않는다.
적용 후 한국어·코드 2개 입력 묶음의 유한한 2560차원 벡터를 확인했다. 별도의 일반 사용자 검색 경로에서는 기존 합성 문서에 대한 동시 요청 10개가 모두 HTTP 200과 예상 문서 일치 검증을 통과했다. 이 짧은 검색 시험의 p50은 1.253초, p95는 2.057초, 전체 경과는 2.059초였다.
한계
반복 입력 baseline, 서로 다른 중간 입력 후보 시험, 짧은 검색 검증은 workload가 달라 직접 비교할 수 없다. 작은 표본의 p95는 운영 SLA가 아니다. 공유 GPU 부하의 크기와 변화도 통제하지 않았다.
별도 offline 시험 38개 통과는 요청 수용 정책과 관련 변경의 제한된 기능 검증이며 전체 소프트웨어나 모든 모델의 검증이 아니다. Bulk indexing, 최대 처리량, 다수 사용자의 지속 부하와 모든 입력의 의미적 임베딩 품질은 입증하지 않았다. 정책과 버전 상태는 2026년 10월 기준이다.
참고자료
- llama.cpp: https://github.com/ggml-org/llama.cpp