From 2814f266b00a52fa11a0b91d1a4424d64c47c859 Mon Sep 17 00:00:00 2001 From: Sergey Vilgelm Date: Tue, 23 May 2023 13:05:47 -0700 Subject: [PATCH] the code (#1) --- README.md | 33 +++++++++++++++++++++- pool.go | 37 ++++++++++++++++++++++++ pool_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 pool.go create mode 100644 pool_test.go diff --git a/README.md b/README.md index f2152c3..51a1270 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # gpool -Generic Pool in Go +Generic wrapper for sync.Pool in Go + +## Usage + +`int64` type +```go +p := Pool[int64]{New: func() int64 { return 42 }} +x := p.Get() +defer p.Put(x) +fmt.Printf("x = (%T) %d", x, x) +// Output: x = (int64) 42 +``` + +`string` type +```go +p := Pool[string]{New: func() string { return "foo" }} +x := p.Get() +defer p.Put(x) +fmt.Printf("x = (%T) %s", x, x) +// Output: x = (string) foo +``` + +## Benchmarks + +```shell +% go test -bench=. -benchmem ./... +goos: darwin +goarch: arm64 +pkg: github.com/sv-tools/gpool +BenchmarkSyncPool-8 699275571 1.614 ns/op 0 B/op 0 allocs/op +BenchmarkPool-8 647708158 1.732 ns/op 0 B/op 0 allocs/op +``` diff --git a/pool.go b/pool.go new file mode 100644 index 0000000..5a3989f --- /dev/null +++ b/pool.go @@ -0,0 +1,37 @@ +package gpool + +import "sync" + +// Pool is generic drop-in replacement of `sync.Pool` +type Pool[T any] struct { + sp sync.Pool + + // New optionally specifies a function to generate + // a value when Get would otherwise return nil. + // It may not be changed concurrently with calls to Get. + New func() T +} + +// Put adds x to the pool. +func (p *Pool[T]) Put(x T) { + p.sp.Put(x) +} + +// Get selects an arbitrary item from the Pool, removes it from the +// Pool, and returns it to the caller. +// Get may choose to ignore the pool and treat it as empty. +// Callers should not assume any relation between values passed to Put and +// the values returned by Get. +// +// If Get would otherwise return nil and p.New is non-nil, Get returns +// the result of calling p.New. +func (p *Pool[T]) Get() (item T) { + x := p.sp.Get() + if x == nil { + if p.New != nil { + return p.New() + } + return item + } + return x.(T) +} diff --git a/pool_test.go b/pool_test.go new file mode 100644 index 0000000..296ebc2 --- /dev/null +++ b/pool_test.go @@ -0,0 +1,79 @@ +package gpool + +import ( + "fmt" + "math/rand" + "sync" + "testing" +) + +func TestPool(t *testing.T) { + p := Pool[int64]{} + x := p.Get() + if x != 0 { + t.Fatalf("expected x = 0, but got %d", x) + } + p.New = func() int64 { + // should return only a number which is greater than zero + v := rand.Int63() + for v <= 0 { + v = rand.Int63() + } + return v + } + x = p.Get() + if x == 0 { + t.Fatal("expected x != 0, but got 0") + } + p.Put(-1) + // getting until the pool returns -1 or fails by timeout + for x != -1 { + x = p.Get() + } +} + +var benchmarkSyncPoolResult any + +func BenchmarkSyncPool(b *testing.B) { + p := sync.Pool{New: func() any { return 42 }} + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + var res any + for pb.Next() { + res = p.Get() + p.Put(res) + } + benchmarkSyncPoolResult = res + }) +} + +var benchmarkPoolResult int64 + +func BenchmarkPool(b *testing.B) { + p := Pool[int64]{New: func() int64 { return 42 }} + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + var res int64 + for pb.Next() { + res = p.Get() + p.Put(res) + } + benchmarkPoolResult = res + }) +} + +func ExamplePool_int64() { + p := Pool[int64]{New: func() int64 { return 42 }} + x := p.Get() + defer p.Put(x) + fmt.Printf("x = (%T) %d", x, x) + // Output: x = (int64) 42 +} + +func ExamplePool_string() { + p := Pool[string]{New: func() string { return "foo" }} + x := p.Get() + defer p.Put(x) + fmt.Printf("x = (%T) %s", x, x) + // Output: x = (string) foo +}