← Writing

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.SliceHeader plus unsafe.Pointer bypasses 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/http gives you. Keep the danger in one small, deliberate place and put a recover() around it.