cache: move goroutine closure to separate function to save memory (#3353)

The goroutine closure was causing objects to be heap allocated.  Moving
it to a separate function fixes that.

```benchmark                                old ns/op     new ns/op     delta
BenchmarkCacheResponse/NoPrefetch-12     773           713           -7.76%
BenchmarkCacheResponse/Prefetch-12       878           837           -4.67%
BenchmarkHash-12                         9.17          9.18          +0.11%

benchmark                                old allocs     new allocs     delta
BenchmarkCacheResponse/NoPrefetch-12     9              8              -11.11%
BenchmarkCacheResponse/Prefetch-12       9              8              -11.11%
BenchmarkHash-12                         0              0              +0.00%

benchmark                                old bytes     new bytes     delta
BenchmarkCacheResponse/NoPrefetch-12     471           327           -30.57%
BenchmarkCacheResponse/Prefetch-12       471           327           -30.57%
BenchmarkHash-12                         0             0             +0.00%
```

Signed-off-by: Charlie Vieth <charlie.vieth@gmail.com>
Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Miek Gieben 2019-10-03 15:05:44 +01:00 committed by GitHub
parent aa96d6b443
commit f8551df272
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -29,28 +29,10 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
i, found := c.get(now, state, server)
if i != nil && found {
resp := i.toMsg(r, now)
w.WriteMsg(resp)
if c.prefetch > 0 {
ttl := i.ttl(now)
i.Freq.Update(c.duration, now)
threshold := int(math.Ceil(float64(c.percentage) / 100 * float64(i.origTTL)))
if i.Freq.Hits() >= c.prefetch && ttl <= threshold {
cw := newPrefetchResponseWriter(server, state, c)
go func(w dns.ResponseWriter) {
cachePrefetches.WithLabelValues(server).Inc()
plugin.NextOrFailure(c.Name(), c.Next, ctx, w, r)
// When prefetching we loose the item i, and with it the frequency
// that we've gathered sofar. See we copy the frequencies info back
// into the new item that was stored in the cache.
if i1 := c.exists(state); i1 != nil {
i1.Freq.Reset(now, i.Freq.Hits())
}
}(cw)
}
if c.shouldPrefetch(i, now) {
go c.doPrefetch(ctx, state, server, i, now)
}
return dns.RcodeSuccess, nil
}
@ -59,6 +41,29 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
return plugin.NextOrFailure(c.Name(), c.Next, ctx, crr, r)
}
func (c *Cache) doPrefetch(ctx context.Context, state request.Request, server string, i *item, now time.Time) {
cw := newPrefetchResponseWriter(server, state, c)
cachePrefetches.WithLabelValues(server).Inc()
plugin.NextOrFailure(c.Name(), c.Next, ctx, cw, state.Req)
// When prefetching we loose the item i, and with it the frequency
// that we've gathered sofar. See we copy the frequencies info back
// into the new item that was stored in the cache.
if i1 := c.exists(state); i1 != nil {
i1.Freq.Reset(now, i.Freq.Hits())
}
}
func (c *Cache) shouldPrefetch(i *item, now time.Time) bool {
if c.prefetch <= 0 {
return false
}
i.Freq.Update(c.duration, now)
threshold := int(math.Ceil(float64(c.percentage) / 100 * float64(i.origTTL)))
return i.Freq.Hits() >= c.prefetch && i.ttl(now) <= threshold
}
// Name implements the Handler interface.
func (c *Cache) Name() string { return "cache" }