CIRCT 20.0.0git
Loading...
Searching...
No Matches
LPSchedulers.cpp
Go to the documentation of this file.
1//===- LPSchedulers.cpp - Schedulers using external LP solvers ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implementation of linear programming-based schedulers using external solvers
10// via OR-Tools.
11//
12//===----------------------------------------------------------------------===//
13
15
16#include "mlir/IR/Operation.h"
17
18#include "ortools/linear_solver/linear_solver.h"
19
20using namespace circt;
21using namespace circt::scheduling;
22using namespace operations_research;
23
24LogicalResult scheduling::scheduleLP(Problem &prob, Operation *lastOp) {
25 Operation *containingOp = prob.getContainingOp();
26 if (!prob.hasOperation(lastOp))
27 return containingOp->emitError("problem does not include last operation");
28
29 MPSolver::OptimizationProblemType problemType;
30 if (!MPSolver::ParseSolverType("GLOP_LINEAR_PROGRAMMING", &problemType) ||
31 !MPSolver::SupportsProblemType(problemType))
32 return containingOp->emitError("GLOP is unvailable");
33
34 MPSolver solver("Problem", problemType);
35 double infinity = solver.infinity();
36
37 // Create start time variables.
38 DenseMap<Operation *, MPVariable *> vars;
39 unsigned i = 0;
40 for (auto *op : prob.getOperations()) {
41 vars[op] = solver.MakeNumVar(0, infinity, (Twine("t_") + Twine(i)).str());
42 ++i;
43 }
44
45 // The objective is to minimize the start time of the last operation.
46 MPObjective *objective = solver.MutableObjective();
47 objective->SetCoefficient(vars[lastOp], 1);
48 objective->SetMinimization();
49
50 // Construct a linear constraint for each dependence.
51 for (auto *op : prob.getOperations())
52 for (auto dep : prob.getDependences(op)) {
53 Operation *src = dep.getSource();
54 Operation *dst = dep.getDestination();
55 if (src == dst)
56 return containingOp->emitError() << "dependence cycle detected";
57
58 // t_src + t.linkedOperatorType.latency <= t_dst
59 // <=> 1 * t_src + -1 * t_dst <= -latency
60 unsigned latency = *prob.getLatency(*prob.getLinkedOperatorType(src));
61 MPConstraint *constraint =
62 solver.MakeRowConstraint(-infinity, -((double)latency));
63 constraint->SetCoefficient(vars[src], 1);
64 constraint->SetCoefficient(vars[dst], -1);
65 }
66
67 // Invoke solver. The LP is infeasible if the scheduling problem contained
68 // dependence cycles. Otherwise, we expect the result to be optimal.
69 MPSolver::ResultStatus result = solver.Solve();
70 if (result == MPSolver::INFEASIBLE)
71 return containingOp->emitError() << "dependence cycle detected";
72 assert(result == MPSolver::OPTIMAL);
73
74 // Retrieve start times.
75 for (auto *op : prob.getOperations())
76 prob.setStartTime(op, std::round(vars[op]->solution_value()));
77
78 return success();
79}
80
81LogicalResult scheduling::scheduleLP(CyclicProblem &prob, Operation *lastOp) {
82 Operation *containingOp = prob.getContainingOp();
83 if (!prob.hasOperation(lastOp))
84 return containingOp->emitError("problem does not include last operation");
85
86 MPSolver::OptimizationProblemType probType;
87 if (!MPSolver::ParseSolverType("CBC_MIXED_INTEGER_PROGRAMMING", &probType) ||
88 !MPSolver::SupportsProblemType(probType))
89 return containingOp->emitError("Cbc is unavailable");
90
91 MPSolver solver("CyclicProblem", probType);
92 double infinity = solver.infinity();
93
94 // Create II variable.
95 MPVariable *ii = solver.MakeIntVar(1, infinity, "II");
96
97 // Create start time variables (and collect latencies to compute upper bound
98 // for the latest start time).
99 DenseMap<Operation *, MPVariable *> t;
100 unsigned upperBound = 0;
101 unsigned i = 0;
102 for (auto *op : prob.getOperations()) {
103 t[op] = solver.MakeIntVar(0, infinity, (Twine("t_") + Twine(i)).str());
104 upperBound += *prob.getLatency(*prob.getLinkedOperatorType(op));
105 ++i;
106 }
107
108 // The objective is to minimize the II as well as the start time of the last
109 // operation. We use a weighted sum to encode both objectives in a single
110 // linear expression.
111 MPObjective *objective = solver.MutableObjective();
112 objective->SetCoefficient(ii, upperBound);
113 objective->SetCoefficient(t[lastOp], 1);
114 objective->SetMinimization();
115
116 // Construct a linear constraint for each dependence.
117 for (auto *op : prob.getOperations())
118 for (auto dep : prob.getDependences(op)) {
119 Operation *src = dep.getSource();
120 Operation *dst = dep.getDestination();
121
122 // t_src + t_src.linkedOperatorType.latency <= t_dst + e.distance * II
123 // <=> 1 * t_src + -1 * t_dst + -e.distance * II <= -latency
124 unsigned latency = *prob.getLatency(*prob.getLinkedOperatorType(src));
125 MPConstraint *constraint =
126 solver.MakeRowConstraint(-infinity, -((double)latency));
127 if (src != dst) { // Handle self-arcs.
128 constraint->SetCoefficient(t[src], 1);
129 constraint->SetCoefficient(t[dst], -1);
130 }
131 constraint->SetCoefficient(ii,
132 -((double)prob.getDistance(dep).value_or(0)));
133 }
134
135 // Invoke solver. The ILP is infeasible if the scheduling problem contained
136 // dependence graph contains cycles that do not include at least one edge with
137 // a non-zero distance. Otherwise, we expect the result to be optimal.
138 MPSolver::ResultStatus result = solver.Solve();
139 if (result == MPSolver::INFEASIBLE)
140 return containingOp->emitError() << "dependence cycle detected";
141 assert(result == MPSolver::OPTIMAL);
142
143 // Retrieve II and start times.
144 prob.setInitiationInterval(std::round(ii->solution_value()));
145 for (auto *op : prob.getOperations())
146 prob.setStartTime(op, std::round(t[op]->solution_value()));
147
148 return success();
149}
assert(baseType &&"element must be base type")
This class models a cyclic scheduling problem.
Definition Problems.h:286
std::optional< unsigned > getDistance(Dependence dep)
The distance determines whether a dependence has to be satisfied in the same iteration (distance=0 or...
Definition Problems.h:301
void setInitiationInterval(unsigned val)
Definition Problems.h:312
This class models the most basic scheduling problem.
Definition Problems.h:75
std::optional< unsigned > getLatency(OperatorType opr)
The latency is the number of cycles opr needs to compute its result.
Definition Problems.h:204
bool hasOperation(Operation *op)
Return true if op is part of this problem.
Definition Problems.h:170
std::optional< OperatorType > getLinkedOperatorType(Operation *op)
The linked operator type provides the runtime characteristics for op.
Definition Problems.h:196
void setStartTime(Operation *op, unsigned val)
Definition Problems.h:215
const OperationSet & getOperations()
Return the set of operations.
Definition Problems.h:172
DependenceRange getDependences(Operation *op)
Return a range object to transparently iterate over op's incoming 1) implicit def-use dependences (ba...
Definition Problems.cpp:53
Operation * getContainingOp()
Return the operation containing this problem, e.g. to emit diagnostics.
Definition Problems.h:165
LogicalResult scheduleLP(Problem &prob, Operation *lastOp)
Solve the basic problem using linear programming and an external LP solver.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.