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