diff options
| author | eunhhu <[email protected]> | 2026-09-26 05:29:44 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-26 05:29:48 +0100 |
| commit | f0bec42645aa845d1c1d71c6e18ac1521e808fcc (patch) | |
| tree | 4d4309ae29de36a5a984c2e5ad4a0424549eb2a7 /docs | |
| parent | 3b89ae82ea9468d54c60a63fd03b9a829748a0ac (diff) | |
| download | pure-f0bec42645aa845d1c1d71c6e18ac1521e808fcc.tar.gz pure-f0bec42645aa845d1c1d71c6e18ac1521e808fcc.zip | |
Benchmark the HTTP hosts against axum
A C load generator, an axum service with the same document contract (in
memory, fdatasync, and crash-safe replacement), and a runner that pins the
servers and the load to separate CPUs. On the Pi 5, Pure uses about 40%
less memory, wins with a new connection per request and with eight durable
writers (3.2x the crash-safe axum), but loses 5x on keep-alive reads and
1.5x on 64 durable writers: no persistent connections, one thread, eight
connection slots and two fsyncs per commit. Note 075 records the numbers
and the targets.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/research/075-http-host-against-axum.md | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/docs/research/075-http-host-against-axum.md b/docs/research/075-http-host-against-axum.md new file mode 100644 index 0000000..96814d3 --- /dev/null +++ b/docs/research/075-http-host-against-axum.md @@ -0,0 +1,54 @@ +# 075 — HTTP 호스트를 axum과 같은 조건에서 잰다 + +2026-09-26. 하네스 [`benchmarks/http`](../../benchmarks/http): C 부하 생성기(`load.c`, epoll, 연결 N개, keep-alive +선택), 같은 문서 계약을 axum 0.8로 구현한 기준 서비스(`axum-baseline`), 실행기(`run.py`). + +착수 기준: + +- 바꿀 결정: Pure의 HTTP 호스트(`server.c`)를 문서 계약 검사용 시제품으로 둘지, 일반적인 고성능 백엔드 + (axum, drogon) 이상을 목표로 다시 짤지. +- 남은 질문: 지금 어디서 지고 어디서 이기는가? 그 차이는 설계의 한계인가, 구현의 공백인가? +- 최소 확인: 같은 기기, 같은 코어 배정, 같은 부하 생성기, 같은 계약으로 잰다. 저장 보장 쓰기는 충돌에 + 안전한 기준(임시 파일·fsync·rename·디렉터리 fsync)과 안전하지 않은 기준을 둘 다 둔다. + +## 조건 + +Raspberry Pi 5(Cortex-A76 4코어, 8 GB), Linux 6.18 AArch64, 저장소는 SD 카드의 ext4. 서버는 CPU 0–2, +부하 생성기는 CPU 3에 고정했다(`taskset`). 문서 64개(64 B)와 1 MiB 문서 하나. 각 5초. Pure의 입력 예산은 +64 MiB(기본 4 MiB에서는 1 MiB 읽기 8개가 예산에 막혀 503을 낸다). axum은 tokio 다중 스레드 기본값. +PUT은 연결마다 자기 키 앞에 16 B를 넣고 받은 ETag로 다음 `If-Match`를 보낸다. + +## 결과 (성공 응답/초, 지연은 µs) + +| 작업 | Pure 프로그램 서버 | Pure 모듈 서버 | axum 메모리 | axum fdatasync | axum 충돌 안전 | +|---|---:|---:|---:|---:|---:| +| GET 64 B, 연결 64, 매번 새 연결 | 23,953 (p99 800) | 23,852 (p99 896) | 22,514 (p99 4,096) | 21,994 | 22,184 | +| GET 64 B, 연결 64, keep-alive | 23,839 | 23,892 | **117,857** (p99 1,024) | 126,643 | 125,429 | +| GET 1 MiB, 연결 8, keep-alive | 1,113 | 1,197 | 1,311 | 1,345 | 1,322 | +| PUT 16 B 저장 보장, 연결 64 | 50 (연결 오류 49) | 191 | (51,323, 저장 안 함) | 429 | 296 | +| PUT 16 B 저장 보장, 연결 8 | 52 | **171** | (23,512, 저장 안 함) | 74 | 54 | + +최대 RSS: Pure 모듈 서버 10.9 MiB, Pure 프로그램 서버 13.4 MiB, axum 15.4–18.3 MiB. 유휴 RSS: Pure 1.7 MiB, axum 2.7 MiB. + +## 해석 + +- **메모리는 이긴다**(최대 RSS 약 40% 적음). 새 연결마다 요청 하나일 때도 이긴다(p99가 4–5배 짧다). +- **keep-alive에서 5배 진다.** Pure 호스트는 응답마다 연결을 닫는다. 요청마다 TCP 연결·accept·poll 등록이 + 반복되어 약 24k/s가 한계다. 게다가 스레드 하나만 쓴다(axum은 3코어). +- **동시 저장 쓰기에서 진다.** 연결 슬롯 8개와 listen backlog 8 때문에 64개 연결을 받지 못하고(프로그램 서버는 + 연결 오류), 묶음 커밋도 한 번에 8개까지다. 커밋 한 번에 fsync가 두 번이다(데이터, 그다음 head). axum은 64개의 + fsync를 병렬로 내고 ext4 저널이 그것들을 합친다. 연결 8개에서는 묶음 커밋 덕에 충돌 안전 axum의 3.2배다. +- 1 MiB 읽기는 비슷하다(10% 차이). 모듈 서버는 GET 응답을 복사하고, 프로그램 서버는 빌려준다. + +모두 설계 방향(소유 스레드, 고정 슬롯, 무복사 전송, 묶음 커밋)의 한계가 아니라 호스트 구현의 공백이다. + +## 목표와 순서 (같은 하네스, 같은 기기) + +1. HTTP/1.1 지속 연결과 파이프라이닝, epoll, 연결 슬롯 수백 개. 목표: keep-alive GET에서 코어 하나로 axum(3코어)의 + 절반 이상. +2. 코어마다 소유 스레드 하나(`SO_REUSEPORT` 샤드). 목표: keep-alive GET ≥ axum, p99 ≤ axum. +3. 커밋당 fsync 한 번(head가 새 노드의 체크섬을 담아 복구가 검증), 큰 묶음, fsync와 다음 묶음 적용의 겹침. + 목표: 연결 64개의 충돌 안전 PUT ≥ 안전하지 않은 axum(429/s). +4. 모듈 서버의 무복사 GET. + +중단 기준: 어떤 단계든 메모리가 axum보다 커지거나, 저장 보장(확인 응답 = 저장됨, `kill -9` 검사)이 깨지면 그 방식은 버린다. |
