cbytebuf

package module
v1.2.3 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2024 License: MIT Imports: 8 Imported by: 1

README

CbyteBuf

Alloc-free replacement for bytes.Buffer based on cbyte.

Usage

package main

import (
	"fmt"
	"github.com/koykov/cbytebuf"
)

func main() {
	buf := cbytebuf.NewCByteBuf()
	defer buf.Release()
	_, _ = buf.WriteString("foo ")
	_, _ = buf.WriteString("bar ")
	// ...
	_, _ = buf.WriteString("end.")
	fmt.Println(buf.String()) // "foo bar ... end."
}

No escapes to heap:

$ go build -gcflags '-m' example.go 
# command-line-arguments
example/example.go:9:9: inlining call to cbytebuf.NewCByteBuf
example/example.go:9:9: main &cbytebuf.b·2 does not escape

See test file for more examples and benchmarks for number of allocations.

How it works

This package was inspired by article Allocation efficiency in high-performance Go services. Please read it before continue.

If you will use a lot of bytes.Buffer (or any analogues) you may notice that GC pressure will increase during the time even if you use sync.Pool. This occurs since all slices in the pools (or any storage) checks by GC during mark phase.

The main approach of CbyteBuf is to avoid using any references and pointers inside it and, consecutive, avoid escapes to heap. In fact the instance of CbyteBuf contains only SliceHeader and temporary int variable - one uintptr and three integers in result. As result any new instance of CBB allocates in stack instead of heap. In fact allocations in heap occurs, but they produces by cbyte and GC doesn't know nothing about them.

We've experienced increasing in more than 2 times the intervals between GC cycles, that is very good for our project. Also we noticed decreasing of GC CPU usage in ~3 times.

Benchmarks

BenchmarkCByteBuf_Write-8          	  345268	      3602 ns/op	       0 B/op	       0 allocs/op
BenchmarkCByteBuf_WriteLong-8      	    2622	    439151 ns/op	       0 B/op	       0 allocs/op
BenchmarkCByteBuf_AppendBytes-8    	 1373740	       870 ns/op	     896 B/op	       1 allocs/op
BenchmarkCByteBuf_AppendString-8   	 1342476	       869 ns/op	     896 B/op	       1 allocs/op
BenchmarkLBPool-8                  	11660017	       101 ns/op	       0 B/op	       0 allocs/op
BenchmarkPool-8                    	 5501479	       205 ns/op	       0 B/op	       0 allocs/op

Also you can see more comparison benchmarks in versus project:

BenchmarkByteArray_Append-8             	  767320	      1449 ns/op	    2040 B/op	       8 allocs/op
BenchmarkByteArray_AppendLong-8         	    1557	    754013 ns/op	 4646288 B/op	      25 allocs/op
BenchmarkByteBufferNative_Write-8       	  517546	      2376 ns/op	    2416 B/op	       5 allocs/op
BenchmarkByteBufferNative_WriteLong-8   	    3441	    346512 ns/op	 1646722 B/op	      10 allocs/op
BenchmarkByteBufferPool_Write-8         	  904567	      1335 ns/op	       0 B/op	       0 allocs/op
BenchmarkByteBufferPool_WriteLong-8     	    1555	    754847 ns/op	 4667398 B/op	      29 allocs/op
BenchmarkCByteBuf_Write-8               	  380574	      3171 ns/op	       0 B/op	       0 allocs/op
BenchmarkCByteBuf_WriteLong-8           	    2631	    454779 ns/op	       0 B/op	       0 allocs/op

As you can see, CbyteBuf is slowest than any byte buffer or byte slice when writing short pieces of data, but has good speed for long writes. Interesting that long writes is more faster that using append().

Anyway it's acceptable cost since it produces zero allocations even if you doesn't use pools. But I recommend to use it together with pool since it reduces amount of CGO calls in cbyte.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrOk            error = nil
	ErrBadAlloc            = errors.New("bad alloc on buffer init or grow")
	ErrNegativeCap         = errors.New("negative cap on the grow")
	ErrNegativeRead        = errors.New("reader returned negative count from Read")
	ErrNilMarshaller       = errors.New("marshaller object is nil")
)
View Source
var (
	// LBP is a default instance of LB pool for simple cases.
	// Just call cbytebuf.LBAcquire() and cbytebuf.LBRelease().
	LBP = LBPool{Size: 1000}
)

Functions

func LBRelease

func LBRelease(b *CByteBuf)

LBRelease puts byte buffer back to default LB pool instance.

func RegisterMetricsHandler

func RegisterMetricsHandler(handler MetricsWriter)

RegisterMetricsHandler registers new metrics handler.

func Release

func Release(b *CByteBuf)

Release puts byte buffer back to default pool instance.

Types

type CByteBuf

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

CByteBuf is a variable-size alloc-free buffer based on cbyte array. Also, no escapes to the heap since buffer doesn't contain any pointer.

func Acquire

func Acquire() *CByteBuf

Acquire gets byte buffer from default pool instance.

func LBAcquire

func LBAcquire() *CByteBuf

LBAcquire gets byte buffer from default LB pool instance.

func NewCByteBuf

func NewCByteBuf() *CByteBuf

NewCByteBuf makes new buffer.

