adder

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2022 License: Apache-2.0 Imports: 6 Imported by: 1

README

Adder

Thread-safe, high performance, contention-awareness LongAdder and DoubleAdder for Go, inspired by OpenJDK9. Beside JDK-based LongAdder and DoubleAdder, the library also includes other adders for various usage.

Usage

package main

import (
	"fmt"
	"time"

	ga "github.com/line/garr/adder"
)

func main() {
	// or ga.DefaultAdder() which uses jdk long-adder as default
	adder := ga.NewLongAdder(ga.JDKAdderType) 

	for i := 0; i < 100; i++ {
		go func() {
			adder.Add(123)
		}()
	}

	time.Sleep(3 * time.Second)

	// get total added value
	fmt.Println(adder.Sum()) 
}

RandomCellAdder

  • A LongAdder with simple strategy by preallocating atomic cell and select random cell to update.
  • Slower than JDK LongAdder but faster than AtomicAdder on contention.
  • Consume ~1KB to store cells.
adder := ga.NewLongAdder(ga.RandomCellAdderType)

AtomicAdder

  • A LongAdder based on atomic variable. All routines share this variable.
adder := ga.NewLongAdder(ga.AtomicAdderType)

MutexAdder

  • A LongAdder based on mutex. All routines share same value and mutex.
adder := ga.NewLongAdder(ga.MutexAdderType)

Benchmark

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOVCS=""
GOVERSION="go1.17.8"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1560996522=/tmp/go-build -gno-record-gcc-switches"
goos: linux
goarch: amd64
cpu: AMD Ryzen 9 3950X 16-Core Processor            
BenchmarkJDKF64AdderSingleRoutine-32                 212           5778275 ns/op               0 B/op          0 allocs/op
BenchmarkAtomicF64AdderSingleRoutine-32              240           4862050 ns/op               0 B/op          0 allocs/op
BenchmarkJDKF64AdderMultiRoutine-32                   78          16965361 ns/op           10984 B/op         55 allocs/op
BenchmarkAtomicF64AdderMultiRoutine-32                18          64129718 ns/op            1042 B/op         33 allocs/op
BenchmarkJDKF64AdderMultiRoutineMix-32                61          18043656 ns/op            1168 B/op         33 allocs/op
BenchmarkAtomicF64AdderMultiRoutineMix-32             16          65600563 ns/op            1046 B/op         33 allocs/op
BenchmarkJDKAdderSingleRoutine-32                    223           5410781 ns/op               0 B/op          0 allocs/op
BenchmarkAtomicAdderSingleRoutine-32                 272           4485851 ns/op               0 B/op          0 allocs/op
BenchmarkRandomCellAdderSingleRoutine-32              75          16295095 ns/op              54 B/op          0 allocs/op
BenchmarkMutexAdderSingleRoutine-32                   63          18643397 ns/op               0 B/op          0 allocs/op
BenchmarkJDKAdderMultiRoutine-32                      96          16589125 ns/op            1090 B/op         33 allocs/op
BenchmarkAtomicAdderMultiRoutine-32                   49          24409391 ns/op            1041 B/op         33 allocs/op
BenchmarkRandomCellAdderMultiRoutine-32               64          20095099 ns/op            1161 B/op         33 allocs/op
BenchmarkMutexAdderMultiRoutine-32                     4         318551677 ns/op            1808 B/op         41 allocs/op
BenchmarkJDKAdderMultiRoutineMix-32                   72          16713561 ns/op            1123 B/op         33 allocs/op
BenchmarkAtomicAdderMultiRoutineMix-32                46          25109417 ns/op            1040 B/op         33 allocs/op
BenchmarkRandomCellAdderMultiRoutineMix-32            68          20661803 ns/op            1153 B/op         33 allocs/op
BenchmarkMutexAdderMultiRoutineMix-32                  4         417323064 ns/op            1784 B/op         40 allocs/op

Documentation

Overview

Package adder contains a collection of thread-safe, concurrent data structures for reading and writing numeric i64/f64 counter, inspired by OpenJDK9 LongAdder.

Beside JDKAdder, ported version of OpenJDK9 LongAdder, package also provides other alternatives for various use cases.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AtomicAdder

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

AtomicAdder is simple atomic-based adder. Fastest at single routine but slower at multi routine when high-contention happens.

func NewAtomicAdder

func NewAtomicAdder() *AtomicAdder

NewAtomicAdder creates new AtomicAdder.

func (*AtomicAdder) Add

func (a *AtomicAdder) Add(x int64)

Add by the given value.

func (*AtomicAdder) Dec

func (a *AtomicAdder) Dec()

Dec by 1.

func (*AtomicAdder) Inc

func (a *AtomicAdder) Inc()

Inc by 1.

func (*AtomicAdder) Reset

func (a *AtomicAdder) Reset()

Reset variables maintaining the sum to zero. This method may be a useful alternative to creating a new adder, but is only effective if there are no concurrent updates.

func (*AtomicAdder) Store

func (a *AtomicAdder) Store(v int64)

