bufpool

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2018 License: MIT Imports: 4 Imported by: 2

README

bufpool

Go library for using a free-list of byte buffers.

Background

A few excellent articles were published that resurfaced the topic of using free-lists of byte.Buffer structures in Go. Because the Go runtime includes facilities to manage free-lists, I was curious about the performance characteristics of various methods of achieving this goal, and decided to benchmark these options.

Description

Several free-list algorithms are included to allow selection of whichever performs best for a particular application scenario.

  • NewChanPool -- uses Go channels to provide concurrent access to internal structures
  • NewLockPool -- uses sync.Mutex to provide concurrent access to internal structures
  • NewSyncPool -- uses sync.Pool to provide concurrent access to internal structures
  • NewPreAllocatedPool -- wrapper around github.com/karrick/gopool, which uses Go channels to provide concurrent access to internal structures

Each of these algorithms performs quite differently when run on different architectures, and with different amounts of concurrency.

Benchmark functions are provided to determine which buffer free-list algorithm best suits a given application.

Usage

Documentation is available via GoDoc.

Example

The most basic example is creating a buffer pool and using it.

    package main
    
    import (
    	"log"
    	"github.com/karrick/bufpool"
    )
    
    func main() {
    	bp, err := bufpool.NewChanPool()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// NOTE: silly example with heavy resource contension
    	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
    		go func() {
    			for j := 0; j < 1000; j++ {
    				bb := bp.Get()
    				// NOTE: buffer is ready to use
    				for k := 0; k < bufpool.DefaultBufSize/2; k++ {
    					bb.WriteByte(byte(k % 256))
    				}
    				// NOTE: no need to reset buffer prior to release
    				bp.Put(bb)
    			}
    		}()
    	}
    }

During buffer pool creation, you may specify the size of newly allocated buffers, the max keep size of a buffer returned to the pool, and the size of the pool.

The max keep size does not mean the maximum size a buffer is allowed to grow. Instead, when a buffer is returned to the pool, if that buffer has grown beyond the specified max keep size, it will be garbage collected, releasing its memory back to the runtime rather than sit in the pool. This prevents an unusual use of a buffer that grows extremely large from holding onto that memory indefinitely.

Ideally one determines what the pool size, initial buffer size, and max buffer size should be for their application, and sets these values when creating the buffer pool.

    package main

    import (
        "log"
        "github.com/karrick/bufpool"
    )

    func main() {
        // NOTE: All variants of bufpool.New*() can have one or more of BufSize(), MaxKeep(), and PoolSize()
        // to customize the created bufpool.FreeList.
        bp, err := bufpool.NewChanPool(bufpool.PoolSize(64), BufSize(16*1024), MaxKeep(128*1024))
        if err != nil {
            log.Fatal(err)
        }
        // as before...
    }

Documentation

Index

Constants

View Source
const DefaultBufSize = 4 * 1024

DefaultBufSize is the default size used to create new buffers.

View Source
const DefaultMaxKeep = 16 * 1024

DefaultMaxKeep is the default size used to determine whether to keep buffers returned to the pool.

View Source
const DefaultPoolSize = 100

DefaultPoolSize is the default number of buffers that the free-list will maintain.

Variables

This section is empty.

Functions

This section is empty.

Types

type ChanPool

type ChanPool struct {
	// contains filtered or unexported fields
}

ChanPool maintains a free-list of buffers.

func (*ChanPool) Get

func (bp *ChanPool) Get() *bytes.Buffer

Get returns an initialized buffer from the free-list.

func (*ChanPool) Put

func (bp *ChanPool) Put(bb *bytes.Buffer)

Put will return a used buffer back to the free-list. If the capacity of the used buffer grew beyond the max buffer size, it will be discarded and its memory returned to the runtime.

type Configurator

type Configurator func(*poolConfig) error

Configurator is a function that modifies a pool configuration structure.

func BufSize

func BufSize(size int) Configurator

BufSize specifies the size of newly allocated buffers.

package main

import (
	"log"
	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewChanPool(bufpool.BufSize(1024))
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

func MaxKeep

func MaxKeep(size int) Configurator

MaxKeep specifies the maximum size of buffers that ought to be kept when returned to the free list. Buffers with a capacity larger than this size will be discarded, and their memory returned to the runtime.

package main

import (
	"log"
	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewChanPool(bufpool.MaxKeep(32 * 1024))
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

func PoolSize

func PoolSize(size int) Configurator

PoolSize specifies the number of buffers to maintain in the pool. This option has no effect, however, on free-lists created with NewSyncPool, because the Go runtime dynamically maintains the size of pools created using sync.Pool.

package main

import (
	"log"
	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewChanPool(bufpool.PoolSize(25))
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

type FreeList

type FreeList interface {
	Get() *bytes.Buffer
	Put(*bytes.Buffer)
}

FreeList represents a data structure that maintains a free-list of buffers, accesible via Get and Put methods.

func NewChanPool

func NewChanPool(setters ...Configurator) (FreeList, error)

NewChanPool creates a new FreeList. The pool size, size of new buffers, and max size of buffers to keep when returned to the pool can all be customized.

package main

import (
	"log"

	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewChanPool()
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

func NewLockPool

func NewLockPool(setters ...Configurator) (FreeList, error)

NewLockPool creates a new FreeList. The pool size, size of new buffers, and max size of buffers to keep when returned to the pool can all be customized.

package main

import (
	"log"

	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewLockPool()
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

func NewPreAllocatedPool added in v1.1.0

func NewPreAllocatedPool(setters ...Configurator) (FreeList, error)

func NewSyncPool

func NewSyncPool(setters ...Configurator) (FreeList, error)

NewSyncPool creates a new FreeList. The pool size, size of new buffers, and max size of buffers to keep when returned to the pool can all be customized. Note that the PoolSize method has no effect for free-lists created with NewSyncPool, because the Go runtime dynamically maintains the size of pools created using sync.Pool.

package main

import (
	"log"

	"github.com/karrick/bufpool"
)

func main() {
	bp, err := bufpool.NewSyncPool()
	if err != nil {
		log.Fatal(err)
	}
	for i := 0; i < 4*bufpool.DefaultPoolSize; i++ {
		go func() {
			for j := 0; j < 1000; j++ {
				bb := bp.Get()
				for k := 0; k < 3*bufpool.DefaultBufSize; k++ {
					bb.WriteByte(byte(k % 256))
				}
				bp.Put(bb)
			}
		}()
	}
}

type LockPool

type LockPool struct {
	// contains filtered or unexported fields
}

LockPool maintains a free-list of buffers.

func (*LockPool) Get

func (bp *LockPool) Get() *bytes.Buffer

Get returns an initialized buffer from the free-list.

func (*LockPool) Put

func (bp *LockPool) Put(bb *bytes.Buffer)

Put will return a used buffer back to the free-list. If the capacity of the used buffer grew beyond the max buffer size, it will be discarded and its memory returned to the runtime.

type SyncPool

type SyncPool struct {
	// contains filtered or unexported fields
}

SyncPool maintains a free-list of buffers.

func (*SyncPool) Get

func (bp *SyncPool) Get() *bytes.Buffer

Get returns an initialized buffer from the free-list.

func (*SyncPool) Put

func (bp *SyncPool) Put(bb *bytes.Buffer)

Put will return a used buffer back to the free-list. If the capacity of the used buffer grew beyond the max buffer size, it will be discarded and its memory returned to the runtime.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL