shared_buffer_pool.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. *
  3. * Copyright 2023 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package grpc
  19. import "sync"
  20. // SharedBufferPool is a pool of buffers that can be shared, resulting in
  21. // decreased memory allocation. Currently, in gRPC-go, it is only utilized
  22. // for parsing incoming messages.
  23. //
  24. // # Experimental
  25. //
  26. // Notice: This API is EXPERIMENTAL and may be changed or removed in a
  27. // later release.
  28. type SharedBufferPool interface {
  29. // Get returns a buffer with specified length from the pool.
  30. //
  31. // The returned byte slice may be not zero initialized.
  32. Get(length int) []byte
  33. // Put returns a buffer to the pool.
  34. Put(*[]byte)
  35. }
  36. // NewSharedBufferPool creates a simple SharedBufferPool with buckets
  37. // of different sizes to optimize memory usage. This prevents the pool from
  38. // wasting large amounts of memory, even when handling messages of varying sizes.
  39. //
  40. // # Experimental
  41. //
  42. // Notice: This API is EXPERIMENTAL and may be changed or removed in a
  43. // later release.
  44. func NewSharedBufferPool() SharedBufferPool {
  45. return &simpleSharedBufferPool{
  46. pools: [poolArraySize]simpleSharedBufferChildPool{
  47. newBytesPool(level0PoolMaxSize),
  48. newBytesPool(level1PoolMaxSize),
  49. newBytesPool(level2PoolMaxSize),
  50. newBytesPool(level3PoolMaxSize),
  51. newBytesPool(level4PoolMaxSize),
  52. newBytesPool(0),
  53. },
  54. }
  55. }
  56. // simpleSharedBufferPool is a simple implementation of SharedBufferPool.
  57. type simpleSharedBufferPool struct {
  58. pools [poolArraySize]simpleSharedBufferChildPool
  59. }
  60. func (p *simpleSharedBufferPool) Get(size int) []byte {
  61. return p.pools[p.poolIdx(size)].Get(size)
  62. }
  63. func (p *simpleSharedBufferPool) Put(bs *[]byte) {
  64. p.pools[p.poolIdx(cap(*bs))].Put(bs)
  65. }
  66. func (p *simpleSharedBufferPool) poolIdx(size int) int {
  67. switch {
  68. case size <= level0PoolMaxSize:
  69. return level0PoolIdx
  70. case size <= level1PoolMaxSize:
  71. return level1PoolIdx
  72. case size <= level2PoolMaxSize:
  73. return level2PoolIdx
  74. case size <= level3PoolMaxSize:
  75. return level3PoolIdx
  76. case size <= level4PoolMaxSize:
  77. return level4PoolIdx
  78. default:
  79. return levelMaxPoolIdx
  80. }
  81. }
  82. const (
  83. level0PoolMaxSize = 16 // 16 B
  84. level1PoolMaxSize = level0PoolMaxSize * 16 // 256 B
  85. level2PoolMaxSize = level1PoolMaxSize * 16 // 4 KB
  86. level3PoolMaxSize = level2PoolMaxSize * 16 // 64 KB
  87. level4PoolMaxSize = level3PoolMaxSize * 16 // 1 MB
  88. )
  89. const (
  90. level0PoolIdx = iota
  91. level1PoolIdx
  92. level2PoolIdx
  93. level3PoolIdx
  94. level4PoolIdx
  95. levelMaxPoolIdx
  96. poolArraySize
  97. )
  98. type simpleSharedBufferChildPool interface {
  99. Get(size int) []byte
  100. Put(any)
  101. }
  102. type bufferPool struct {
  103. sync.Pool
  104. defaultSize int
  105. }
  106. func (p *bufferPool) Get(size int) []byte {
  107. bs := p.Pool.Get().(*[]byte)
  108. if cap(*bs) < size {
  109. p.Pool.Put(bs)
  110. return make([]byte, size)
  111. }
  112. return (*bs)[:size]
  113. }
  114. func newBytesPool(size int) simpleSharedBufferChildPool {
  115. return &bufferPool{
  116. Pool: sync.Pool{
  117. New: func() any {
  118. bs := make([]byte, size)
  119. return &bs
  120. },
  121. },
  122. defaultSize: size,
  123. }
  124. }
  125. // nopBufferPool is a buffer pool just makes new buffer without pooling.
  126. type nopBufferPool struct {
  127. }
  128. func (nopBufferPool) Get(length int) []byte {
  129. return make([]byte, length)
  130. }
  131. func (nopBufferPool) Put(*[]byte) {
  132. }