benchclass.mx raw

   1  package iskra
   2  
   3  import "bytes"
   4  
   5  type BenchClass uint8
   6  
   7  const (
   8  	BenchSkip    BenchClass = 0
   9  	BenchSimple  BenchClass = 1
  10  	BenchComplex BenchClass = 2
  11  )
  12  
  13  // ClassifyBenchCost determines if a function warrants benchmarking
  14  // based on its AST dump structure.
  15  func ClassifyBenchCost(astDump string, funcName string) BenchClass {
  16  	data := []byte(astDump)
  17  	lines := bytes.Split(data, []byte("\n"))
  18  
  19  	hasLoop := false
  20  	hasSwitch := false
  21  	maxDepth := int32(0)
  22  	blockCount := int32(0)
  23  	hasSliceParam := false
  24  	isRecursive := false
  25  	funcNameBytes := []byte(funcName)
  26  
  27  	for _, line := range lines {
  28  		trimmed := bytes.TrimSpace(line)
  29  		if len(trimmed) == 0 {
  30  			continue
  31  		}
  32  
  33  		depth := indentDepth(line)
  34  		if depth > maxDepth {
  35  			maxDepth = depth
  36  		}
  37  
  38  		if bytes.HasPrefix(trimmed, []byte("For")) || bytes.HasPrefix(trimmed, []byte("Range")) {
  39  			hasLoop = true
  40  		}
  41  		if bytes.HasPrefix(trimmed, []byte("Switch")) || bytes.HasPrefix(trimmed, []byte("Select")) {
  42  			hasSwitch = true
  43  		}
  44  		if bytes.HasPrefix(trimmed, []byte("Block")) {
  45  			blockCount++
  46  		}
  47  		if bytes.HasPrefix(trimmed, []byte("Params")) {
  48  			hasSliceParam = checkSliceParams(lines)
  49  		}
  50  		if bytes.Contains(trimmed, []byte("[")) && bytes.Contains(trimmed, funcNameBytes) {
  51  			isRecursive = true
  52  		}
  53  	}
  54  
  55  	if hasLoop || isRecursive {
  56  		return BenchComplex
  57  	}
  58  	if hasSwitch || hasSliceParam || maxDepth > 4 || blockCount > 2 {
  59  		return BenchSimple
  60  	}
  61  	if maxDepth <= 2 && blockCount <= 1 {
  62  		return BenchSkip
  63  	}
  64  	return BenchSimple
  65  }
  66  
  67  func indentDepth(line []byte) int32 {
  68  	d := int32(0)
  69  	for _, b := range string(line) {
  70  		if b == ' ' {
  71  			d++
  72  		} else if b == '\t' {
  73  			d += 2
  74  		} else {
  75  			break
  76  		}
  77  	}
  78  	return d / 2
  79  }
  80  
  81  func checkSliceParams(lines [][]byte) bool {
  82  	inParams := false
  83  	for _, line := range lines {
  84  		trimmed := bytes.TrimSpace(line)
  85  		if bytes.HasPrefix(trimmed, []byte("Params")) {
  86  			inParams = true
  87  			continue
  88  		}
  89  		if inParams {
  90  			if len(trimmed) > 0 && trimmed[0] != ' ' && !bytes.HasPrefix(line, []byte("  ")) {
  91  				break
  92  			}
  93  			if bytes.Contains(trimmed, []byte("[]")) || bytes.Contains(trimmed, []byte("string")) {
  94  				return true
  95  			}
  96  		}
  97  	}
  98  	return false
  99  }
 100  
 101  func (c BenchClass) String() string {
 102  	switch c {
 103  	case BenchSkip:
 104  		return "skip"
 105  	case BenchSimple:
 106  		return "simple"
 107  	case BenchComplex:
 108  		return "complex"
 109  	default:
 110  		return "unknown"
 111  	}
 112  }
 113