asm-gc gains (tcp-sendfile socket path) → builtin (90 lines) that issues SYS_SENDFILE(40) in a loop, streaming a file from fd → socket with no bounce through the Lumbda heap. Zero-copy kernel path for large responses. examples/http-static-server-sendfile.lsp (hybrid): small assets (≤ 16 KB) stay inline-cached as full HTTP responses; large assets cache only headers and stream the body via tcp-sendfile. 4-way race on i5-8350U, 100 PDF requests (2.56 MiB), concurrency 8: uncached 159 req/s 406 MiB/s 15.5 MB RSS cached 238 req/s 603 MiB/s 7.2 MB RSS sendfile 480 req/s 1226 MiB/s 4.2 MB RSS caddy 485 req/s 1238 MiB/s 37.1 MB RSS sendfile lands within 2% of caddy on throughput with 9x less peak RSS in a 27 KB binary vs caddy's 38 MB (1400x smaller). examples/http-static-server-adaptive.lsp (learning preload): per-URL hit counter persisted to www.hits every N requests. At boot, ranks and preloads top *cache-max* URLs from the prior run's data (cold-start falls back to a seed list). Cold requests beyond the seed promote on first hit. Drops heap-restore arena pattern since the server mutates persistent state every request; relies on GC build's mark-sweep. tests/bench-www-race.sh: adds sendfile variant on port 8083, auto-sizes PDF byte count from the on-disk whitepaper so a whitepaper rebuild doesn't desync the MiB/s calc. Whitepaper §11.7 "Static File Serving: Cache, Sendfile, and Adaptive Preload" documents the four variants, benchmark table, and the arena-vs-mutation tradeoff. §13 Future Work adds DAG-of-hot-paths predictive preload as the direction for > 1000-resource deployments where frequency-only ranking is too narrow.
152 lines
7.1 KiB
Bash
Executable file
152 lines
7.1 KiB
Bash
Executable file
#!/bin/bash
|
|
# bench-www-race.sh — asm-gc lumbda-www (uncached + cached + sendfile)
|
|
# vs caddy file-server on the same lumbda.com docroot. Small-request
|
|
# (index.html) and large-request (2.56 MiB whitepaper PDF) workloads,
|
|
# identical concurrency, adjacent runs. Reports req/s, MiB/s, and
|
|
# peak RSS for each server. The PDF size is pulled from the file on
|
|
# disk so a whitepaper rebuild does not desync the MiB/s calculation.
|
|
set -u
|
|
cd "$(dirname "$0")/.."
|
|
|
|
SMALL_N=${SMALL_N:-2000}
|
|
LARGE_N=${LARGE_N:-200}
|
|
CONCURRENCY=${CONCURRENCY:-8}
|
|
LUMBDA_PORT=${LUMBDA_PORT:-8080}
|
|
LUMBDA_CACHED_PORT=${LUMBDA_CACHED_PORT:-8082}
|
|
LUMBDA_SENDFILE_PORT=${LUMBDA_SENDFILE_PORT:-8083}
|
|
CADDY_PORT=${CADDY_PORT:-8081}
|
|
CADDY_BIN=${CADDY_BIN:-/home/fox/git/make_post_sell/caddy}
|
|
PDF_PATH="whitepaper/lumbda-whitepaper.pdf"
|
|
PDF_BYTES=$(wc -c < "$PDF_PATH")
|
|
PDF_MIB=$(python3 -c "print(f'{$PDF_BYTES / (1024*1024):.2f}')")
|
|
|
|
declare -a SPAWNED=()
|
|
cleanup() {
|
|
for pid in "${SPAWNED[@]}"; do kill -9 "$pid" 2>/dev/null; done
|
|
sleep 0.2
|
|
pkill -9 -u "$USER" -f 'http-static-server' 2>/dev/null || true
|
|
pkill -9 -u "$USER" -f 'caddy file-server' 2>/dev/null || true
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
make -s -C asm all
|
|
|
|
printf "\n═══════════════════════════════════════════════════════\n"
|
|
printf "asm-gc lumbda-www (uncached + cached + sendfile) vs caddy\n"
|
|
printf " %s small (/), %s large (/whitepaper.pdf %s MiB)\n" "$SMALL_N" "$LARGE_N" "$PDF_MIB"
|
|
printf " concurrency %s\n" "$CONCURRENCY"
|
|
printf "═══════════════════════════════════════════════════════\n"
|
|
|
|
# ── start three servers ──
|
|
|
|
# 1. lumbda-www uncached (reads file per request)
|
|
./asm/lumbda-gc < examples/http-static-server.lsp >/dev/null 2>&1 &
|
|
LUMBDA_PID=$!
|
|
SPAWNED+=("$LUMBDA_PID")
|
|
|
|
# 2. lumbda-www cached (in-memory hash-table of pre-built responses).
|
|
# Patch *port* to avoid colliding with the uncached server.
|
|
sed "s|(define \*port\* 8080)|(define *port* $LUMBDA_CACHED_PORT)|" \
|
|
examples/http-static-server-cached.lsp \
|
|
| ./asm/lumbda-gc >/dev/null 2>&1 &
|
|
LUMBDA_CACHED_PID=$!
|
|
SPAWNED+=("$LUMBDA_CACHED_PID")
|
|
|
|
# 3. lumbda-www sendfile (small assets inline-cached, big files streamed
|
|
# via SYS_SENDFILE — zero-copy kernel->socket). Port patched to avoid
|
|
# collisions with the other two lumbda servers.
|
|
sed "s|(define \*port\* 8080)|(define *port* $LUMBDA_SENDFILE_PORT)|" \
|
|
examples/http-static-server-sendfile.lsp \
|
|
| ./asm/lumbda-gc >/dev/null 2>&1 &
|
|
LUMBDA_SENDFILE_PID=$!
|
|
SPAWNED+=("$LUMBDA_SENDFILE_PID")
|
|
|
|
# 4. caddy file-server
|
|
"$CADDY_BIN" file-server --root www --listen ":$CADDY_PORT" >/dev/null 2>&1 &
|
|
CADDY_PID=$!
|
|
SPAWNED+=("$CADDY_PID")
|
|
|
|
# Wait for all four to be ready.
|
|
for _ in $(seq 1 40); do
|
|
a=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$LUMBDA_PORT/" 2>/dev/null || echo 0)
|
|
b=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$LUMBDA_CACHED_PORT/" 2>/dev/null || echo 0)
|
|
d=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$LUMBDA_SENDFILE_PORT/" 2>/dev/null || echo 0)
|
|
c=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$CADDY_PORT/" 2>/dev/null || echo 0)
|
|
[ "$a" = "200" ] && [ "$b" = "200" ] && [ "$d" = "200" ] && [ "$c" = "200" ] && break
|
|
sleep 0.1
|
|
done
|
|
kill -0 "$LUMBDA_PID" 2>/dev/null || { echo "lumbda-www (uncached) failed to start"; exit 1; }
|
|
kill -0 "$LUMBDA_CACHED_PID" 2>/dev/null || { echo "lumbda-www (cached) failed to start"; exit 1; }
|
|
kill -0 "$LUMBDA_SENDFILE_PID" 2>/dev/null || { echo "lumbda-www (sendfile) failed to start"; exit 1; }
|
|
kill -0 "$CADDY_PID" 2>/dev/null || { echo "caddy failed to start"; exit 1; }
|
|
|
|
# ── Byte-integrity checks ──
|
|
curl -s "http://localhost:$LUMBDA_PORT/whitepaper.pdf" -o /tmp/a-lumbda.pdf
|
|
curl -s "http://localhost:$LUMBDA_CACHED_PORT/whitepaper.pdf" -o /tmp/a-cached.pdf
|
|
curl -s "http://localhost:$LUMBDA_SENDFILE_PORT/whitepaper.pdf" -o /tmp/a-sendfile.pdf
|
|
curl -s "http://localhost:$CADDY_PORT/whitepaper.pdf" -o /tmp/a-caddy.pdf
|
|
for f in /tmp/a-lumbda.pdf /tmp/a-cached.pdf /tmp/a-sendfile.pdf /tmp/a-caddy.pdf; do
|
|
label=$(basename "$f" .pdf | sed 's/^a-//')
|
|
if cmp "$f" whitepaper/lumbda-whitepaper.pdf >/dev/null 2>&1; then
|
|
printf " pdf-integrity (%-8s): byte-identical\n" "$label"
|
|
else
|
|
printf " pdf-integrity (%-8s): DIFFERS\n" "$label"
|
|
fi
|
|
done
|
|
rm -f /tmp/a-lumbda.pdf /tmp/a-cached.pdf /tmp/a-sendfile.pdf /tmp/a-caddy.pdf
|
|
|
|
# ── benchmark one (pid, port, label, N, path) ──
|
|
bench_one() {
|
|
local pid="$1" port="$2" label="$3" n="$4" path="$5"
|
|
local peak=0 rss t0 t1
|
|
t0=$(date +%s.%N)
|
|
( seq 1 "$n" | xargs -P "$CONCURRENCY" -I_ \
|
|
curl -s -o /dev/null "http://localhost:$port$path" ) &
|
|
local curl_pid=$!
|
|
while kill -0 "$curl_pid" 2>/dev/null; do
|
|
rss=$(awk '/^VmRSS:/ {print $2}' /proc/$pid/status 2>/dev/null || echo 0)
|
|
[ "$rss" -gt "$peak" ] && peak=$rss
|
|
sleep 0.2
|
|
done
|
|
wait "$curl_pid" 2>/dev/null || true
|
|
t1=$(date +%s.%N)
|
|
local rps=$(python3 -c "
|
|
t = float('$t1') - float('$t0')
|
|
print(f'{$n / max(t, 1e-6):.0f}')
|
|
")
|
|
local mibs=0
|
|
if [ "$path" = "/whitepaper.pdf" ]; then
|
|
mibs=$(python3 -c "
|
|
t = float('$t1') - float('$t0')
|
|
print(f'{$n * $PDF_BYTES / (1024*1024) / max(t, 1e-6):.0f}')
|
|
")
|
|
printf " %-20s %6s req/s %4s MiB/s peak_rss_kb=%s\n" "$label" "$rps" "$mibs" "$peak"
|
|
else
|
|
printf " %-20s %6s req/s peak_rss_kb=%s\n" "$label" "$rps" "$peak"
|
|
fi
|
|
}
|
|
|
|
echo
|
|
echo "── small (/) ──"
|
|
bench_one "$LUMBDA_PID" "$LUMBDA_PORT" "lumbda-www uncached" "$SMALL_N" "/"
|
|
bench_one "$LUMBDA_CACHED_PID" "$LUMBDA_CACHED_PORT" "lumbda-www cached" "$SMALL_N" "/"
|
|
bench_one "$LUMBDA_SENDFILE_PID" "$LUMBDA_SENDFILE_PORT" "lumbda-www sendfile" "$SMALL_N" "/"
|
|
bench_one "$CADDY_PID" "$CADDY_PORT" "caddy file-server" "$SMALL_N" "/"
|
|
|
|
echo
|
|
echo "── large (/whitepaper.pdf, $PDF_MIB MiB) ──"
|
|
bench_one "$LUMBDA_PID" "$LUMBDA_PORT" "lumbda-www uncached" "$LARGE_N" "/whitepaper.pdf"
|
|
bench_one "$LUMBDA_CACHED_PID" "$LUMBDA_CACHED_PORT" "lumbda-www cached" "$LARGE_N" "/whitepaper.pdf"
|
|
bench_one "$LUMBDA_SENDFILE_PID" "$LUMBDA_SENDFILE_PORT" "lumbda-www sendfile" "$LARGE_N" "/whitepaper.pdf"
|
|
bench_one "$CADDY_PID" "$CADDY_PORT" "caddy file-server" "$LARGE_N" "/whitepaper.pdf"
|
|
|
|
echo
|
|
echo "── binary sizes (stripped) ──"
|
|
# Strip to temp for fair comparison — asm binaries ship stripped in prod.
|
|
cp asm/lumbda-gc /tmp/lumbda-gc-s && strip /tmp/lumbda-gc-s
|
|
printf " lumbda-gc %10s bytes\n" "$(du -b /tmp/lumbda-gc-s | cut -f1)"
|
|
printf " caddy %10s bytes (Go, v2.5.1, already stripped)\n" "$(du -b "$CADDY_BIN" | cut -f1)"
|
|
rm -f /tmp/lumbda-gc-s
|
|
|
|
kill -9 "$LUMBDA_PID" "$LUMBDA_CACHED_PID" "$LUMBDA_SENDFILE_PID" "$CADDY_PID" 2>/dev/null
|
|
wait "$LUMBDA_PID" "$LUMBDA_CACHED_PID" "$LUMBDA_SENDFILE_PID" "$CADDY_PID" 2>/dev/null || true
|