func (*CByteBuf) AppendBytes

func (b *CByteBuf) AppendBytes(dst []byte) []byte

AppendBytes appends buffer value to destination and return it.

func (*CByteBuf) AppendString

func (b *CByteBuf) AppendString(dst string) string

AppendString appends buffer value to destination string and return it.

func (*CByteBuf) Bytes

func (b *CByteBuf) Bytes() []byte

Bytes returns contents of the buffer.

func (*CByteBuf) Cap

func (b *CByteBuf) Cap() int

Cap returns capacity of the buffer.

func (*CByteBuf) Grow

func (b *CByteBuf) Grow(cap int) error

Grow increases or decrease capacity of the buffer.

func (*CByteBuf) GrowDelta

func (b *CByteBuf) GrowDelta(delta int) error

GrowDelta increases or decrease capacity of the buffer using delta value.

Delta may be negative, but if delta will less than -capacity, the error will be triggered.

func (*CByteBuf) GrowLen

func (b *CByteBuf) GrowLen(len int) error

GrowLen increases or decrease length of the buffer.

May increase capacity if needed.

func (*CByteBuf) Len

func (b *CByteBuf) Len() int

Len returns length of the buffer.

func (*CByteBuf) ReadFrom

func (b *CByteBuf) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom implements io.ReaderFrom.

func (*CByteBuf) Release

func (b *CByteBuf) Release()

Release manually releases of the underlying byte array.

Using the buffer data after call this func may crash your app. This method truncates buffer's capacity.

func (*CByteBuf) Reset

func (b *CByteBuf) Reset()

Reset all data accumulated in buffer.

This method made special to use together with pools. Using the buffer data after call this func may crash your app. Buffer capacity keeps to reduce amount of further CGO calls.

func (*CByteBuf) ResetLen

func (b *CByteBuf) ResetLen()

ResetLen resets buffer length without releasing memory.

func (*CByteBuf) String

func (b *CByteBuf) String() string

Get the contents of the buffer as string.

func (*CByteBuf) Write

func (b *CByteBuf) Write(data []byte) (int, error)

Write implements io.Writer.

func (*CByteBuf) WriteBool

func (b *CByteBuf) WriteBool(v bool) (int, error)

WriteBool writes boolean value in the buffer.

func (*CByteBuf) WriteByte

func (b *CByteBuf) WriteByte(c byte) error

WriteByte implements io.ByteWriter.

func (*CByteBuf) WriteFloat

func (b *CByteBuf) WriteFloat(f float64, prec int) (int, error)

WriteFloat writes float value in the buffer.

func (*CByteBuf) WriteInt

func (b *CByteBuf) WriteInt(i int64) (int, error)

WriteInt writes integer value in the buffer.

func (*CByteBuf) WriteMarshallerTo

func (b *CByteBuf) WriteMarshallerTo(m MarshallerTo) (int, error)

WriteMarshallerTo marshals data of struct implemented MarshallerTo interface into the buffer.

func (*CByteBuf) WriteString

func (b *CByteBuf) WriteString(s string) (int, error)

WriteString implements io.StringWriter.

func (*CByteBuf) WriteTo

func (b *CByteBuf) WriteTo(w io.Writer) (int64, error)

WriteTo implements io.WriterTo.

func (*CByteBuf) WriteUint

func (b *CByteBuf) WriteUint(u uint64) (int, error)

WriteUint writes unsigned integer value in the buffer.

type DummyMetrics

type DummyMetrics struct{}

DummyMetrics writer. Used by default and does nothing.

func (DummyMetrics) PoolAcquire

func (m DummyMetrics) PoolAcquire(_ uint64)

func (DummyMetrics) PoolRelease

func (m DummyMetrics) PoolRelease(_ uint64)

type LBPool

type LBPool struct {
	Size          uint
	ReleaseFactor float32
	// contains filtered or unexported fields
}

LBPool is a pool implementation based on lbpool.Pool.

func (*LBPool) Get

func (p *LBPool) Get() *CByteBuf

Get old byte buffer from the LB pool or create a new byte buffer.

func (*LBPool) Put

func (p *LBPool) Put(b *CByteBuf)

Put byte buffer back to the LB pool.

Using data returned from the buffer after putting is unsafe.

type MarshallerTo

type MarshallerTo interface {
	Size() int
	MarshalTo(data []byte) (int, error)
}

MarshallerTo interface to write struct like Protobuf.

type MetricsWriter

type MetricsWriter interface {
	// PoolAcquire registers acquire of cbyte object from pool.
	PoolAcquire(cap uint64)
	// PoolRelease registers release of cbyte object back to pool.
	PoolRelease(cap uint64)
}

type Pool

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

Pool is a pool implementation based on sync.Pool.

var P Pool

P is a default instance of native pool for simple cases. Just call cbytebuf.Acquire() and cbytebuf.Release().

func (*Pool) Get

func (p *Pool) Get() *CByteBuf

Get old byte buffer from the pool or create a new byte buffer.

func (*Pool) Put

func (p *Pool) Put(b *CByteBuf)

Put byte buffer back to the pool.

Using data returned from the buffer after putting is unsafe.

Directories

Path Synopsis
metrics
prometheus module

Jump to

Keyboard shortcuts

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