aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-26 05:29:44 +0100
committereunhhu <[email protected]>2026-09-26 05:29:48 +0100
commitf0bec42645aa845d1c1d71c6e18ac1521e808fcc (patch)
tree4d4309ae29de36a5a984c2e5ad4a0424549eb2a7 /docs
parent3b89ae82ea9468d54c60a63fd03b9a829748a0ac (diff)
downloadpure-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.md54
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` 검사)이 깨지면 그 방식은 버린다.