cubefs

Форк
0
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

15
package resourcepool_test
16

17
import (
18
	"crypto/rand"
19
	"encoding/json"
20
	"fmt"
21
	"runtime"
22
	"testing"
23

24
	"github.com/stretchr/testify/require"
25

26
	rp "github.com/cubefs/cubefs/blobstore/common/resourcepool"
27
)
28

29
const (
30
	kb  = 1 << 10
31
	mb  = 1 << 20
32
	mb4 = 4 * mb
33
)
34

35
func TestMemPoolBase(t *testing.T) {
36
	classes := map[int]int{kb: 2, mb: 1}
37

38
	pool := rp.NewMemPool(classes)
39
	require.NotNil(t, pool)
40

41
	bufm, err := pool.Get(mb)
42
	require.NoError(t, err)
43
	require.Equal(t, mb, len(bufm))
44
	require.Equal(t, mb, cap(bufm))
45

46
	_, err = pool.Get(mb)
47
	require.NoError(t, err)
48
	// require.ErrorIs(t, err, rp.ErrPoolLimit)
49

50
	bufk, err := pool.Get(kb)
51
	require.NoError(t, err)
52
	require.Equal(t, kb, len(bufk))
53
	require.Equal(t, kb, cap(bufk))
54

55
	bufk2, err := pool.Get(kb / 2)
56
	require.NoError(t, err)
57
	require.Equal(t, kb/2, len(bufk2))
58
	require.Equal(t, kb, cap(bufk2))
59

60
	err = pool.Put(bufm)
61
	require.NoError(t, err)
62

63
	bufmx, err := pool.Get(mb)
64
	require.NoError(t, err)
65
	require.Equal(t, bufm, bufmx)
66
}
67

68
func TestMemPoolEmpty(t *testing.T) {
69
	pool := rp.NewMemPool(nil)
70
	require.NotNil(t, pool)
71

72
	_, err := pool.Get(kb)
73
	require.ErrorIs(t, err, rp.ErrNoSuitableSizeClass)
74

75
	buf, err := pool.Alloc(kb)
76
	require.NoError(t, err)
77
	require.Equal(t, kb, len(buf))
78
	require.Equal(t, kb, cap(buf))
79

80
	err = pool.Put(buf)
81
	require.ErrorIs(t, err, rp.ErrNoSuitableSizeClass)
82
}
83

84
func TestMemPoolChanAlloc(t *testing.T) {
85
	classes := map[int]int{mb: 1}
86
	pool := rp.NewMemPool(classes)
87
	require.NotNil(t, pool)
88

89
	bufm, err := pool.Get(mb)
90
	require.NoError(t, err)
91
	require.Equal(t, mb, len(bufm))
92
	require.Equal(t, mb, cap(bufm))
93

94
	_, err = pool.Get(mb)
95
	require.NoError(t, err)
96

97
	// if matched size class, return ErrPoolLimit
98
	_, err = pool.Alloc(mb)
99
	require.NoError(t, err)
100

101
	// if oversize, make new buffer
102
	bufm4, err := pool.Alloc(mb4)
103
	require.NoError(t, err)
104
	require.Equal(t, mb4, len(bufm4))
105
	require.Equal(t, mb4, cap(bufm4))
106

107
	// put oversize buffer to top class
108
	err = pool.Put(bufm4)
109
	require.NoError(t, err)
110

111
	// maybe get the oversize buffer
112
	bufm, err = pool.Get(mb)
113
	require.NoError(t, err)
114
	require.Equal(t, mb, len(bufm))
115
	require.True(t, mb4 == cap(bufm) || mb == cap(bufm))
116

117
	err = pool.Put(bufm)
118
	require.NoError(t, err)
119
}
120

