본문으로 건너뛰기
버전: In Development

벤치마크

개요의 요약 표 뒤에 있는 환경별 전체 수치입니다. 모든 측정은 Python 3.11 + GIL (프로덕션 기본) 기준입니다. Python 3.14t free-threaded 비교는 Free-Threaded Python 참조.

환경 A — 순수 DB Client

무엇을 격리하는가. FastAPI 없음, 모델 추론 없음, HTTP loadgen 없음. Python 루프가 batch_read를 직접 호출 — 주변 스택을 최대한 얇게. aerospike-py 우위가 가장 크게 드러나는 조건입니다.

설정

항목
출처benchmark/results/20260416_134243/report.md
Clientofficial (sync C), official-async (executor 래핑), py-async (aerospike-py)
Set9
Batch size50, 200
동시성10
Iteration30

종합 결과

Clientavg mean (ms)avg p99 (ms)avg TPS
official107.56195.34138.2
official-async110.64211.33125.7
py-async (aerospike-py)22.45120.67373.7
py-async vs officiallatency 4.8× 빠름1.6×TPS 2.7× 높음
py-async vs official-async4.9× 빠름1.8×3.0×

Set별 비교 (official vs aerospike-py)

setbatchofficial mean (ms)official p99aerospike-py meanaerospike-py p99mean speedupTPS speedup
set_150110.23200.1419.74105.465.6×4.3×
set_1200127.06206.1930.53194.054.2×3.4×
set_250121.76210.2415.5734.687.8×5.8×
set_2200110.86194.8224.98124.864.4×3.2×
set_350108.00184.3218.53103.225.8×5.3×
set_3200128.85220.1523.35110.905.5×4.0×
set_450109.15206.1118.18116.926.0×5.3×
set_4200118.69195.7723.12109.325.1×3.5×
set_550113.21195.2725.57301.284.4×3.0×
set_5200122.26197.8926.85148.114.6×3.8×
set_650115.30210.7418.87103.986.1×5.2×
set_6200123.93261.4130.47122.364.1×3.6×
set_750115.34190.6817.8344.866.5×5.0×
set_7200126.81215.8741.17133.613.1×2.1×
set_85013.9296.2715.2097.950.9×0.9×
set_820019.95102.1216.27116.031.2×1.0×
set_950111.80217.1416.9295.596.6×5.6×
set_9200139.05210.9520.95108.816.6×4.8×
set_8는 빈 set

0% found rate — 공식 client가 success path 보다 not-found 응답을 더 빨리 처리. "fast not-found" 시간을 반영하므로 실제 read latency 비교는 의미 없음. 나머지 모든 set은 4–8× mean speedup.

official-async (sync C client를 loop.run_in_executor로 래핑)는 모든 set에서 bare sync client보다 약간 더 느림 — 매 요청마다 thread pool hop 비용이 추가되기 때문. 따라서 aerospike-py가 official-async에 대해 보이는 격차는 official에 대한 격차보다 일관되게 (batch=50 기준 mean speedup 6.0–7.1×, official 대비는 4.4–6.6×).

환경 B — uvicorn ASGI Only

무엇을 격리하는가. batch_read 주변에 FastAPI + uvicorn을 추가하되 모델 추론은 없음. "Key-value lookup 앞에 REST API" 형태.

설정

항목
출처benchmark/results/asgi_20260416_134730/asgi-report.md
동시성5
Iteration50

파이프라인 분해 (latency, ms)

Clienttotal meanp50p90p95p99aerospike stepinferencehttp stepTPSerrors
official289.56289.36429.74461.15473.60280.001.55300.6916.60
aerospike-py228.49189.56457.42468.03497.09221.121.46258.1019.41
  • Total mean: −21% (290 → 228 ms)
  • Aerospike step: −21% (280 → 221 ms) — wall time 대부분이 DB call이므로 client 이득이 E2E에 그대로 전달
  • TPS: +17% (16.6 → 19.4 req/s)
  • p99는 거의 동등 — 동시성 5에서는 Tokio 모델이 GIL을 강하게 stress하지 않아 tail이 벌어지지 않음