Store value. This function is only effective if there are no concurrent updates.

func (*AtomicAdder) Sum

func (a *AtomicAdder) Sum() int64

Sum returns the current sum. The returned value is NOT an atomic snapshot because of concurrent update.

func (*AtomicAdder) SumAndReset

func (a *AtomicAdder) SumAndReset() (sum int64)

SumAndReset equivalent in effect to sum followed by reset. Like the nature of Sum and Reset, this function is only effective if there are no concurrent updates.

type AtomicF64Adder

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

AtomicF64Adder is simple atomic-based adder. Fastest at single routine but slower at multi routine when high-contention happens.

func NewAtomicF64Adder

func NewAtomicF64Adder() *AtomicF64Adder

NewAtomicF64Adder creates new AtomicF64Adder.

func (*AtomicF64Adder) Add

func (a *AtomicF64Adder) Add(v float64)

Add by the given value.

func (*AtomicF64Adder) Dec

func (a *AtomicF64Adder) Dec()

Dec by 1.

func (*AtomicF64Adder) Inc

func (a *AtomicF64Adder) Inc()

Inc by 1.

func (*AtomicF64Adder) Reset

func (a *AtomicF64Adder) Reset()

Reset variables maintaining the sum to zero. This method may be a useful alternative to creating a new adder, but is only effective if there are no concurrent updates.

func (*AtomicF64Adder) Store

func (a *AtomicF64Adder) Store(v float64)

Store value. This function is only effective if there are no concurrent updates.

func (*AtomicF64Adder) Sum

func (a *AtomicF64Adder) Sum() float64

Sum returns the current sum. The returned value is NOT an atomic snapshot because of concurrent update.

func (*AtomicF64Adder) SumAndReset

func (a *AtomicF64Adder) SumAndReset() (sum float64)

SumAndReset equivalent in effect to sum followed by reset. Like the nature of Sum and Reset, this function is only effective if there are no concurrent updates.

type Float64Adder

type Float64Adder interface {
	Add(x float64)
	Inc()
	Dec()
	Sum() float64
	Reset()
	SumAndReset() float64
	Store(v float64)
}

Float64Adder interface.

func DefaultFloat64Adder

func DefaultFloat64Adder() Float64Adder

DefaultFloat64Adder returns jdk f64 adder.

func NewFloat64Adder

func NewFloat64Adder(t Type) Float64Adder

NewFloat64Adder create new float64 adder upon type.

type JDKAdder

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

JDKAdder is ported version of OpenJDK9 LongAdder.

When multiple routines update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control, contention overhead could be a pain.

JDKAdder is preferable to atomic, delivers significantly higher throughput under high contention, at the expense of higher space consumption, while keeping same characteristics under low contention.

One or more variables, called Cells, together maintain an initially zero sum. When updates are contended across routines, the set of variables may grow dynamically to reduce contention. In other words, updates are distributed over Cells. The value is lazy, only aggregated (sum) over Cells when needed.

JDKAdder is high performance, non-blocking and safe for concurrent use.

func NewJDKAdder

func NewJDKAdder() *JDKAdder

NewJDKAdder creates new JDKAdder.

func (*JDKAdder) Add

func (u *JDKAdder) Add(x int64)

Add by the given value.

func (*JDKAdder) Dec

func (u *JDKAdder) Dec()

Dec by 1.

func (*JDKAdder) Inc

func (u *JDKAdder) Inc()

Inc by 1.

func (*JDKAdder) Reset

func (u *JDKAdder) Reset()

Reset variables maintaining the sum to zero. This method may be a useful alternative to creating a new adder, but is only effective if there are no concurrent updates.

func (*JDKAdder) Store

func (u *JDKAdder) Store(v int64)

Store value. This function is only effective if there are no concurrent updates.

func (*JDKAdder) Sum

func (u *JDKAdder) Sum() int64

Sum returns the current sum. The returned value is NOT an atomic snapshot because of concurrent update.

func (*JDKAdder) SumAndReset

func (u *JDKAdder) SumAndReset() (sum int64)

SumAndReset equivalent in effect to sum followed by reset. Like the nature of Sum and Reset, this function is only effective if there are no concurrent updates.

type JDKF64Adder

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

JDKF64Adder is ported version of OpenJDK9 DoubleAdder.

When multiple routines update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control, contention overhead could be a pain.

JDKF64Adder is preferable to atomic, delivers significantly higher throughput under high contention, at the expense of higher space consumption, while keeping same characteristics under low contention.

One or more variables, called Cells, together maintain an initially zero sum. When updates are contended across routines, the set of variables may grow dynamically to reduce contention. In other words, updates are distributed over Cells. The value is lazy, only aggregated (sum) over Cells when needed.

JDKF64Adder is high performance, non-blocking and safe for concurrent use.

func NewJDKF64Adder

func NewJDKF64Adder() *JDKF64Adder

NewJDKF64Adder creates new JDKF64Adder.

func (*JDKF64Adder) Add

func (f *JDKF64Adder) Add(x float64)

