1 // Copyright 2022 CUE Authors 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 // Package stats is an experimental package for getting statistics on CUE 16 // evaluations. 17 package stats 18 19 import ( 20 "strings" 21 "text/template" 22 ) 23 24 // Counts holds counters for key events during a CUE evaluation. 25 // 26 // This is an experimental type and the contents may change without notice. 27 type Counts struct { 28 // Operation counters 29 // 30 // These counters account for several key operations. 31 32 // Unifications counts the number of calls to adt.Unify 33 Unifications int64 34 35 // Disjuncts indicates the number of total disjuncts processed as part 36 // of a Unify operation. A unification with no | operator counts as a 37 // single disjunct, so Disjuncts is always greater than or equal to the 38 // number of Unifications. 39 // 40 // If Disjuncts is much larger than Unification, this may indicate room 41 // for optimization. In particular, most practical uses of disjunctions 42 // should allow for near-linear processing. 43 Disjuncts int64 44 45 // Conjuncts is an estimate of the number of conjunctions processed during 46 // the calls to Unify. This includes the conjuncts added in the compilation 47 // phase as well as the derivative conjuncts inserted from other nodes 48 // after following references. 49 // 50 // A number of Conjuncts much larger than Disjuncts may indicate non-linear 51 // algorithmic behavior. 52 Conjuncts int64 53 54 // Buffer counters 55 // 56 // Each unification and disjunct operation is associated with an object 57 // with temporary buffers. Reuse of this buffer is critical for performance. 58 // The following counters track this. 59 60 Freed int64 // Number of buffers returned to the free pool. 61 Reused int64 // Number of times a buffer is reused instead of allocated. 62 Allocs int64 // Total number of allocated buffer objects. 63 Retained int64 // Number of times a buffer is retained upon finalization. 64 } 65 66 // TODO: None of the methods below protect against overflows or underflows. 67 // If those start happening in practice, or if the counters get large enough, 68 // add checks on each of the operations. 69 70 func (c *Counts) Add(other Counts) { 71 c.Unifications += other.Unifications 72 c.Conjuncts += other.Conjuncts 73 c.Disjuncts += other.Disjuncts 74 75 c.Freed += other.Freed 76 c.Retained += other.Retained 77 c.Reused += other.Reused 78 c.Allocs += other.Allocs 79 } 80 81 func (c Counts) Since(start Counts) Counts { 82 c.Unifications -= start.Unifications 83 c.Conjuncts -= start.Conjuncts 84 c.Disjuncts -= start.Disjuncts 85 86 c.Freed -= start.Freed 87 c.Retained -= start.Retained 88 c.Reused -= start.Reused 89 c.Allocs -= start.Allocs 90 91 return c 92 } 93 94 // Leaks reports the number of nodeContext structs leaked. These are typically 95 // benign, as they will just be garbage collected, as long as the pointer from 96 // the original nodes has been eliminated or the original nodes are also not 97 // referred to. But Leaks may have notable impact on performance, and thus 98 // should be avoided. 99 func (s Counts) Leaks() int64 { 100 return s.Allocs + s.Reused - s.Freed 101 } 102 103 var stats = template.Must(template.New("stats").Parse(`{{"" -}} 104 105 Leaks: {{.Leaks}} 106 Freed: {{.Freed}} 107 Reused: {{.Reused}} 108 Allocs: {{.Allocs}} 109 Retain: {{.Retained}} 110 111 Unifications: {{.Unifications}} 112 Conjuncts: {{.Conjuncts}} 113 Disjuncts: {{.Disjuncts}}`)) 114 115 func (s Counts) String() string { 116 buf := &strings.Builder{} 117 err := stats.Execute(buf, s) 118 if err != nil { 119 panic(err) 120 } 121 return buf.String() 122 } 123