121
func TestMemPoolSyncAlloc(t *testing.T) {
122
	classes := map[int]int{mb: 1}
123
	pool := rp.NewMemPoolWith(classes, func(size, capacity int) rp.Pool {
124
		return rp.NewPool(func() interface{} {
125
			return make([]byte, size)
126
		}, capacity)
127
	})
128
	require.NotNil(t, pool)
129

130
	bufm, err := pool.Get(mb)
131
	require.NoError(t, err)
132
	require.Equal(t, mb, len(bufm))
133
	require.Equal(t, mb, cap(bufm))
134

135
	_, err = pool.Get(mb)
136
	require.ErrorIs(t, err, rp.ErrPoolLimit)
137

138
	// if matched size class, return ErrPoolLimit
139
	_, err = pool.Alloc(mb)
140
	require.ErrorIs(t, err, rp.ErrPoolLimit)
141

142
	// if oversize, make new buffer
143
	bufm4, err := pool.Alloc(mb4)
144
	require.NoError(t, err)
145
	require.Equal(t, mb4, len(bufm4))
146
	require.Equal(t, mb4, cap(bufm4))
147

148
	// put oversize buffer to top class
149
	err = pool.Put(bufm4)
150
	require.NoError(t, err)
151

152
	// maybe get the oversize buffer
153
	bufm, err = pool.Get(mb)
154
	require.NoError(t, err)
155
	require.Equal(t, mb, len(bufm))
156
	require.True(t, mb4 == cap(bufm) || mb == cap(bufm))
157

158
	err = pool.Put(bufm)
159
	require.NoError(t, err)
160

161
	// bufm4 released by gc after twice runtime.GC()
162
	// see sync/pool.go: runtime_registerPoolCleanup(poolCleanup)
163
	runtime.GC()
164
	runtime.GC()
165
	bufm, err = pool.Get(mb)
166
	require.NoError(t, err)
167
	require.Equal(t, mb, len(bufm))
168
	require.Equal(t, mb, cap(bufm))
169
}
170

171
func TestMemPoolPutGet(t *testing.T) {
172
	classes := map[int]int{mb: 1}
173

174
	pool := rp.NewMemPool(classes)
175
	require.NotNil(t, pool)
176

177
	bufm, err := pool.Get(mb)
178
	require.NoError(t, err)
179
	require.Equal(t, mb, len(bufm))
180
	require.Equal(t, mb, cap(bufm))
181

182
	zero := make([]byte, len(bufm))
183
	rand.Read(bufm)
184
	require.NotEqual(t, zero, bufm)
185

186
	err = pool.Put(bufm)
187
	require.NoError(t, err)
188

189
	bufmx, err := pool.Get(mb)
190
	require.NoError(t, err)
191
	require.Equal(t, bufm, bufmx)
192

193
	// if oversize, make new buffer
194
	bufm4, err := pool.Alloc(mb4)
195
	require.NoError(t, err)
196
	require.Equal(t, mb4, len(bufm4))
197
	require.Equal(t, mb4, cap(bufm4))
198

199
	rand.Read(bufm4)
200
	require.NotEqual(t, zero, bufm4[:mb])
201

202
	// put oversize buffer to top class
203
	err = pool.Put(bufm4)
204
	require.NoError(t, err)
205

206
	// maybe get the oversize buffer
207
	bufm, err = pool.Get(mb)
208
	require.NoError(t, err)
209
	require.Equal(t, mb, len(bufm))
210
	if cap(bufm) == mb4 {
211
		require.Equal(t, bufm4[:mb], bufm)
212
	} else if cap(bufm) == mb {
213
		require.Equal(t, zero, bufm)
214
	} else {
215
		require.Fail(t, "cant be there")
216
	}
217
}
218