Add by the given value.

func (*JDKF64Adder) Dec

func (f *JDKF64Adder) Dec()

Dec by 1.

func (*JDKF64Adder) Inc

func (f *JDKF64Adder) Inc()

Inc by 1.

func (*JDKF64Adder) Reset

func (f *JDKF64Adder) Reset()

Reset variables maintaining the sum to zero. This method may be a useful alternative to creating a new adder, but is only effective if there are no concurrent updates.

func (*JDKF64Adder) Store

func (f *JDKF64Adder) Store(v float64)

Store value. This function is only effective if there are no concurrent updates.

func (*JDKF64Adder) Sum

func (f *JDKF64Adder) Sum() float64

Sum returns the current sum. The returned value is NOT an atomic snapshot because of concurrent update.

func (*JDKF64Adder) SumAndReset

func (f *JDKF64Adder) SumAndReset() (sum float64)

SumAndReset equivalent in effect to sum followed by reset. Like the nature of Sum and Reset, this function is only effective if there are no concurrent updates.

type LongAdder

type LongAdder interface {
	Add(x int64)
	Inc()
	Dec()
	Sum() int64
	Reset()
	SumAndReset() int64
	Store(v int64)
}

LongAdder interface.

func DefaultAdder

func DefaultAdder() LongAdder

DefaultAdder returns jdk long adder.

func NewLongAdder

func NewLongAdder(t Type) LongAdder

NewLongAdder create new long adder upon type.

type MutexAdder

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

MutexAdder is mutex-based LongAdder. Slowest compared to other alternatives.

func NewMutexAdder

func NewMutexAdder() *MutexAdder

NewMutexAdder creates new MutexAdder.

func (*MutexAdder) Add

func (m *MutexAdder) Add(x int64)

Add by the given value.

func (*MutexAdder) Dec

func (m *MutexAdder) Dec()

Dec by 1.

func (*MutexAdder) Inc

func (m *MutexAdder) Inc()

Inc by 1.

func (*MutexAdder) Reset

func (m *MutexAdder) Reset()

Reset variables maintaining the sum to zero.

func (*MutexAdder) Store

func (m *MutexAdder) Store(v int64)

Store value.

func (*MutexAdder) Sum

func (m *MutexAdder) Sum() (sum int64)

Sum returns the current sum.

func (*MutexAdder) SumAndReset

func (m *MutexAdder) SumAndReset() (sum int64)

SumAndReset equivalent in effect to sum followed by reset.

type RandomCellAdder

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

RandomCellAdder takes idea from JDKAdder by preallocating a fixed number of Cells. Unlike JDKAdder, in each update, RandomCellAdder assign a random-fixed Cell to invoker instead of retry/reassign Cell when contention.

RandomCellAdder is often faster than JDKAdder in multi routine race benchmark but slower in case of single routine (no race).

RandomCellAdder consume ~1KB for storing cells, which is often larger than JDKAdder which number of cells is dynamic.

func NewRandomCellAdder

func NewRandomCellAdder() *RandomCellAdder

NewRandomCellAdder create new RandomCellAdder

func (*RandomCellAdder) Add

func (r *RandomCellAdder) Add(x int64)

Add the given value

func (*RandomCellAdder) Dec

func (r *RandomCellAdder) Dec()

Dec by 1

func (*RandomCellAdder) Inc

func (r *RandomCellAdder) Inc()

Inc by 1

func (*RandomCellAdder) Reset

func (r *RandomCellAdder) Reset()

Reset variables maintaining the sum to zero. This method may be a useful alternative to creating a new adder, but is only effective if there are no concurrent updates. Because this method is intrinsically racy

func (*RandomCellAdder) Store

func (r *RandomCellAdder) Store(v int64)

Store value. This function is only effective if there are no concurrent updates.

func (*RandomCellAdder) Sum

func (r *RandomCellAdder) Sum() (sum int64)

Sum return the current sum. The returned value is NOT an atomic snapshot; invocation in the absence of concurrent updates returns an accurate result, but concurrent updates that occur while the sum is being calculated might not be incorporated.

func (*RandomCellAdder) SumAndReset

func (r *RandomCellAdder) SumAndReset() (sum int64)

SumAndReset equivalent in effect to sum followed by reset. This method may apply for example during quiescent points between multithreaded computations. If there are updates concurrent with this method, the returned value is guaranteed to be the final value occurring before the reset.

type Type

type Type byte

Type of LongAdder.

const (
	// JDKAdderType is type for JDK-based LongAdder.
	JDKAdderType Type = iota
	// RandomCellAdderType is type for RandomCellAdder.
	RandomCellAdderType
	// AtomicAdderType is type for atomic-based adder.
	AtomicAdderType
	// MutexAdderType is type for MutexAdder.
	MutexAdderType
	// JDKF64AdderType is type for JDK-based DoubleAdder.
	JDKF64AdderType
	// AtomicF64AdderType is type for atomic-based float64 adder.
	AtomicF64AdderType
)

Jump to

Keyboard shortcuts

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