;;; http-client-bench.lsp — sequential HTTP load generator in Scheme ;;; ;;; Makes N requests to 127.0.0.1:PORT and reports elapsed wall time ;;; + requests/sec. Uses only the six tcp-* primitives, so it runs ;;; identically in Python, C, and asm. ;;; ;;; The earlier tests/web-benchmark.sh used curl — each curl fork+exec ;;; costs ~2 ms, swamping actual server work. This client keeps every ;;; request in-process: that irreducible cost disappears, so the real ;;; server throughput shows up. ;;; ;;; Usage: ;;; python3 lumbda.py --fast examples/http-client-bench.lsp ;;; ./c/lumbda examples/http-client-bench.lsp ;;; ./asm/lumbda < examples/http-client-bench.lsp ;;; ;;; Override N or PORT by pre-setting *n-requests* / *port* before load. (define *host* "127.0.0.1") (define *port* 8080) (define *n-requests* 500) (define *path* "/bench") (define *request* (string-append "GET " *path* " HTTP/1.0\r\n" "Host: " *host* "\r\n" "Connection: close\r\n\r\n")) ;;; Pass snap as arg so heap-restore can rewind per-request ;;; allocations on asm without invalidating the client loop's ;;; closure env. (define (one-request) (let ((sock (tcp-connect *host* *port*))) (if sock (begin (tcp-send sock *request*) (let ((resp (tcp-recv sock 8192))) (tcp-close sock) (if (and resp (> (string-length resp) 0)) 1 0))) 0))) (define (client-loop n ok snap) (if (= n 0) ok (let ((got (one-request))) (heap-restore snap) (client-loop (- n 1) (+ ok got) snap)))) (define t0 (current-time-ms)) (define ok (client-loop *n-requests* 0 (heap-snapshot))) (define t1 (current-time-ms)) (define elapsed-ms (- t1 t0)) (define rps (if (> elapsed-ms 0) (quotient (* *n-requests* 1000) elapsed-ms) 0)) (display "target : http://") (display *host*) (display ":") (display *port*) (display *path*) (newline) (display "requests : ") (display *n-requests*) (newline) (display "ok : ") (display ok) (newline) (display "elapsed : ") (display elapsed-ms) (display " ms") (newline) (display "rps : ") (display rps) (newline)