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}}