github.com/consensys/gnark-crypto@v0.14.0/internal/generator/polynomial/template/pool.go.tmpl (about) 1 {{ $sham := eq .ElementType "small_rational.SmallRational"}} 2 import ( 3 "{{.FieldPackagePath}}" 4 {{- if not $sham}} 5 "encoding/json" 6 "fmt" 7 "runtime" 8 "sort" 9 "sync" 10 "unsafe" 11 {{- end}} 12 ) 13 14 {{ if $sham}} 15 // Do as little as possible to instantiate the interface 16 type Pool struct { 17 } 18 19 func NewPool(...int) (pool Pool) { 20 return Pool{} 21 } 22 23 func (p *Pool) Make(n int) []{{.ElementType}} { 24 return make([]{{.ElementType}}, n) 25 } 26 27 func (p *Pool) Dump(...[]{{.ElementType}}) { 28 } 29 30 func (p *Pool) Clone(slice []{{.ElementType}}) []{{.ElementType}} { 31 res := p.Make(len(slice)) 32 copy(res, slice) 33 return res 34 } 35 {{ else}} 36 // Memory management for polynomials 37 // WARNING: This is not thread safe TODO: Make sure that is not a problem 38 // TODO: There is a lot of "unsafe" memory management here and needs to be vetted thoroughly 39 40 type sizedPool struct { 41 maxN int 42 pool sync.Pool 43 stats poolStats 44 } 45 46 type inUseData struct { 47 allocatedFor []uintptr 48 pool *sizedPool 49 } 50 51 type Pool struct { 52 //lock sync.Mutex 53 inUse sync.Map 54 subPools []sizedPool 55 } 56 57 func (p *sizedPool) get(n int) *{{.ElementType}} { 58 p.stats.make(n) 59 return p.pool.Get().(*{{.ElementType}}) 60 } 61 62 func (p *sizedPool) put(ptr *{{.ElementType}}) { 63 p.stats.dump() 64 p.pool.Put(ptr) 65 } 66 67 func NewPool(maxN ...int) (pool Pool) { 68 69 sort.Ints(maxN) 70 pool = Pool{ 71 subPools: make([]sizedPool, len(maxN)), 72 } 73 74 for i := range pool.subPools { 75 subPool := &pool.subPools[i] 76 subPool.maxN = maxN[i] 77 subPool.pool = sync.Pool{ 78 New: func() interface{} { 79 subPool.stats.Allocated++ 80 return getDataPointer(make([]{{.ElementType}}, 0, subPool.maxN)) 81 }, 82 } 83 } 84 return 85 } 86 87 func (p *Pool) findCorrespondingPool(n int) *sizedPool { 88 poolI := 0 89 for poolI < len(p.subPools) && n > p.subPools[poolI].maxN { 90 poolI++ 91 } 92 return &p.subPools[poolI] // out of bounds error here would mean that n is too large 93 } 94 95 func (p *Pool) Make(n int) []{{.ElementType}} { 96 pool := p.findCorrespondingPool(n) 97 ptr := pool.get(n) 98 p.addInUse(ptr, pool) 99 return unsafe.Slice(ptr,n) 100 } 101 102 // Dump dumps a set of polynomials into the pool 103 func (p *Pool) Dump(slices ...[]{{.ElementType}}) { 104 for _, slice := range slices { 105 ptr := getDataPointer(slice) 106 if metadata, ok := p.inUse.Load(ptr); ok { 107 p.inUse.Delete(ptr) 108 metadata.(inUseData).pool.put(ptr) 109 } else { 110 panic("attempting to dump a slice not created by the pool") 111 } 112 } 113 } 114 115 func (p *Pool) addInUse(ptr *{{.ElementType}}, pool *sizedPool) { 116 pcs := make([]uintptr, 2) 117 n := runtime.Callers(3, pcs) 118 119 if prevPcs, ok := p.inUse.Load(ptr); ok { // TODO: remove if unnecessary for security 120 panic(fmt.Errorf("re-allocated non-dumped slice, previously allocated at %v", runtime.CallersFrames(prevPcs.(inUseData).allocatedFor))) 121 } 122 p.inUse.Store(ptr, inUseData{ 123 allocatedFor: pcs[:n], 124 pool: pool, 125 }) 126 } 127 128 func printFrame(frame runtime.Frame) { 129 fmt.Printf("\t%s line %d, function %s\n", frame.File, frame.Line, frame.Function) 130 } 131 132 func (p *Pool) printInUse() { 133 fmt.Println("slices never dumped allocated at:") 134 p.inUse.Range(func(_, pcs any) bool { 135 fmt.Println("-------------------------") 136 137 var frame runtime.Frame 138 frames := runtime.CallersFrames(pcs.(inUseData).allocatedFor) 139 more := true 140 for more { 141 frame, more = frames.Next() 142 printFrame(frame) 143 } 144 return true 145 }) 146 } 147 148 type poolStats struct { 149 Used int 150 Allocated int 151 ReuseRate float64 152 InUse int 153 GreatestNUsed int 154 SmallestNUsed int 155 } 156 157 type poolsStats struct { 158 SubPools []poolStats 159 InUse int 160 } 161 162 func (s *poolStats) make(n int) { 163 s.Used++ 164 s.InUse++ 165 if n > s.GreatestNUsed { 166 s.GreatestNUsed = n 167 } 168 if s.SmallestNUsed == 0 || s.SmallestNUsed > n { 169 s.SmallestNUsed = n 170 } 171 } 172 173 func (s *poolStats) dump() { 174 s.InUse-- 175 } 176 177 func (s *poolStats) finalize() { 178 s.ReuseRate = float64(s.Used) / float64(s.Allocated) 179 } 180 181 func getDataPointer(slice []{{.ElementType}}) *{{.ElementType}} { 182 return (*{{.ElementType}})(unsafe.SliceData(slice)) 183 } 184 185 func (p *Pool) PrintPoolStats() { 186 InUse := 0 187 subStats := make([]poolStats, len(p.subPools)) 188 for i := range p.subPools { 189 subPool := &p.subPools[i] 190 subPool.stats.finalize() 191 subStats[i] = subPool.stats 192 InUse += subPool.stats.InUse 193 } 194 195 stats := poolsStats{ 196 SubPools: subStats, 197 InUse: InUse, 198 } 199 serialized, _ := json.MarshalIndent(stats, "", " ") 200 fmt.Println(string(serialized)) 201 p.printInUse() 202 } 203 204 func (p *Pool) Clone(slice []{{.ElementType}}) []{{.ElementType}} { 205 res := p.Make(len(slice)) 206 copy(res, slice) 207 return res 208 } 209 {{end}}