github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-378/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-378/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  }