llvm.org GIT mirror llvm / 73c6031
ADT: Add DAGDeltaAlgorithm, which is a DAG minimization algorithm built on top of the standard 'delta debugging' algorithm. - This can give substantial speedups in the delta process for inputs we can construct dependency information for. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@105612 91177308-0d34-0410-b5e6-96231b3b80d8 Daniel Dunbar 10 years ago
4 changed file(s) with 553 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 //===--- DAGDeltaAlgorithm.h - A DAG Minimization Algorithm ----*- C++ -*--===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
7
8 #ifndef LLVM_ADT_DAGDELTAALGORITHM_H
9 #define LLVM_ADT_DAGDELTAALGORITHM_H
10
11 #include
12 #include
13
14 namespace llvm {
15
16 /// DAGDeltaAlgorithm - Implements a "delta debugging" algorithm for minimizing
17 /// directed acyclic graphs using a predicate function.
18 ///
19 /// The result of the algorithm is a subset of the input change set which is
20 /// guaranteed to satisfy the predicate, assuming that the input set did. For
21 /// well formed predicates, the result set is guaranteed to be such that
22 /// removing any single element not required by the dependencies on the other
23 /// elements would falsify the predicate.
24 ///
25 /// The DAG should be used to represent dependencies in the changes which are
26 /// likely to hold across the predicate function. That is, for a particular
27 /// changeset S and predicate P:
28 ///
29 /// P(S) => P(S union pred(S))
30 ///
31 /// The minization algorithm uses this dependency information to attempt to
32 /// eagerly prune large subsets of changes. As with \see DeltaAlgorithm, the DAG
33 /// is not required to satisfy this property, but the algorithm will run
34 /// substantially fewer tests with appropriate dependencies. \see DeltaAlgorithm
35 /// for more information on the properties which the predicate function itself
36 /// should satisfy.
37 class DAGDeltaAlgorithm {
38 public:
39 typedef unsigned change_ty;
40 typedef std::pair edge_ty;
41
42 // FIXME: Use a decent data structure.
43 typedef std::set changeset_ty;
44 typedef std::vector changesetlist_ty;
45
46 public:
47 /// Run - Minimize the DAG formed by the \arg Changes vertices and the \arg
48 /// Dependencies edges by executing \see ExecuteOneTest() on subsets of
49 /// changes and returning the smallest set which still satisfies the test
50 /// predicate and the input \arg Dependencies.
51 ///
52 /// \param Changes The list of changes.
53 ///
54 /// \param Dependencies The list of dependencies amongst changes. For each
55 /// (x,y) in \arg Dependencies, both x and y must be in \arg Changes. The
56 /// minimization algorithm guarantees that for each tested changed set S, x
57 /// \in S implies y \in S. It is an error to have cyclic dependencies.
58 changeset_ty Run(const changeset_ty &Changes,
59 const std::vector &Dependencies);
60
61 /// UpdatedSearchState - Callback used when the search state changes.
62 virtual void UpdatedSearchState(const changeset_ty &Changes,
63 const changesetlist_ty &Sets,
64 const changeset_ty &Required) {}
65
66 /// ExecuteOneTest - Execute a single test predicate on the change set \arg S.
67 virtual bool ExecuteOneTest(const changeset_ty &S) = 0;
68 };
69
70 } // end namespace llvm
71
72 #endif
77 ConstantRange.cpp
88 Debug.cpp
99 DeltaAlgorithm.cpp
10 DAGDeltaAlgorithm.cpp
1011 Dwarf.cpp
1112 ErrorHandling.cpp
1213 FileUtilities.cpp
0 //===--- DAGDeltaAlgorithm.cpp - A DAG Minimization Algorithm --*- C++ -*--===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
7 //
8 // The algorithm we use attempts to exploit the dependency information by
9 // minimizing top-down. We start by constructing an initial root set R, and
10 // then iteratively:
11 //
12 // 1. Minimize the set R using the test predicate:
13 // P'(S) = P(S union pred*(S))
14 //
15 // 2. Extend R to R' = R union pred(R).
16 //
17 // until a fixed point is reached.
18 //
19 // The idea is that we want to quickly prune entire portions of the graph, so we
20 // try to find high-level nodes that can be eliminated with all of their
21 // dependents.
22 //
23 // FIXME: The current algorithm doesn't actually provide a strong guarantee
24 // about the minimality of the result. The problem is that after adding nodes to
25 // the required set, we no longer consider them for elimination. For strictly
26 // well formed predicates, this doesn't happen, but it commonly occurs in
27 // practice when there are unmodelled dependencies. I believe we can resolve
28 // this by allowing the required set to be minimized as well, but need more test
29 // cases first.
30 //
31 //===----------------------------------------------------------------------===//
32
33 #include "llvm/ADT/DAGDeltaAlgorithm.h"
34 #include "llvm/ADT/DeltaAlgorithm.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/Format.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include
39 #include
40 #include
41 #include
42 using namespace llvm;
43
44 namespace {
45
46 class DAGDeltaAlgorithmImpl {
47 friend class DeltaActiveSetHelper;
48
49 public:
50 typedef DAGDeltaAlgorithm::change_ty change_ty;
51 typedef DAGDeltaAlgorithm::changeset_ty changeset_ty;
52 typedef DAGDeltaAlgorithm::changesetlist_ty changesetlist_ty;
53 typedef DAGDeltaAlgorithm::edge_ty edge_ty;
54
55 private:
56 typedef std::vector::iterator pred_iterator_ty;
57 typedef std::vector::iterator succ_iterator_ty;
58 typedef std::set::iterator pred_closure_iterator_ty;
59 typedef std::set::iterator succ_closure_iterator_ty;
60
61 DAGDeltaAlgorithm &DDA;
62
63 const changeset_ty &Changes;
64 const std::vector &Dependencies;
65
66 std::vector Roots;
67
68 /// Cache of failed test results. Successful test results are never cached
69 /// since we always reduce following a success. We maintain an independent
70 /// cache from that used by the individual delta passes because we may get
71 /// hits across multiple individual delta invocations.
72 mutable std::set FailedTestsCache;
73
74 // FIXME: Gross.
75 std::map > Predecessors;
76 std::map > Successors;
77
78 std::map > PredClosure;
79 std::map > SuccClosure;
80
81 private:
82 pred_iterator_ty pred_begin(change_ty Node) {
83 assert(Predecessors.count(Node) && "Invalid node!");
84 return Predecessors[Node].begin();
85 }
86 pred_iterator_ty pred_end(change_ty Node) {
87 assert(Predecessors.count(Node) && "Invalid node!");
88 return Predecessors[Node].end();
89 }
90
91 pred_closure_iterator_ty pred_closure_begin(change_ty Node) {
92 assert(PredClosure.count(Node) && "Invalid node!");
93 return PredClosure[Node].begin();
94 }
95 pred_closure_iterator_ty pred_closure_end(change_ty Node) {
96 assert(PredClosure.count(Node) && "Invalid node!");
97 return PredClosure[Node].end();
98 }
99
100 succ_iterator_ty succ_begin(change_ty Node) {
101 assert(Successors.count(Node) && "Invalid node!");
102 return Successors[Node].begin();
103 }
104 succ_iterator_ty succ_end(change_ty Node) {
105 assert(Successors.count(Node) && "Invalid node!");
106 return Successors[Node].end();
107 }
108
109 succ_closure_iterator_ty succ_closure_begin(change_ty Node) {
110 assert(SuccClosure.count(Node) && "Invalid node!");
111 return SuccClosure[Node].begin();
112 }
113 succ_closure_iterator_ty succ_closure_end(change_ty Node) {
114 assert(SuccClosure.count(Node) && "Invalid node!");
115 return SuccClosure[Node].end();
116 }
117
118 void UpdatedSearchState(const changeset_ty &Changes,
119 const changesetlist_ty &Sets,
120 const changeset_ty &Required) {
121 DDA.UpdatedSearchState(Changes, Sets, Required);
122 }
123
124 /// ExecuteOneTest - Execute a single test predicate on the change set \arg S.
125 bool ExecuteOneTest(const changeset_ty &S) {
126 // Check dependencies invariant.
127 DEBUG({
128 for (changeset_ty::const_iterator it = S.begin(),
129 ie = S.end(); it != ie; ++it)
130 for (succ_iterator_ty it2 = succ_begin(*it),
131 ie2 = succ_end(*it); it2 != ie2; ++it2)
132 assert(S.count(*it2) && "Attempt to run invalid changeset!");
133 });
134
135 return DDA.ExecuteOneTest(S);
136 }
137
138 public:
139 DAGDeltaAlgorithmImpl(DAGDeltaAlgorithm &_DDA,
140 const changeset_ty &_Changes,
141 const std::vector &_Dependencies);
142
143 changeset_ty Run();
144
145 /// GetTestResult - Get the test result for the active set \arg Changes with
146 /// \arg Required changes from the cache, executing the test if necessary.
147 ///
148 /// \param Changes - The set of active changes being minimized, which should
149 /// have their pred closure included in the test.
150 /// \param Required - The set of changes which have previously been
151 /// established to be required.
152 /// \return - The test result.
153 bool GetTestResult(const changeset_ty &Changes, const changeset_ty &Required);
154 };
155
156 /// Helper object for minimizing an active set of changes.
157 class DeltaActiveSetHelper : public DeltaAlgorithm {
158 DAGDeltaAlgorithmImpl &DDAI;
159
160 const changeset_ty &Required;
161
162 protected:
163 /// UpdatedSearchState - Callback used when the search state changes.
164 virtual void UpdatedSearchState(const changeset_ty &Changes,
165 const changesetlist_ty &Sets) {
166 DDAI.UpdatedSearchState(Changes, Sets, Required);
167 }
168
169 virtual bool ExecuteOneTest(const changeset_ty &S) {
170 return DDAI.GetTestResult(S, Required);
171 }
172
173 public:
174 DeltaActiveSetHelper(DAGDeltaAlgorithmImpl &_DDAI,
175 const changeset_ty &_Required)
176 : DDAI(_DDAI), Required(_Required) {}
177 };
178
179 }
180
181 DAGDeltaAlgorithmImpl::DAGDeltaAlgorithmImpl(DAGDeltaAlgorithm &_DDA,
182 const changeset_ty &_Changes,
183 const std::vector
184 &_Dependencies)
185 : DDA(_DDA),
186 Changes(_Changes),
187 Dependencies(_Dependencies)
188 {
189 for (changeset_ty::const_iterator it = Changes.begin(),
190 ie = Changes.end(); it != ie; ++it) {
191 Predecessors.insert(std::make_pair(*it, std::vector()));
192 Successors.insert(std::make_pair(*it, std::vector()));
193 }
194 for (std::vector::const_iterator it = Dependencies.begin(),
195 ie = Dependencies.end(); it != ie; ++it) {
196 Predecessors[it->second].push_back(it->first);
197 Successors[it->first].push_back(it->second);
198 }
199
200 // Compute the roots.
201 for (changeset_ty::const_iterator it = Changes.begin(),
202 ie = Changes.end(); it != ie; ++it)
203 if (succ_begin(*it) == succ_end(*it))
204 Roots.push_back(*it);
205
206 // Pre-compute the closure of the successor relation.
207 std::vector Worklist(Roots.begin(), Roots.end());
208 while (!Worklist.empty()) {
209 change_ty Change = Worklist.back();
210 Worklist.pop_back();
211
212 std::set &ChangeSuccs = SuccClosure[Change];
213 for (pred_iterator_ty it = pred_begin(Change),
214 ie = pred_end(Change); it != ie; ++it) {
215 SuccClosure[*it].insert(Change);
216 SuccClosure[*it].insert(ChangeSuccs.begin(), ChangeSuccs.end());
217 Worklist.push_back(*it);
218 }
219 }
220
221 // Invert to form the predecessor closure map.
222 for (changeset_ty::const_iterator it = Changes.begin(),
223 ie = Changes.end(); it != ie; ++it)
224 PredClosure.insert(std::make_pair(*it, std::set()));
225 for (changeset_ty::const_iterator it = Changes.begin(),
226 ie = Changes.end(); it != ie; ++it)
227 for (succ_closure_iterator_ty it2 = succ_closure_begin(*it),
228 ie2 = succ_closure_end(*it); it2 != ie2; ++it2)
229 PredClosure[*it2].insert(*it);
230
231 // Dump useful debug info.
232 DEBUG({
233 llvm::errs() << "-- DAGDeltaAlgorithmImpl --\n";
234 llvm::errs() << "Changes: [";
235 for (changeset_ty::const_iterator it = Changes.begin(),
236 ie = Changes.end(); it != ie; ++it) {
237 if (it != Changes.begin()) llvm::errs() << ", ";
238 llvm::errs() << *it;
239
240 if (succ_begin(*it) != succ_end(*it)) {
241 llvm::errs() << "(";
242 for (succ_iterator_ty it2 = succ_begin(*it),
243 ie2 = succ_end(*it); it2 != ie2; ++it2) {
244 if (it2 != succ_begin(*it)) llvm::errs() << ", ";
245 llvm::errs() << "->" << *it2;
246 }
247 llvm::errs() << ")";
248 }
249 }
250 llvm::errs() << "]\n";
251
252 llvm::errs() << "Roots: [";
253 for (std::vector::const_iterator it = Roots.begin(),
254 ie = Roots.end(); it != ie; ++it) {
255 if (it != Roots.begin()) llvm::errs() << ", ";
256 llvm::errs() << *it;
257 }
258 llvm::errs() << "]\n";
259
260 llvm::errs() << "Predecessor Closure:\n";
261 for (changeset_ty::const_iterator it = Changes.begin(),
262 ie = Changes.end(); it != ie; ++it) {
263 llvm::errs() << format(" %-4d: [", *it);
264 for (pred_closure_iterator_ty it2 = pred_closure_begin(*it),
265 ie2 = pred_closure_end(*it); it2 != ie2; ++it2) {
266 if (it2 != pred_closure_begin(*it)) llvm::errs() << ", ";
267 llvm::errs() << *it2;
268 }
269 llvm::errs() << "]\n";
270 }
271
272 llvm::errs() << "Successor Closure:\n";
273 for (changeset_ty::const_iterator it = Changes.begin(),
274 ie = Changes.end(); it != ie; ++it) {
275 llvm::errs() << format(" %-4d: [", *it);
276 for (succ_closure_iterator_ty it2 = succ_closure_begin(*it),
277 ie2 = succ_closure_end(*it); it2 != ie2; ++it2) {
278 if (it2 != succ_closure_begin(*it)) llvm::errs() << ", ";
279 llvm::errs() << *it2;
280 }
281 llvm::errs() << "]\n";
282 }
283
284 llvm::errs() << "\n\n";
285 });
286 }
287
288 bool DAGDeltaAlgorithmImpl::GetTestResult(const changeset_ty &Changes,
289 const changeset_ty &Required) {
290 changeset_ty Extended(Required);
291 Extended.insert(Changes.begin(), Changes.end());
292 for (changeset_ty::iterator it = Changes.begin(),
293 ie = Changes.end(); it != ie; ++it)
294 Extended.insert(pred_closure_begin(*it), pred_closure_end(*it));
295
296 if (FailedTestsCache.count(Extended))
297 return false;
298
299 bool Result = ExecuteOneTest(Extended);
300 if (!Result)
301 FailedTestsCache.insert(Extended);
302
303 return Result;
304 }
305
306 DAGDeltaAlgorithm::changeset_ty
307 DAGDeltaAlgorithmImpl::Run() {
308 // The current set of changes we are minimizing, starting at the roots.
309 changeset_ty CurrentSet(Roots.begin(), Roots.end());
310
311 // The set of required changes.
312 changeset_ty Required;
313
314 // Iterate until the active set of changes is empty. Convergence is guaranteed
315 // assuming input was a DAG.
316 //
317 // Invariant: CurrentSet intersect Required == {}
318 // Invariant: Required == (Required union succ*(Required))
319 while (!CurrentSet.empty()) {
320 DEBUG({
321 llvm::errs() << "DAG_DD - " << CurrentSet.size() << " active changes, "
322 << Required.size() << " required changes\n";
323 });
324
325 // Minimize the current set of changes.
326 DeltaActiveSetHelper Helper(*this, Required);
327 changeset_ty CurrentMinSet = Helper.Run(CurrentSet);
328
329 // Update the set of required changes. Since
330 // CurrentMinSet subset CurrentSet
331 // and after the last iteration,
332 // succ(CurrentSet) subset Required
333 // then
334 // succ(CurrentMinSet) subset Required
335 // and our invariant on Required is maintained.
336 Required.insert(CurrentMinSet.begin(), CurrentMinSet.end());
337
338 // Replace the current set with the predecssors of the minimized set of
339 // active changes.
340 CurrentSet.clear();
341 for (changeset_ty::const_iterator it = CurrentMinSet.begin(),
342 ie = CurrentMinSet.end(); it != ie; ++it)
343 CurrentSet.insert(pred_begin(*it), pred_end(*it));
344
345 // FIXME: We could enforce CurrentSet intersect Required == {} here if we
346 // wanted to protect against cyclic graphs.
347 }
348
349 return Required;
350 }
351
352 DAGDeltaAlgorithm::changeset_ty
353 DAGDeltaAlgorithm::Run(const changeset_ty &Changes,
354 const std::vector &Dependencies) {
355 return DAGDeltaAlgorithmImpl(*this, Changes, Dependencies).Run();
356 }
0 //===- llvm/unittest/ADT/DAGDeltaAlgorithmTest.cpp ------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "gtest/gtest.h"
10 #include "llvm/ADT/DAGDeltaAlgorithm.h"
11 #include
12 #include
13 using namespace llvm;
14
15 namespace std {
16
17 static std::ostream &operator<<(std::ostream &OS,
18 const std::set &S) {
19 OS << "{";
20 for (std::set::const_iterator it = S.begin(),
21 ie = S.end(); it != ie; ++it) {
22 if (it != S.begin())
23 OS << ",";
24 OS << *it;
25 }
26 OS << "}";
27 return OS;
28 }
29
30 }
31
32 namespace {
33
34 typedef DAGDeltaAlgorithm::edge_ty edge_ty;
35
36 class FixedDAGDeltaAlgorithm : public DAGDeltaAlgorithm {
37 changeset_ty FailingSet;
38 unsigned NumTests;
39
40 protected:
41 virtual bool ExecuteOneTest(const changeset_ty &Changes) {
42 ++NumTests;
43 return std::includes(Changes.begin(), Changes.end(),
44 FailingSet.begin(), FailingSet.end());
45 }
46
47 public:
48 FixedDAGDeltaAlgorithm(const changeset_ty &_FailingSet)
49 : FailingSet(_FailingSet),
50 NumTests(0) {}
51
52 unsigned getNumTests() const { return NumTests; }
53 };
54
55 std::set fixed_set(unsigned N, ...) {
56 std::set S;
57 va_list ap;
58 va_start(ap, N);
59 for (unsigned i = 0; i != N; ++i)
60 S.insert(va_arg(ap, unsigned));
61 va_end(ap);
62 return S;
63 }
64
65 std::set range(unsigned Start, unsigned End) {
66 std::set S;
67 while (Start != End)
68 S.insert(Start++);
69 return S;
70 }
71
72 std::set range(unsigned N) {
73 return range(0, N);
74 }
75
76 TEST(DAGDeltaAlgorithmTest, Basic) {
77 std::vector Deps;
78
79 // Dependencies:
80 // 1 - 3
81 Deps.clear();
82 Deps.push_back(std::make_pair(3, 1));
83
84 // P = {3,5,7} \in S,
85 // [0, 20),
86 // should minimize to {1,3,5,7} in a reasonable number of tests.
87 FixedDAGDeltaAlgorithm FDA(fixed_set(3, 3, 5, 7));
88 EXPECT_EQ(fixed_set(4, 1, 3, 5, 7), FDA.Run(range(20), Deps));
89 EXPECT_GE(46U, FDA.getNumTests());
90
91 // Dependencies:
92 // 0 - 1
93 // \- 2 - 3
94 // \- 4
95 Deps.clear();
96 Deps.push_back(std::make_pair(1, 0));
97 Deps.push_back(std::make_pair(2, 0));
98 Deps.push_back(std::make_pair(4, 0));
99 Deps.push_back(std::make_pair(3, 2));
100
101 // This is a case where we must hold required changes.
102 //
103 // P = {1,3} \in S,
104 // [0, 5),
105 // should minimize to {0,1,2,3} in a small number of tests.
106 FixedDAGDeltaAlgorithm FDA2(fixed_set(2, 1, 3));
107 EXPECT_EQ(fixed_set(4, 0, 1, 2, 3), FDA2.Run(range(5), Deps));
108 EXPECT_GE(9U, FDA2.getNumTests());
109
110 // This is a case where we should quickly prune part of the tree.
111 //
112 // P = {4} \in S,
113 // [0, 5),
114 // should minimize to {0,4} in a small number of tests.
115 FixedDAGDeltaAlgorithm FDA3(fixed_set(1, 4));
116 EXPECT_EQ(fixed_set(2, 0, 4), FDA3.Run(range(5), Deps));
117 EXPECT_GE(6U, FDA3.getNumTests());
118 }
119
120 }
121