219
func TestMemPoolZero(t *testing.T) {
220
	buffer := make([]byte, 1<<24)
221
	zero := make([]byte, 1<<24)
222
	cases := []struct {
223
		from, 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

236
	for _, cs := range cases {
237
		pool := rp.NewMemPool(nil)
238
		require.NotNil(t, pool)
239

240
		buf := buffer[cs.from:cs.to]
241
		rand.Read(buf)
242
		if cs.from != cs.to {
243
			require.NotEqual(t, zero[cs.from:cs.to], buf)
244
		}
245
		pool.Zero(buf)
246
		require.Equal(t, cs.to-cs.from, len(buf))
247
		require.Equal(t, zero[cs.from:cs.to], buf)
248
	}
249
}
250

251
func TestMemPoolStatus(t *testing.T) {
252
	{
253
		pool := rp.NewMemPool(nil)
254
		require.NotNil(t, pool)
255
		t.Logf("status empty: %+v", pool.Status())
256
	}
257
	{
258
		classes := map[int]int{kb: 2, mb: 1}
259

260
		pool := rp.NewMemPool(classes)
261
		require.NotNil(t, pool)
262
		t.Logf("status init: %+v", pool.Status())
263

264
		_, err := pool.Get(mb)
265
		require.NoError(t, err)
266
		bufk, err := pool.Get(kb)
267
		require.NoError(t, err)
268
		t.Logf("status running: %+v", pool.Status())
269

270
		pool.Put(bufk)
271
		data, _ := json.Marshal(pool.Status())
272
		t.Logf("status json: %s", string(data))
273
	}
274
}
275

276
func BenchmarkMempool(b *testing.B) {
277
	mp := rp.NewMemPool(map[int]int{
278
		1 << 11: -1,
279
		1 << 14: -1,
280
		1 << 16: -1,
281
		1 << 18: -1,
282
		1 << 20: -1,
283
		1 << 21: -1,
284
		1 << 22: -1,
285
	})
286

287
	for _, size := range []int{
288
		1 << 10,
289
		1 << 16,
290
		1 << 18,
291
		1 << 20,
292
		1 << 21,
293
		1 << 22,
294
	} {
295
		b.ResetTimer()
296
		b.Run(humman(size), func(b *testing.B) {
297
			b.ResetTimer()
298
			for ii := 0; ii <= b.N; ii++ {
299
				if buf, err := mp.Get(size); err == nil {
300
					_ = mp.Put(buf)
301
				}
302
			}
303
		})
304
	}
305
}
306

307
func BenchmarkZero(b *testing.B) {
308
	funcZero := func(b, zero []byte) {
309
		for len(b) > 0 {
310
			n := copy(b, zero)
311
			b = b[n:]
312
		}
313
	}
314

315
	cases := []struct {
316
		size int
317
		zero int
318
	}{}
319

320
	for _, size := range []int{
321
		1 << 0,
322
		1 << 5,
323
		1 << 10,
324
		1 << 15,
325
		1 << 20,
326
		1 << 21,
327
		1 << 22,
328
		1 << 23,
329
		1 << 24,
330
		1 << 25,
331
	} {
332
		// zoro buffer from 256B - 32M
333
		for i := 8; i <= 24; i++ {
334
			cases = append(cases, struct {
335
				size int
336
				zero int
337
			}{size, 1 << i})
338
		}
339
	}
340

341
	for _, cs := range cases {
342
		b.ResetTimer()
343
		b.Run(fmt.Sprintf("%s-%s", humman(cs.size), humman(cs.zero)), func(b *testing.B) {
344
			zero := make([]byte, cs.zero)
345
			buff := make([]byte, cs.size)
346
			b.ResetTimer()
347
			for ii := 0; ii <= b.N; ii++ {
348
				funcZero(buff, zero)
349
			}
350
		})
351
	}
352
}
353

354
var size2humman = []func(int) string{
355
	func(size int) string { return fmt.Sprintf("%dB", size) },
356
	func(size int) string { return fmt.Sprintf("%dKB", size/1024) },
357
	func(size int) string { return fmt.Sprintf("%dMB", size/1024/1024) },
358
	func(size int) string { return fmt.Sprintf("%dGB", size/1024/1024/1024) },
359
}
360

361
func humman(size int) string {
362
	for ii := len(size2humman) - 1; ii >= 0; ii-- {
363
		if size >= (1 << (10 * ii)) {
364
			return size2humman[ii](size)
365
		}
366
	}
367
	return "-"
368
}
369

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.