github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/cache/alloc.go (about)

     1  // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package cache
     6  
     7  const (
     8  	// The min size of a byte slice held in an allocCache instance. Byte slices
     9  	// smaller than this value will not be cached.
    10  	allocCacheMinSize = 1024
    11  	// The max size of a byte slice held in an allocCache instance. Byte slices
    12  	// larger than this value will not be cached.
    13  	allocCacheMaxSize    = 64 * 1024
    14  	allocCacheCountLimit = 16
    15  	// The maximum size of data held in a single allocCache instance. There will
    16  	// be O(num-cpus) allocCaches per Cache.
    17  	allocCacheSizeLimit = 512 * 1024
    18  )
    19  
    20  // allocCache implements a small cache for the byte slice allocations used by
    21  // the Cache. If an allocCache is empty, allocations are passed through to the
    22  // Go runtime allocator. An allocCache holds a list of recently freed buffers
    23  // that is capped at 16 entries and 512KB in size. When a buffer is freed, the
    24  // existing cached buffers are trimmed until the new entry can fit with the
    25  // count and size limits. When a buffer is allocated, this cached list is
    26  // walked from beginning to end looking for the first buffer which is the same
    27  // size class as the allocation. Size classes are multiples of 1024.
    28  type allocCache struct {
    29  	size int
    30  	bufs [][]byte
    31  }
    32  
    33  func sizeToClass(size int) int {
    34  	return (size - 1) / 1024
    35  }
    36  
    37  func classToSize(class int) int {
    38  	return (class + 1) * 1024
    39  }
    40  
    41  func (c *allocCache) alloc(n int) []byte {
    42  	if n < allocCacheMinSize || n >= allocCacheMaxSize {
    43  		return make([]byte, n)
    44  	}
    45  
    46  	class := sizeToClass(n)
    47  	for i := range c.bufs {
    48  		if t := c.bufs[i]; sizeToClass(len(t)) == class {
    49  			j := len(c.bufs) - 1
    50  			c.bufs[i], c.bufs[j] = c.bufs[j], c.bufs[i]
    51  			c.bufs[j] = nil
    52  			c.bufs = c.bufs[:j]
    53  			c.size -= len(t)
    54  			return t[:n]
    55  		}
    56  	}
    57  
    58  	return make([]byte, n, classToSize(class))
    59  }
    60  
    61  func (c *allocCache) free(b []byte) {
    62  	n := cap(b)
    63  	if n < allocCacheMinSize || n >= allocCacheMaxSize {
    64  		return
    65  	}
    66  	b = b[:n:n]
    67  
    68  	for c.size+n >= allocCacheSizeLimit || len(c.bufs) >= allocCacheCountLimit {
    69  		i := len(c.bufs) - 1
    70  		t := c.bufs[i]
    71  		c.size -= len(t)
    72  		c.bufs[i] = nil
    73  		c.bufs = c.bufs[:i]
    74  	}
    75  	c.bufs = append(c.bufs, b)
    76  	c.size += n
    77  }