xref: /aosp_15_r20/external/swiftshader/tests/regres/cov/optimization.go (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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