sync.Pool has a somewhat surprising behavior that it has processor-local copies (https://victoriametrics.com/blog/go-sync-pool/). So on large machines (mine has GOMAXPROCS=32 default), we can end up with 32 copies of items stored in sync.Pool - even without concurrent usage.
Prometheus uses a pool for gzip:
|
gz := gzipPool.Get().(*gzip.Writer) |
. With 32 copies of the gzip writer, we end up with ~10mb heap usage. Which isn't
crazy but is a pretty substantial amount for an otherwise very small application doing very little allocations/GC, and serving even a tiny number of metrics.
The reason sync.Pool does this is to improve throughput by avoiding synchronization. However, in a scraping setup I would think that its very uncommon to have much concurrency (maybe you have a couple of scrapers, but I'd imagine the median is 1 scraper)
Below shows my application's memory usage after disabling gzip due to this:
So at least in my specific case, it seems like it might be more optimal to use a sync.Pool-like structure that doesn't have this per-processor copying mechanism.
sync.Poolhas a somewhat surprising behavior that it has processor-local copies (https://victoriametrics.com/blog/go-sync-pool/). So on large machines (mine has GOMAXPROCS=32 default), we can end up with 32 copies of items stored in sync.Pool - even without concurrent usage.Prometheus uses a pool for gzip:
client_golang/prometheus/promhttp/http.go
Line 660 in 9bf26f7
The reason
sync.Pooldoes this is to improve throughput by avoiding synchronization. However, in a scraping setup I would think that its very uncommon to have much concurrency (maybe you have a couple of scrapers, but I'd imagine the median is 1 scraper)Below shows my application's memory usage after disabling gzip due to this:
So at least in my specific case, it seems like it might be more optimal to use a
sync.Pool-like structure that doesn't have this per-processor copying mechanism.