Skip to content

Commit

Permalink
🕜 perf: refactor buffer pool implementation to use dynamic sizing
Browse files Browse the repository at this point in the history
  • Loading branch information
sohaha committed Sep 19, 2024
1 parent 245ccba commit 92d7cc5
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 47 deletions.
32 changes: 30 additions & 2 deletions zarray/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,16 +199,44 @@ func Shift[T comparable](list *[]T) (v T) {
}

// Slice converts a string to a slice.
func Slice[T comparable](s string) []T {
// If n is not empty, the string will be split into n parts.
func Slice[T comparable](s string, n ...int) []T {
if s == "" {
return []T{}
}

ss := strings.Split(s, ",")
var ss []string
if len(n) > 0 {
ss = strings.SplitN(s, ",", n[0])
} else {
ss = strings.Split(s, ",")
}
res := make([]T, len(ss))
for i := range ss {
ztype.To(zstring.TrimSpace(ss[i]), &res[i])
}

return res
}

// Join slice to string.
// If n is not empty, the string will be split into n parts.
func Join[T comparable](s []T, sep string) string {
if len(s) == 0 {
return ""
}

b := zstring.Buffer(len(s))
for i := 0; i < len(s); i++ {
v := ztype.ToString(s[i])
if v == "" {
continue
}
b.WriteString(v)
if i < len(s)-1 {
b.WriteString(sep)
}
}

return b.String()
}
11 changes: 11 additions & 0 deletions zarray/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,16 @@ func TestSlice(t *testing.T) {
tt.Equal([]string{"a", "b", "c"}, zarray.Slice[string]("a,b,c"))
tt.Equal([]int{1, 2, 3}, zarray.Slice[int]("1,2,3"))
tt.Equal([]float64{1.1, 2.2, 3.3}, zarray.Slice[float64]("1.1,2.2,3.3"))
tt.Equal([]string{"1.1", "2.2,3.3"}, zarray.Slice[string]("1.1,2.2,3.3", 2))
tt.Equal([]int{}, zarray.Slice[int](""))
}

func TestJoin(t *testing.T) {
tt := zlsgo.NewTest(t)
tt.Equal("a,b,c", zarray.Join([]string{"a", "b", "c"}, ","))
tt.Equal("1,2,3", zarray.Join([]int{1, 2, 3}, ","))
tt.Equal("1.1,2.2,3.3", zarray.Join([]float64{1.1, 2.2, 3.3}, ","))
tt.Equal("1.1,2.2,3.3", zarray.Join([]string{"1.1", "2.2", "3.3"}, ","))
tt.Equal("1.1,3.3", zarray.Join([]string{"1.1", "", "3.3"}, ","))
tt.Equal("", zarray.Join([]string{}, ","))
}
5 changes: 4 additions & 1 deletion zfile/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ func TestPermissionDenied(t *testing.T) {
dir := "./permission_denied"

os.Mkdir(dir, 0o000)
defer Rmdir(dir)
defer func() {
os.Chmod(dir, 777)
Rmdir(dir)
}()

tt.Run("NotPermission", func(tt *TestUtil) {
tt.EqualTrue(!HasReadWritePermission(dir))
Expand Down
2 changes: 1 addition & 1 deletion zlog/zlogger.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func (log *Logger) outPut(level int, s string, isWrap bool, calldDepth int, pref
s = prefixText[0] + s
}

buf := zutil.GetBuff(len(s) + 34)
buf := zutil.GetBuff(uint(len(s) + 34))
defer zutil.PutBuff(buf)

now := ztime.Time()
Expand Down
110 changes: 69 additions & 41 deletions zutil/buffpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,88 @@ import (
"sync"
)

var bufTypes = [...]int{
0, 16, 32, 64, 128, 256, 512, 1024, 2048, 5120, 1 << 20, (1 << 19) * 10, (1 << 20) * 10, (1 << 19) * 100, (1 << 20) * 100,
}
var BuffSize = uint(32)

const bufTypeNum = len(bufTypes)
type BufferPool struct {
begin int
end int
shards map[int]*sync.Pool
}

var (
bufPools [bufTypeNum]sync.Pool
BuffSize = bufTypes[3]
)
var bufPools = NewBufferPool(BuffSize, (1<<20)*100)

func init() {
for i := 0; i < bufTypeNum; i++ {
l := bufTypes[i]
bufPools[i].New = func() interface{} {
return bytes.NewBuffer(make([]byte, 0, l))
func NewBufferPool(left, right uint) *BufferPool {
begin, end := int(roundUpToPowerOfTwo(left)), int(roundUpToPowerOfTwo(right))
p := &BufferPool{
begin: begin,
end: end,
shards: map[int]*sync.Pool{},
}
for i := begin; i <= end; i *= 2 {
capacity := i
p.shards[i] = &sync.Pool{
New: func() any { return bytes.NewBuffer(make([]byte, 0, capacity)) },
}
}
return p
}

func GetBuff(ss ...int) *bytes.Buffer {
size := BuffSize
if len(ss) > 0 {
size = ss[0]
}
if size > 0 {
if size <= bufTypes[bufTypeNum-1] {
for i := 0; i < bufTypeNum; i++ {
if size <= bufTypes[i] {
return bufPools[i].Get().(*bytes.Buffer)
}
func (p *BufferPool) Put(b *bytes.Buffer, noreset ...bool) {
if b != nil {
if pool, ok := p.shards[b.Cap()]; ok {
if len(noreset) == 0 || !noreset[0] {
b.Reset()
}

pool.Put(b)
}
}
}

func (p *BufferPool) Get(n ...uint) *bytes.Buffer {
size := int(max(uint(p.begin), n...))
if pool, ok := p.shards[size]; ok {
b := pool.Get().(*bytes.Buffer)
if b.Cap() < size {
b.Grow(size)
b.Reset()
}
return bytes.NewBuffer(make([]byte, 0, size))
return b
}
return bytes.NewBuffer(make([]byte, 0, size))
}

return bufPools[0].Get().(*bytes.Buffer)
func roundUpToPowerOfTwo(v uint) uint {
v--
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v++
return v
}

func PutBuff(buffer *bytes.Buffer) {
size := buffer.Cap()
buffer.Reset()
if size > bufTypes[bufTypeNum-1] {
bufPools[0].Put(buffer)
return
func max(a uint, n ...uint) (size uint) {
if len(n) > 0 && n[0] > 0 {
size = n[0]
} else {
size = BuffSize
}
for i := 1; i < bufTypeNum; i++ {
if size <= bufTypes[i] {
if size == bufTypes[i] {
bufPools[i].Put(buffer)
} else {
bufPools[i-1].Put(buffer)
}
return
}

b := roundUpToPowerOfTwo(size)

if a > b {
return a
}

return b
}

func GetBuff(size ...uint) *bytes.Buffer {
return bufPools.Get(size...)
}

func PutBuff(buffer *bytes.Buffer, noreset ...bool) {
bufPools.Put(buffer, noreset...)
}
4 changes: 2 additions & 2 deletions zutil/buffpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ var sizeString = strings.Repeat("0", 1024*1024)

func BenchmarkPoolBytesPoolMinSize_max(b *testing.B) {
v := []byte(sizeString)
s := 16
s := uint(16)
for i := 0; i < b.N; i++ {
var content = zutil.GetBuff(s)
content.Write(v)
Expand All @@ -117,7 +117,7 @@ func BenchmarkPoolBytesPoolMinSize_max(b *testing.B) {

func BenchmarkPoolBytesPoolMaxSize_max(b *testing.B) {
v := []byte(sizeString)
s := 1024 * 1024
s := uint(1024 * 1024)
for i := 0; i < b.N; i++ {
var content = zutil.GetBuff(s)
content.Write(v)
Expand Down

0 comments on commit 92d7cc5

Please sign in to comment.