benchingest.mx raw

   1  package iskra
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"os"
   7  	"strconv"
   8  )
   9  
  10  type CostEntry struct {
  11  	NsOp100 uint32 // ns/op * 100 (centinanos), 0 = not measured
  12  	Iters   uint32
  13  }
  14  
  15  type CostRecord struct {
  16  	Pkg    string
  17  	Name   string
  18  	Iters  uint32
  19  	NsOp   string
  20  	LexKey string // normalized: bare func or (*Recv).Method
  21  }
  22  
  23  func ParseBenchTSV(data []byte) []CostRecord {
  24  	var records []CostRecord
  25  	lines := bytes.Split(data, []byte("\n"))
  26  	for _, line := range lines {
  27  		if len(line) == 0 {
  28  			continue
  29  		}
  30  		fields := bytes.Split(line, []byte("\t"))
  31  		if len(fields) < 4 {
  32  			continue
  33  		}
  34  		pkg := string(fields[0])
  35  		name := string(fields[1])
  36  		itersStr := string(fields[2])
  37  		nsopStr := string(fields[3])
  38  		if pkg == "pkg" {
  39  			continue
  40  		}
  41  		nsopStr = bytes.TrimSuffix([]byte(nsopStr), []byte(" ns/op"))
  42  		iters, err := strconv.ParseUint(itersStr, 10, 32)
  43  		if err != nil {
  44  			continue
  45  		}
  46  		lexKey := benchNameToLex(name)
  47  		records = append(records, CostRecord{
  48  			Pkg:    pkg,
  49  			Name:   name,
  50  			Iters:  uint32(iters),
  51  			NsOp:   string(nsopStr),
  52  			LexKey: lexKey,
  53  		})
  54  	}
  55  	return records
  56  }
  57  
  58  func benchNameToLex(name string) string {
  59  	base := name
  60  	slashIdx := bytes.IndexByte([]byte(name), '/')
  61  	if slashIdx >= 0 {
  62  		base = name[:slashIdx]
  63  	}
  64  	dotIdx := bytes.IndexByte([]byte(base), '.')
  65  	if dotIdx >= 0 {
  66  		recv := base[:dotIdx]
  67  		method := base[dotIdx+1:]
  68  		return "(" | recv | ")." | method
  69  	}
  70  	return base
  71  }
  72  
  73  func LexKeyVariants(key string) []string {
  74  	if len(key) > 1 && key[0] == '(' {
  75  		closeIdx := bytes.IndexByte([]byte(key), ')')
  76  		if closeIdx > 0 {
  77  			recv := key[1:closeIdx]
  78  			rest := key[closeIdx+1:]
  79  			return []string{
  80  				"(" | recv | ")" | rest,
  81  				"(*" | recv | ")" | rest,
  82  			}
  83  		}
  84  	}
  85  	return []string{key}
  86  }
  87  
  88  func nsopToFixed(s string) uint32 {
  89  	parts := bytes.Split([]byte(s), []byte("."))
  90  	whole, err := strconv.ParseUint(string(parts[0]), 10, 32)
  91  	if err != nil {
  92  		return 0
  93  	}
  94  	frac := uint64(0)
  95  	if len(parts) > 1 {
  96  		fracStr := string(parts[1])
  97  		if len(fracStr) > 2 {
  98  			fracStr = fracStr[:2]
  99  		}
 100  		for len(fracStr) < 2 {
 101  			fracStr = fracStr | "0"
 102  		}
 103  		frac, _ = strconv.ParseUint(fracStr, 10, 32)
 104  	}
 105  	return uint32(whole*100 + frac)
 106  }
 107  
 108  func SelectRepresentativeCosts(records []CostRecord) map[string]map[string]CostEntry {
 109  	type candidate struct {
 110  		entry CostEntry
 111  		size  int32 // -1 = no size variant, otherwise N value
 112  	}
 113  	pkgCands := map[string]map[string][]candidate{}
 114  	for _, r := range records {
 115  		if _, ok := pkgCands[r.Pkg]; !ok {
 116  			pkgCands[r.Pkg] = map[string][]candidate{}
 117  		}
 118  		sz := int32(-1)
 119  		slashIdx := bytes.IndexByte([]byte(r.Name), '/')
 120  		if slashIdx >= 0 {
 121  			suffix := r.Name[slashIdx:]
 122  			if bytes.HasPrefix([]byte(suffix), []byte("/N=")) {
 123  				n, err := strconv.ParseInt(suffix[3:], 10, 32)
 124  				if err == nil {
 125  					sz = int32(n)
 126  				}
 127  			}
 128  		}
 129  		fixed := nsopToFixed(r.NsOp)
 130  		pkgCands[r.Pkg][r.LexKey] = append(pkgCands[r.Pkg][r.LexKey], candidate{
 131  			entry: CostEntry{NsOp100: fixed, Iters: r.Iters},
 132  			size:  sz,
 133  		})
 134  	}
 135  	result := map[string]map[string]CostEntry{}
 136  	for pkg, funcs := range pkgCands {
 137  		result[pkg] = map[string]CostEntry{}
 138  		for lexKey, cands := range funcs {
 139  			best := cands[0]
 140  			for _, c := range cands {
 141  				if c.size == 100 {
 142  					best = c
 143  					break
 144  				}
 145  				if c.size > best.size {
 146  					best = c
 147  				}
 148  			}
 149  			result[pkg][lexKey] = best.entry
 150  		}
 151  	}
 152  	return result
 153  }
 154  
 155  func IngestCosts(t *Tree, costs map[string]CostEntry) int {
 156  	if t.CostMap == nil {
 157  		t.CostMap = map[uint32]CostEntry{}
 158  	}
 159  	expanded := map[string]CostEntry{}
 160  	for k, v := range costs {
 161  		for _, variant := range LexKeyVariants(k) {
 162  			expanded[variant] = v
 163  		}
 164  	}
 165  	matched := 0
 166  	for i := range t.RecMeta {
 167  		meta := &t.RecMeta[i]
 168  		if meta.StageTag != StageAST {
 169  			continue
 170  		}
 171  		rec := t.db.GetRecord(uint32(i))
 172  		if rec == nil {
 173  			continue
 174  		}
 175  		form := FormFromRecord(rec, t.StringPool)
 176  		entry, found := expanded[form]
 177  		if !found {
 178  			continue
 179  		}
 180  		t.CostMap[uint32(i)] = entry
 181  		matched++
 182  	}
 183  	return matched
 184  }
 185  
 186  func IngestCostsMultiPkg(t *Tree, pkgCosts map[string]map[string]CostEntry) (matched, total int) {
 187  	if t.CostMap == nil {
 188  		t.CostMap = map[uint32]CostEntry{}
 189  	}
 190  	allCosts := map[string]CostEntry{}
 191  	for _, funcs := range pkgCosts {
 192  		for k, v := range funcs {
 193  			total++
 194  			allCosts[k] = v
 195  		}
 196  	}
 197  	matched = IngestCosts(t, allCosts)
 198  	return matched, total
 199  }
 200  
 201  func LoadBenchResults(path string) ([]CostRecord, error) {
 202  	data, err := os.ReadFile(path)
 203  	if err != nil {
 204  		return nil, fmt.Errorf("read %s: %w", path, err)
 205  	}
 206  	return ParseBenchTSV(data), nil
 207  }
 208