Skipping net/http: 249,000 requests per second in Go
The result: 249,000 req/s on localhost, 17,000+ over a real network, zero panics, zero leaks.
1. The baseline
- Stock
net/http, tuned: around 9,000 req/s. - Keep-alives off: under 3,000.
- The cost is the per-request correctness work. Good for production, in the way for a load generator.
2. The connection
type UnsafeConnection struct {
conn net.Conn
buffer []byte
bufferPtr unsafe.Pointer
isReady int64
}
- 512 workers share 1,024 persistent TCP connections.
- Nothing reconnects mid-run, so setup time never lands in the numbers.
- Buffers are preallocated and reused, so the GC has almost nothing to do.
3. Zero-copy reads
func safeUnsafeByteSlice(ptr unsafe.Pointer, length int) []byte {
if ptr == nil || length <= 0 {
return make([]byte, 0)
}
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(ptr),
Len: length,
Cap: length,
}))
}
- Builds a slice straight over raw memory: no copy, no allocation.
reflect.SliceHeaderplusunsafe.Pointerbypasses Go’s memory model. Fast, and actually dangerous.
4. Containing the blast radius
func (worker *UnsafeWorker) safeWorkerLoop(client *UnsafeBenchmarkClient) {
defer func() {
if r := recover(); r != nil {
// Log and keep going
}
}()
// Write request, read response, track success/errors
}
- Every worker’s hot loop runs under
recover(). - One bad worker logs and dies. The other 511 keep going.
- Unsafe in the small, stable in the large.
5. Measuring honestly
func RunCrashProofUnsafeBenchmark() {
for i := 0; i < NUM_BENCHMARKS; i++ {
// Launch all workers with sync.WaitGroup
// Sleep for the benchmark duration
// Collect all request counts
// Calculate RPS, mean, median, stddev, etc.
}
}
- 15 full cycles, reporting mean, median, and standard deviation.
- The number that matters is the coefficient of variation (stddev ÷ mean): under 5% is excellent, under 10% good, under 20% moderate, above that poor.
Run 1: 248,210 req/s
Run 2: 249,379 req/s
Run 3: 247,844 req/s
...
Mean: 248,477 req/s
Median: 248,210 req/s
Std deviation: 637 req/s
Coefficient of var.: 0.26%
- 0.26% CV means the speed is repeatable, not a lucky spike.
Notes
- Don’t ship this.
unsafe, a hand-rolled pool, and slices over raw pointers are a recipe for memory corruption in production. - It’s fine for load generation. You control the tool, and the safety you give up was only ever protecting you.
- Takeaway: Go has far more headroom than
net/httpgives you. Keep the danger in one small, deliberate place and put arecover()around it.