더 높은 동시성 변형 (concurrency=10, 200 iter)

같은 환경에서 동시성을 올린 두 번째 run (benchmark/results/asgi_20260416_135234/asgi-report.md):

Clienttotal meanp95TPSerrors
official465.81885.6420.90
aerospike-py580.70986.9313.956
포화 상태에서의 backpressure

이 run은 테스트 하네스가 포화 상태 — aerospike-py 측에서 56 errors가 발생 (서버 풀이 흡수할 수 없는 부하를 native async client가 더 많이 발사하면서 reject/timeout). 깨끗한 apples-to-apples 비교가 아님. backpressure 튜닝 없이 native async client가 over-issue하면 이런 양상이 가능함을 보여주는 증거. 이를 해결하는 gathersingle recipe는 Bottleneck Analysis 참조.

환경 C — uvicorn + DLRM (실 serving)

무엇을 격리하는가. 프로덕션 형태의 전체 파이프라인: HTTP request → key 추출 → batch_read(9 set × 200 keys) → feature build → DLRM 추론 (PyTorch CPU) → response. 실제 recsys serving pod에 가장 가까운 측정.

설정

항목
출처benchmark/results/k6-runtime-client-comparison.md ("3.11 + GIL, stage OFF")
이미지aerospike-benchmark:latest (Python 3.11.14)
Loadgenk6 10 VUs × 60s, scenario 4개
Pod2 replicas, 4 CPU / 4 GiB request, 8 CPU / 8 GiB limit

두 endpoint (/predict/official/sample, /predict/py-async/sample)이 같은 pod에 있어 k6가 교대 호출 — 서버 상태와 네트워크가 완전히 동일.

k6 client 측 latency (single mode, 10 VUs × 60s)

지표aerospike-pyofficialaerospike-py 우위
avg118 ms146 ms−19%
median134 ms148 ms−10%
p90173 ms293 ms−41% 🔥
p95189 ms324 ms−42% 🔥

k6 client 측 latency (gather mode — 9-set fan-out)

지표aerospike-pyofficial우위
p95234 ms266 ms−12%

gather mode는 9개 batch_read 호출을 asyncio.gather(...)로 묶음. 두 client 모두 result 변환 단계에서 GIL 직렬화에 부딪혀 격차가 줄어듦. 자세한 분석은 Bottleneck Analysis 참조.

서버 측 Prometheus 지표 (같은 run)

predict_duration_seconds p95 (FastAPI E2E, pod 내부 측정):

Clientp95
aerospike-py202 ms
official274 ms

aerospike_batch_read_all_duration_seconds p95 (앱 레벨 — batch_read + to_dict + demux):

Clientp95
aerospike-py137 ms
official252 ms

서버 측과 client 측 수치가 같은 방향으로 일치 — 둘 사이의 차이 (~13 ms)는 네트워크 RTT + gateway + connection setup.

Mode별 요약 (모두 p95, ms)

Modeaerospike-pyofficialaerospike-py 우위
single189324−42%
gather234266−12%
merge_gather (aerospike-py 전용)202
stress (0→20→50 VUs ramp)592

환경별 패턴

DB call 주변에 layer가 추가될수록 비율은 압축되지만 (mean 4.8× → 1.24×), 상위 percentile 우위는 유지됩니다 — aerospike-py는 I/O 동안 GIL을 해제하는 반면, 공식 client는 run_in_executor로 GIL 획득을 직렬화하여 tail이 튀기 때문. 환경 C에서 mean은 −19% 에 그치지만 p95는 여전히 −42% 인 이유.

추가 조사 거리: GIL 자체가 제거되면 어떻게 되는지는 Free-Threaded Python, 추가 −33%를 주는 gathersingle recipe는 Bottleneck Analysis 참조.