cubefs
368 строк · 7.9 Кб
1// Copyright 2022 The CubeFS Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12// implied. See the License for the specific language governing
13// permissions and limitations under the License.
14
15package resourcepool_test
16
17import (
18"crypto/rand"
19"encoding/json"
20"fmt"
21"runtime"
22"testing"
23
24"github.com/stretchr/testify/require"
25
26rp "github.com/cubefs/cubefs/blobstore/common/resourcepool"
27)
28
29const (
30kb = 1 << 10
31mb = 1 << 20
32mb4 = 4 * mb
33)
34
35func TestMemPoolBase(t *testing.T) {
36classes := map[int]int{kb: 2, mb: 1}
37
38pool := rp.NewMemPool(classes)
39require.NotNil(t, pool)
40
41bufm, err := pool.Get(mb)
42require.NoError(t, err)
43require.Equal(t, mb, len(bufm))
44require.Equal(t, mb, cap(bufm))
45
46_, err = pool.Get(mb)
47require.NoError(t, err)
48// require.ErrorIs(t, err, rp.ErrPoolLimit)
49
50bufk, err := pool.Get(kb)
51require.NoError(t, err)
52require.Equal(t, kb, len(bufk))
53require.Equal(t, kb, cap(bufk))
54
55bufk2, err := pool.Get(kb / 2)
56require.NoError(t, err)
57require.Equal(t, kb/2, len(bufk2))
58require.Equal(t, kb, cap(bufk2))
59
60err = pool.Put(bufm)
61require.NoError(t, err)
62
63bufmx, err := pool.Get(mb)
64require.NoError(t, err)
65require.Equal(t, bufm, bufmx)
66}
67
68func TestMemPoolEmpty(t *testing.T) {
69pool := rp.NewMemPool(nil)
70require.NotNil(t, pool)
71
72_, err := pool.Get(kb)
73require.ErrorIs(t, err, rp.ErrNoSuitableSizeClass)
74
75buf, err := pool.Alloc(kb)
76require.NoError(t, err)
77require.Equal(t, kb, len(buf))
78require.Equal(t, kb, cap(buf))
79
80err = pool.Put(buf)
81require.ErrorIs(t, err, rp.ErrNoSuitableSizeClass)
82}
83
84func TestMemPoolChanAlloc(t *testing.T) {
85classes := map[int]int{mb: 1}
86pool := rp.NewMemPool(classes)
87require.NotNil(t, pool)
88
89bufm, err := pool.Get(mb)
90require.NoError(t, err)
91require.Equal(t, mb, len(bufm))
92require.Equal(t, mb, cap(bufm))
93
94_, err = pool.Get(mb)
95require.NoError(t, err)
96
97// if matched size class, return ErrPoolLimit
98_, err = pool.Alloc(mb)
99require.NoError(t, err)
100
101// if oversize, make new buffer
102bufm4, err := pool.Alloc(mb4)
103require.NoError(t, err)
104require.Equal(t, mb4, len(bufm4))
105require.Equal(t, mb4, cap(bufm4))
106
107// put oversize buffer to top class
108err = pool.Put(bufm4)
109require.NoError(t, err)
110
111// maybe get the oversize buffer
112bufm, err = pool.Get(mb)
113require.NoError(t, err)
114require.Equal(t, mb, len(bufm))
115require.True(t, mb4 == cap(bufm) || mb == cap(bufm))
116
117err = pool.Put(bufm)
118require.NoError(t, err)
119}
120
121func TestMemPoolSyncAlloc(t *testing.T) {
122classes := map[int]int{mb: 1}
123pool := rp.NewMemPoolWith(classes, func(size, capacity int) rp.Pool {
124return rp.NewPool(func() interface{} {
125return make([]byte, size)
126}, capacity)
127})
128require.NotNil(t, pool)
129
130bufm, err := pool.Get(mb)
131require.NoError(t, err)
132require.Equal(t, mb, len(bufm))
133require.Equal(t, mb, cap(bufm))
134
135_, err = pool.Get(mb)
136require.ErrorIs(t, err, rp.ErrPoolLimit)
137
138// if matched size class, return ErrPoolLimit
139_, err = pool.Alloc(mb)
140require.ErrorIs(t, err, rp.ErrPoolLimit)
141
142// if oversize, make new buffer
143bufm4, err := pool.Alloc(mb4)
144require.NoError(t, err)
145require.Equal(t, mb4, len(bufm4))
146require.Equal(t, mb4, cap(bufm4))
147
148// put oversize buffer to top class
149err = pool.Put(bufm4)
150require.NoError(t, err)
151
152// maybe get the oversize buffer
153bufm, err = pool.Get(mb)
154require.NoError(t, err)
155require.Equal(t, mb, len(bufm))
156require.True(t, mb4 == cap(bufm) || mb == cap(bufm))
157
158err = pool.Put(bufm)
159require.NoError(t, err)
160
161// bufm4 released by gc after twice runtime.GC()
162// see sync/pool.go: runtime_registerPoolCleanup(poolCleanup)
163runtime.GC()
164runtime.GC()
165bufm, err = pool.Get(mb)
166require.NoError(t, err)
167require.Equal(t, mb, len(bufm))
168require.Equal(t, mb, cap(bufm))
169}
170
171func TestMemPoolPutGet(t *testing.T) {
172classes := map[int]int{mb: 1}
173
174pool := rp.NewMemPool(classes)
175require.NotNil(t, pool)
176
177bufm, err := pool.Get(mb)
178require.NoError(t, err)
179require.Equal(t, mb, len(bufm))
180require.Equal(t, mb, cap(bufm))
181
182zero := make([]byte, len(bufm))
183rand.Read(bufm)
184require.NotEqual(t, zero, bufm)
185
186err = pool.Put(bufm)
187require.NoError(t, err)
188
189bufmx, err := pool.Get(mb)
190require.NoError(t, err)
191require.Equal(t, bufm, bufmx)
192
193// if oversize, make new buffer
194bufm4, err := pool.Alloc(mb4)
195require.NoError(t, err)
196require.Equal(t, mb4, len(bufm4))
197require.Equal(t, mb4, cap(bufm4))
198
199rand.Read(bufm4)
200require.NotEqual(t, zero, bufm4[:mb])
201
202// put oversize buffer to top class
203err = pool.Put(bufm4)
204require.NoError(t, err)
205
206// maybe get the oversize buffer
207bufm, err = pool.Get(mb)
208require.NoError(t, err)
209require.Equal(t, mb, len(bufm))
210if cap(bufm) == mb4 {
211require.Equal(t, bufm4[:mb], bufm)
212} else if cap(bufm) == mb {
213require.Equal(t, zero, bufm)
214} else {
215require.Fail(t, "cant be there")
216}
217}
218
219func TestMemPoolZero(t *testing.T) {
220buffer := make([]byte, 1<<24)
221zero := make([]byte, 1<<24)
222cases := []struct {
223from, to int
224}{
225{0, 1 << 20},
226{0, 1 << 24},
227{1, 1 << 20},
228{1 << 20, 1 << 20},
229{1 << 20, 1 << 22},
230{1 << 20, 1 << 24},
231{1029, 1029},
232{1029, 1 << 14},
233{1029, 1 << 24},
234}
235
236for _, cs := range cases {
237pool := rp.NewMemPool(nil)
238require.NotNil(t, pool)
239
240buf := buffer[cs.from:cs.to]
241rand.Read(buf)
242if cs.from != cs.to {
243require.NotEqual(t, zero[cs.from:cs.to], buf)
244}
245pool.Zero(buf)
246require.Equal(t, cs.to-cs.from, len(buf))
247require.Equal(t, zero[cs.from:cs.to], buf)
248}
249}
250
251func TestMemPoolStatus(t *testing.T) {
252{
253pool := rp.NewMemPool(nil)
254require.NotNil(t, pool)
255t.Logf("status empty: %+v", pool.Status())
256}
257{
258classes := map[int]int{kb: 2, mb: 1}
259
260pool := rp.NewMemPool(classes)
261require.NotNil(t, pool)
262t.Logf("status init: %+v", pool.Status())
263
264_, err := pool.Get(mb)
265require.NoError(t, err)
266bufk, err := pool.Get(kb)
267require.NoError(t, err)
268t.Logf("status running: %+v", pool.Status())
269
270pool.Put(bufk)
271data, _ := json.Marshal(pool.Status())
272t.Logf("status json: %s", string(data))
273}
274}
275
276func BenchmarkMempool(b *testing.B) {
277mp := rp.NewMemPool(map[int]int{
2781 << 11: -1,
2791 << 14: -1,
2801 << 16: -1,
2811 << 18: -1,
2821 << 20: -1,
2831 << 21: -1,
2841 << 22: -1,
285})
286
287for _, size := range []int{
2881 << 10,
2891 << 16,
2901 << 18,
2911 << 20,
2921 << 21,
2931 << 22,
294} {
295b.ResetTimer()
296b.Run(humman(size), func(b *testing.B) {
297b.ResetTimer()
298for ii := 0; ii <= b.N; ii++ {
299if buf, err := mp.Get(size); err == nil {
300_ = mp.Put(buf)
301}
302}
303})
304}
305}
306
307func BenchmarkZero(b *testing.B) {
308funcZero := func(b, zero []byte) {
309for len(b) > 0 {
310n := copy(b, zero)
311b = b[n:]
312}
313}
314
315cases := []struct {
316size int
317zero int
318}{}
319
320for _, size := range []int{
3211 << 0,
3221 << 5,
3231 << 10,
3241 << 15,
3251 << 20,
3261 << 21,
3271 << 22,
3281 << 23,
3291 << 24,
3301 << 25,
331} {
332// zoro buffer from 256B - 32M
333for i := 8; i <= 24; i++ {
334cases = append(cases, struct {
335size int
336zero int
337}{size, 1 << i})
338}
339}
340
341for _, cs := range cases {
342b.ResetTimer()
343b.Run(fmt.Sprintf("%s-%s", humman(cs.size), humman(cs.zero)), func(b *testing.B) {
344zero := make([]byte, cs.zero)
345buff := make([]byte, cs.size)
346b.ResetTimer()
347for ii := 0; ii <= b.N; ii++ {
348funcZero(buff, zero)
349}
350})
351}
352}
353
354var size2humman = []func(int) string{
355func(size int) string { return fmt.Sprintf("%dB", size) },
356func(size int) string { return fmt.Sprintf("%dKB", size/1024) },
357func(size int) string { return fmt.Sprintf("%dMB", size/1024/1024) },
358func(size int) string { return fmt.Sprintf("%dGB", size/1024/1024/1024) },
359}
360
361func humman(size int) string {
362for ii := len(size2humman) - 1; ii >= 0; ii-- {
363if size >= (1 << (10 * ii)) {
364return size2humman[ii](size)
365}
366}
367return "-"
368}
369