1*03ce13f7SAndroid Build Coastguard Worker// Copyright 2020 The SwiftShader Authors. All Rights Reserved. 2*03ce13f7SAndroid Build Coastguard Worker// 3*03ce13f7SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*03ce13f7SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*03ce13f7SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*03ce13f7SAndroid Build Coastguard Worker// 7*03ce13f7SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*03ce13f7SAndroid Build Coastguard Worker// 9*03ce13f7SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*03ce13f7SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*03ce13f7SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*03ce13f7SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*03ce13f7SAndroid Build Coastguard Worker// limitations under the License. 14*03ce13f7SAndroid Build Coastguard Worker 15*03ce13f7SAndroid Build Coastguard Workerpackage cov 16*03ce13f7SAndroid Build Coastguard Worker 17*03ce13f7SAndroid Build Coastguard Workerimport ( 18*03ce13f7SAndroid Build Coastguard Worker "log" 19*03ce13f7SAndroid Build Coastguard Worker "sort" 20*03ce13f7SAndroid Build Coastguard Worker "sync" 21*03ce13f7SAndroid Build Coastguard Worker) 22*03ce13f7SAndroid Build Coastguard Worker 23*03ce13f7SAndroid Build Coastguard Worker// Optimize optimizes the Tree by de-duplicating common spans into a tree of 24*03ce13f7SAndroid Build Coastguard Worker// SpanGroups. 25*03ce13f7SAndroid Build Coastguard Workerfunc (t *Tree) Optimize() { 26*03ce13f7SAndroid Build Coastguard Worker log.Printf("Optimizing coverage tree...") 27*03ce13f7SAndroid Build Coastguard Worker 28*03ce13f7SAndroid Build Coastguard Worker // Start by gathering all of the unique spansets 29*03ce13f7SAndroid Build Coastguard Worker wg := sync.WaitGroup{} 30*03ce13f7SAndroid Build Coastguard Worker wg.Add(len(t.files)) 31*03ce13f7SAndroid Build Coastguard Worker for _, file := range t.files { 32*03ce13f7SAndroid Build Coastguard Worker file := file 33*03ce13f7SAndroid Build Coastguard Worker go func() { 34*03ce13f7SAndroid Build Coastguard Worker defer wg.Done() 35*03ce13f7SAndroid Build Coastguard Worker o := optimizer{} 36*03ce13f7SAndroid Build Coastguard Worker for idx, tc := range file.tcm { 37*03ce13f7SAndroid Build Coastguard Worker o.invertForCommon(tc, &t.testRoot.children[idx]) 38*03ce13f7SAndroid Build Coastguard Worker } 39*03ce13f7SAndroid Build Coastguard Worker o.createGroups(file) 40*03ce13f7SAndroid Build Coastguard Worker }() 41*03ce13f7SAndroid Build Coastguard Worker } 42*03ce13f7SAndroid Build Coastguard Worker wg.Wait() 43*03ce13f7SAndroid Build Coastguard Worker} 44*03ce13f7SAndroid Build Coastguard Worker 45*03ce13f7SAndroid Build Coastguard Workertype optimizer struct{} 46*03ce13f7SAndroid Build Coastguard Worker 47*03ce13f7SAndroid Build Coastguard Worker// createGroups looks for common SpanSets, and creates indexable span groups 48*03ce13f7SAndroid Build Coastguard Worker// which are then used instead. 49*03ce13f7SAndroid Build Coastguard Workerfunc (o *optimizer) createGroups(f *treeFile) { 50*03ce13f7SAndroid Build Coastguard Worker const minSpansInGroup = 2 51*03ce13f7SAndroid Build Coastguard Worker 52*03ce13f7SAndroid Build Coastguard Worker type spansetKey string 53*03ce13f7SAndroid Build Coastguard Worker spansetMap := map[spansetKey]SpanSet{} 54*03ce13f7SAndroid Build Coastguard Worker 55*03ce13f7SAndroid Build Coastguard Worker f.tcm.traverse(func(tc *TestCoverage) { 56*03ce13f7SAndroid Build Coastguard Worker if len(tc.Spans) >= minSpansInGroup { 57*03ce13f7SAndroid Build Coastguard Worker key := spansetKey(tc.Spans.String()) 58*03ce13f7SAndroid Build Coastguard Worker if _, ok := spansetMap[key]; !ok { 59*03ce13f7SAndroid Build Coastguard Worker spansetMap[key] = tc.Spans 60*03ce13f7SAndroid Build Coastguard Worker } 61*03ce13f7SAndroid Build Coastguard Worker } 62*03ce13f7SAndroid Build Coastguard Worker }) 63*03ce13f7SAndroid Build Coastguard Worker 64*03ce13f7SAndroid Build Coastguard Worker if len(spansetMap) == 0 { 65*03ce13f7SAndroid Build Coastguard Worker return 66*03ce13f7SAndroid Build Coastguard Worker } 67*03ce13f7SAndroid Build Coastguard Worker 68*03ce13f7SAndroid Build Coastguard Worker type spansetInfo struct { 69*03ce13f7SAndroid Build Coastguard Worker key spansetKey 70*03ce13f7SAndroid Build Coastguard Worker set SpanSet // fully expanded set 71*03ce13f7SAndroid Build Coastguard Worker grp SpanGroup 72*03ce13f7SAndroid Build Coastguard Worker id SpanGroupID 73*03ce13f7SAndroid Build Coastguard Worker } 74*03ce13f7SAndroid Build Coastguard Worker spansets := make([]*spansetInfo, 0, len(spansetMap)) 75*03ce13f7SAndroid Build Coastguard Worker for key, set := range spansetMap { 76*03ce13f7SAndroid Build Coastguard Worker spansets = append(spansets, &spansetInfo{ 77*03ce13f7SAndroid Build Coastguard Worker key: key, 78*03ce13f7SAndroid Build Coastguard Worker set: set, 79*03ce13f7SAndroid Build Coastguard Worker grp: SpanGroup{Spans: set}, 80*03ce13f7SAndroid Build Coastguard Worker }) 81*03ce13f7SAndroid Build Coastguard Worker } 82*03ce13f7SAndroid Build Coastguard Worker 83*03ce13f7SAndroid Build Coastguard Worker // Sort by number of spans in each sets starting with the largest. 84*03ce13f7SAndroid Build Coastguard Worker sort.Slice(spansets, func(i, j int) bool { 85*03ce13f7SAndroid Build Coastguard Worker a, b := spansets[i].set, spansets[j].set 86*03ce13f7SAndroid Build Coastguard Worker switch { 87*03ce13f7SAndroid Build Coastguard Worker case len(a) > len(b): 88*03ce13f7SAndroid Build Coastguard Worker return true 89*03ce13f7SAndroid Build Coastguard Worker case len(a) < len(b): 90*03ce13f7SAndroid Build Coastguard Worker return false 91*03ce13f7SAndroid Build Coastguard Worker } 92*03ce13f7SAndroid Build Coastguard Worker return a.List().Compare(b.List()) == -1 // Just to keep output stable 93*03ce13f7SAndroid Build Coastguard Worker }) 94*03ce13f7SAndroid Build Coastguard Worker 95*03ce13f7SAndroid Build Coastguard Worker // Assign IDs now that we have stable order. 96*03ce13f7SAndroid Build Coastguard Worker for i := range spansets { 97*03ce13f7SAndroid Build Coastguard Worker spansets[i].id = SpanGroupID(i) 98*03ce13f7SAndroid Build Coastguard Worker } 99*03ce13f7SAndroid Build Coastguard Worker 100*03ce13f7SAndroid Build Coastguard Worker // Loop over the spanGroups starting from the largest, and try to fold them 101*03ce13f7SAndroid Build Coastguard Worker // into the larger sets. 102*03ce13f7SAndroid Build Coastguard Worker // This is O(n^2) complexity. 103*03ce13f7SAndroid Build Coastguard WorkernextSpan: 104*03ce13f7SAndroid Build Coastguard Worker for i, a := range spansets[:len(spansets)-1] { 105*03ce13f7SAndroid Build Coastguard Worker for _, b := range spansets[i+1:] { 106*03ce13f7SAndroid Build Coastguard Worker if len(a.set) > len(b.set) && a.set.containsAll(b.set) { 107*03ce13f7SAndroid Build Coastguard Worker extend := b.id // Do not take address of iterator! 108*03ce13f7SAndroid Build Coastguard Worker a.grp.Spans = a.set.removeAll(b.set) 109*03ce13f7SAndroid Build Coastguard Worker a.grp.Extend = &extend 110*03ce13f7SAndroid Build Coastguard Worker continue nextSpan 111*03ce13f7SAndroid Build Coastguard Worker } 112*03ce13f7SAndroid Build Coastguard Worker } 113*03ce13f7SAndroid Build Coastguard Worker } 114*03ce13f7SAndroid Build Coastguard Worker 115*03ce13f7SAndroid Build Coastguard Worker // Rebuild a map of spansetKey to SpanGroup 116*03ce13f7SAndroid Build Coastguard Worker spangroupMap := make(map[spansetKey]*spansetInfo, len(spansets)) 117*03ce13f7SAndroid Build Coastguard Worker for _, s := range spansets { 118*03ce13f7SAndroid Build Coastguard Worker spangroupMap[s.key] = s 119*03ce13f7SAndroid Build Coastguard Worker } 120*03ce13f7SAndroid Build Coastguard Worker 121*03ce13f7SAndroid Build Coastguard Worker // Store the groups in the tree 122*03ce13f7SAndroid Build Coastguard Worker f.spangroups = make(map[SpanGroupID]SpanGroup, len(spansets)) 123*03ce13f7SAndroid Build Coastguard Worker for _, s := range spansets { 124*03ce13f7SAndroid Build Coastguard Worker f.spangroups[s.id] = s.grp 125*03ce13f7SAndroid Build Coastguard Worker } 126*03ce13f7SAndroid Build Coastguard Worker 127*03ce13f7SAndroid Build Coastguard Worker // Update all the uses. 128*03ce13f7SAndroid Build Coastguard Worker f.tcm.traverse(func(tc *TestCoverage) { 129*03ce13f7SAndroid Build Coastguard Worker key := spansetKey(tc.Spans.String()) 130*03ce13f7SAndroid Build Coastguard Worker if g, ok := spangroupMap[key]; ok { 131*03ce13f7SAndroid Build Coastguard Worker tc.Spans = nil 132*03ce13f7SAndroid Build Coastguard Worker tc.Group = &g.id 133*03ce13f7SAndroid Build Coastguard Worker } 134*03ce13f7SAndroid Build Coastguard Worker }) 135*03ce13f7SAndroid Build Coastguard Worker} 136*03ce13f7SAndroid Build Coastguard Worker 137*03ce13f7SAndroid Build Coastguard Worker// invertCommon looks for tree nodes with the majority of the child nodes with 138*03ce13f7SAndroid Build Coastguard Worker// the same spans. This span is promoted up to the parent, and the children 139*03ce13f7SAndroid Build Coastguard Worker// have the span inverted. 140*03ce13f7SAndroid Build Coastguard Workerfunc (o *optimizer) invertForCommon(tc *TestCoverage, t *Test) { 141*03ce13f7SAndroid Build Coastguard Worker wg := sync.WaitGroup{} 142*03ce13f7SAndroid Build Coastguard Worker wg.Add(len(tc.Children)) 143*03ce13f7SAndroid Build Coastguard Worker for id, child := range tc.Children { 144*03ce13f7SAndroid Build Coastguard Worker id, child := id, child 145*03ce13f7SAndroid Build Coastguard Worker go func() { 146*03ce13f7SAndroid Build Coastguard Worker defer wg.Done() 147*03ce13f7SAndroid Build Coastguard Worker o.invertForCommon(child, &t.children[id]) 148*03ce13f7SAndroid Build Coastguard Worker }() 149*03ce13f7SAndroid Build Coastguard Worker } 150*03ce13f7SAndroid Build Coastguard Worker wg.Wait() 151*03ce13f7SAndroid Build Coastguard Worker 152*03ce13f7SAndroid Build Coastguard Worker counts := map[SpanID]int{} 153*03ce13f7SAndroid Build Coastguard Worker for _, child := range tc.Children { 154*03ce13f7SAndroid Build Coastguard Worker for span := range child.Spans { 155*03ce13f7SAndroid Build Coastguard Worker counts[span] = counts[span] + 1 156*03ce13f7SAndroid Build Coastguard Worker } 157*03ce13f7SAndroid Build Coastguard Worker } 158*03ce13f7SAndroid Build Coastguard Worker 159*03ce13f7SAndroid Build Coastguard Worker for span, count := range counts { 160*03ce13f7SAndroid Build Coastguard Worker if count > len(t.children)/2 { 161*03ce13f7SAndroid Build Coastguard Worker tc.Spans = tc.Spans.invert(span) 162*03ce13f7SAndroid Build Coastguard Worker for _, idx := range t.indices { 163*03ce13f7SAndroid Build Coastguard Worker child := tc.Children.index(idx) 164*03ce13f7SAndroid Build Coastguard Worker child.Spans = child.Spans.invert(span) 165*03ce13f7SAndroid Build Coastguard Worker if child.deletable() { 166*03ce13f7SAndroid Build Coastguard Worker delete(tc.Children, idx) 167*03ce13f7SAndroid Build Coastguard Worker } 168*03ce13f7SAndroid Build Coastguard Worker } 169*03ce13f7SAndroid Build Coastguard Worker } 170*03ce13f7SAndroid Build Coastguard Worker } 171*03ce13f7SAndroid Build Coastguard Worker} 172