Skip to content

[SharedResourcesProblem] [Simplex Scheduler] Simplex scheduler deals with multiple resource constraints #8480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/circt/Dialect/SSP/SSPAttributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ in {
}
}

// SharedOperatorsProblem
// SharedResourcesProblem
def LimitProp : ResourceTypeProperty<SSPDialect,
"Limit", "unsigned", "::circt::scheduling::SharedOperatorsProblem"> {
"Limit", "unsigned", "::circt::scheduling::SharedResourcesProblem"> {
let mnemonic = "limit";
}
6 changes: 3 additions & 3 deletions include/circt/Dialect/SSP/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ struct Default<scheduling::ChainingProblem> {
};

template <>
struct Default<scheduling::SharedOperatorsProblem> {
struct Default<scheduling::SharedResourcesProblem> {
static constexpr auto operationProperties =
Default<scheduling::Problem>::operationProperties;
static constexpr auto operatorTypeProperties =
Expand All @@ -647,9 +647,9 @@ struct Default<scheduling::ModuloProblem> {
static constexpr auto operationProperties =
Default<scheduling::Problem>::operationProperties;
static constexpr auto operatorTypeProperties =
Default<scheduling::SharedOperatorsProblem>::operatorTypeProperties;
Default<scheduling::SharedResourcesProblem>::operatorTypeProperties;
static constexpr auto resourceTypeProperties =
Default<scheduling::SharedOperatorsProblem>::resourceTypeProperties;
Default<scheduling::SharedResourcesProblem>::resourceTypeProperties;
static constexpr auto dependenceProperties =
Default<scheduling::CyclicProblem>::dependenceProperties;
static constexpr auto instanceProperties =
Expand Down
8 changes: 4 additions & 4 deletions include/circt/Scheduling/Algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ LogicalResult scheduleSimplex(Problem &prob, Operation *lastOp);
/// \p prob does not include \p lastOp.
LogicalResult scheduleSimplex(CyclicProblem &prob, Operation *lastOp);

/// Solve the acyclic problem with shared operators using a linear
/// Solve the acyclic problem with shared resources using a linear
/// programming-based heuristic. The approach tries to minimize the start time
/// of the given \p lastOp, but optimality is not guaranteed. Fails if the
/// dependence graph contains cycles, or \p prob does not include \p lastOp.
LogicalResult scheduleSimplex(SharedOperatorsProblem &prob, Operation *lastOp);
LogicalResult scheduleSimplex(SharedResourcesProblem &prob, Operation *lastOp);

/// Solve the modulo scheduling problem using a linear programming-based
/// heuristic. The approach tries to determine the smallest feasible initiation
Expand Down Expand Up @@ -87,11 +87,11 @@ LogicalResult scheduleLP(Problem &prob, Operation *lastOp);
/// \p lastOp.
LogicalResult scheduleLP(CyclicProblem &prob, Operation *lastOp);

/// Solve the acyclic problem with shared operators using constraint programming
/// Solve the acyclic problem with shared resources using constraint programming
/// and an external SAT solver. The objective is to minimize the start time of
/// the given \p lastOp. Fails if the dependence graph contains cycles, or \p
/// prob does not include \p lastOp.
LogicalResult scheduleCPSAT(SharedOperatorsProblem &prob, Operation *lastOp);
LogicalResult scheduleCPSAT(SharedResourcesProblem &prob, Operation *lastOp);

} // namespace scheduling
} // namespace circt
Expand Down
12 changes: 6 additions & 6 deletions include/circt/Scheduling/Problems.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,16 +480,16 @@ class ChainingProblem : public virtual Problem {
/// accept new operands (coming from a distinct operation) in each time step.
///
/// A solution to this problem is feasible iff the number of operations that use
/// a certain limited operator type, and start in the same time step, does not
/// exceed the operator type's limit. These constraints do not apply to operator
/// a certain limited resource type, and start in the same time step, does not
/// exceed the resource type's limit. These constraints do not apply to resource
/// types without a limit (not set, or 0).
class SharedOperatorsProblem : public virtual Problem {
class SharedResourcesProblem : public virtual Problem {
public:
static constexpr auto name = "SharedOperatorsProblem";
static constexpr auto name = "SharedResourcesProblem";
using Problem::Problem;

protected:
SharedOperatorsProblem() = default;
SharedResourcesProblem() = default;

private:
ResourceTypeProperty<unsigned> limit;
Expand Down Expand Up @@ -526,7 +526,7 @@ class SharedOperatorsProblem : public virtual Problem {
/// and start in the same congruence class (= start time *mod* II), does
/// not exceed the operator type's limit.
class ModuloProblem : public virtual CyclicProblem,
public virtual SharedOperatorsProblem {
public virtual SharedResourcesProblem {
public:
static constexpr auto name = "ModuloProblem";
using CyclicProblem::CyclicProblem;
Expand Down
4 changes: 2 additions & 2 deletions lib/Dialect/SSP/Transforms/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ void PrintPass::runOnOperation() {
printInstance<CyclicProblem>(instOp, os);
else if (probName == "ChainingProblem")
printInstance<ChainingProblem>(instOp, os);
else if (probName == "SharedOperatorsProblem")
printInstance<SharedOperatorsProblem>(instOp, os);
else if (probName == "SharedResourcesProblem")
printInstance<SharedResourcesProblem>(instOp, os);
else if (probName == "ModuloProblem")
printInstance<ModuloProblem>(instOp, os);
else if (probName == "ChainingCyclicProblem")
Expand Down
4 changes: 2 additions & 2 deletions lib/Dialect/SSP/Transforms/Roundtrip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ static InstanceOp roundtrip(InstanceOp instOp, bool check, bool verify,
return roundtripAs<Problem>(instOp, check, verify, builder);
if (problemName == "CyclicProblem")
return roundtripAs<CyclicProblem>(instOp, check, verify, builder);
if (problemName == "SharedOperatorsProblem")
return roundtripAs<SharedOperatorsProblem>(instOp, check, verify, builder);
if (problemName == "SharedResourcesProblem")
return roundtripAs<SharedResourcesProblem>(instOp, check, verify, builder);
if (problemName == "ModuloProblem")
return roundtripAs<ModuloProblem>(instOp, check, verify, builder);
if (problemName == "ChainingProblem")
Expand Down
8 changes: 4 additions & 4 deletions lib/Dialect/SSP/Transforms/Schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ static InstanceOp scheduleWithSimplex(InstanceOp instOp, StringRef options,
return scheduleProblemTWithSimplex<Problem>(instOp, lastOp, builder);
if (problemName == "CyclicProblem")
return scheduleProblemTWithSimplex<CyclicProblem>(instOp, lastOp, builder);
if (problemName == "SharedOperatorsProblem")
return scheduleProblemTWithSimplex<SharedOperatorsProblem>(instOp, lastOp,
if (problemName == "SharedResourcesProblem")
return scheduleProblemTWithSimplex<SharedResourcesProblem>(instOp, lastOp,
builder);
if (problemName == "ModuloProblem")
return scheduleProblemTWithSimplex<ModuloProblem>(instOp, lastOp, builder);
Expand Down Expand Up @@ -219,13 +219,13 @@ static InstanceOp scheduleWithCPSAT(InstanceOp instOp, StringRef options,
}

auto problemName = instOp.getProblemName();
if (problemName != "SharedOperatorsProblem") {
if (problemName != "SharedResourcesProblem") {
llvm::errs() << "ssp-schedule: Unsupported problem '" << problemName
<< "' for CPSAT scheduler\n";
return {};
}

auto prob = loadProblem<SharedOperatorsProblem>(instOp);
auto prob = loadProblem<SharedResourcesProblem>(instOp);
if (failed(prob.check()) || failed(scheduling::scheduleCPSAT(prob, lastOp)) ||
failed(prob.verify()))
return {};
Expand Down
4 changes: 2 additions & 2 deletions lib/Scheduling/CPSATSchedulers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ using llvm::format;
/// https://github.com/google/or-tools/blob/stable/examples/python/rcpsp_sat.py
/// but a gentler introduction (though with differing formulation) is
/// https://python-mip.readthedocs.io/en/latest/examples.html
LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
LogicalResult scheduling::scheduleCPSAT(SharedResourcesProblem &prob,
Operation *lastOp) {
Operation *containingOp = prob.getContainingOp();
if (!prob.hasOperation(lastOp))
Expand Down Expand Up @@ -123,7 +123,7 @@ LogicalResult scheduling::scheduleCPSAT(SharedOperatorsProblem &prob,
(Twine("demand_") + Twine(i) + Twine("_") +
Twine(resource.getAttr().strref()))
.str());
// Conventional formulation for SharedOperatorsProblem;
// Conventional formulation for SharedResourcesProblem;
// interval during which the resource is occupied has size 1.
IntervalVar start =
cpModel.NewFixedSizeIntervalVar(taskInterval.StartExpr(), 1);
Expand Down
12 changes: 6 additions & 6 deletions lib/Scheduling/Problems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,18 +330,18 @@ LogicalResult ChainingProblem::verify() {
}

//===----------------------------------------------------------------------===//
// SharedOperatorsProblem
// SharedResourcesProblem
//===----------------------------------------------------------------------===//

Problem::PropertyStringVector
SharedOperatorsProblem::getProperties(ResourceType rsrc) {
SharedResourcesProblem::getProperties(ResourceType rsrc) {
auto psv = Problem::getProperties(rsrc);
if (auto limit = getLimit(rsrc))
psv.emplace_back("limit", std::to_string(*limit));
return psv;
}

LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) {
LogicalResult SharedResourcesProblem::checkLatency(Operation *op) {
if (failed(Problem::checkLatency(op)))
return failure();

Expand All @@ -364,7 +364,7 @@ LogicalResult SharedOperatorsProblem::checkLatency(Operation *op) {
return success();
}

LogicalResult SharedOperatorsProblem::verifyUtilization(ResourceType rsrc) {
LogicalResult SharedResourcesProblem::verifyUtilization(ResourceType rsrc) {
auto limit = getLimit(rsrc);
if (!limit)
return success();
Expand Down Expand Up @@ -393,7 +393,7 @@ LogicalResult SharedOperatorsProblem::verifyUtilization(ResourceType rsrc) {
return success();
}

LogicalResult SharedOperatorsProblem::verify() {
LogicalResult SharedResourcesProblem::verify() {
if (failed(Problem::verify()))
return failure();

Expand Down Expand Up @@ -442,7 +442,7 @@ LogicalResult ModuloProblem::verify() {
if (failed(CyclicProblem::verify()))
return failure();

// Don't call SharedOperatorsProblem::verify() here to prevent redundant
// Don't call SharedResourcesProblem::verify() here to prevent redundant
// verification of the base problem.
for (auto rsrc : getResourceTypes())
if (failed(verifyUtilization(rsrc)))
Expand Down
39 changes: 24 additions & 15 deletions lib/Scheduling/SimplexSchedulers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,17 +218,17 @@ class CyclicSimplexScheduler : public SimplexSchedulerBase {
LogicalResult schedule() override;
};

// This class solves acyclic, resource-constrained `SharedOperatorsProblem` with
// This class solves acyclic, resource-constrained `SharedResourcesProblem` with
// a simplified version of the iterative heuristic presented in [2].
class SharedOperatorsSimplexScheduler : public SimplexSchedulerBase {
class SharedResourcesSimplexScheduler : public SimplexSchedulerBase {
private:
SharedOperatorsProblem &prob;
SharedResourcesProblem &prob;

protected:
Problem &getProblem() override { return prob; }

public:
SharedOperatorsSimplexScheduler(SharedOperatorsProblem &prob,
SharedResourcesSimplexScheduler(SharedResourcesProblem &prob,
Operation *lastOp)
: SimplexSchedulerBase(lastOp), prob(prob) {}
LogicalResult schedule() override;
Expand Down Expand Up @@ -870,10 +870,10 @@ LogicalResult CyclicSimplexScheduler::schedule() {
}

//===----------------------------------------------------------------------===//
// SharedOperatorsSimplexScheduler
// SharedResourcesSimplexScheduler
//===----------------------------------------------------------------------===//

static bool isLimited(Operation *op, SharedOperatorsProblem &prob) {
static bool isLimited(Operation *op, SharedResourcesProblem &prob) {
auto maybeRsrcs = prob.getLinkedResourceTypes(op);
if (!maybeRsrcs)
return false;
Expand All @@ -882,7 +882,7 @@ static bool isLimited(Operation *op, SharedOperatorsProblem &prob) {
});
}

LogicalResult SharedOperatorsSimplexScheduler::schedule() {
LogicalResult SharedResourcesSimplexScheduler::schedule() {
if (failed(checkLastOp()))
return failure();

Expand Down Expand Up @@ -938,18 +938,26 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() {
assert(maybeRsrcs && "Limited operation must have linked resource types");

auto &rsrcs = *maybeRsrcs;
assert(rsrcs.size() == 1 &&
"Multi-resource operations are not yet supported by this scheduler");
for (auto rsrc : rsrcs) {
assert(prob.getLimit(rsrc).value_or(0) > 0 &&
"All resources must have non-zero limits");
}

auto rsrc = rsrcs[0];
unsigned limit = prob.getLimit(rsrc).value_or(0);
assert(limit > 0);

Comment on lines 946 to 949
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this dead code?

Suggested change
auto rsrc = rsrcs[0];
unsigned limit = prob.getLimit(rsrc).value_or(0);
assert(limit > 0);

// Find the first time step (beginning at the current start time in the
// partial schedule) in which an operator instance is available.
// partial schedule) in which all required resources are available for the
// operation.
unsigned startTimeVar = startTimeVariables[op];
unsigned candTime = getStartTime(startTimeVar);
while (reservationTable[rsrc].lookup(candTime) == limit)

while (std::any_of(rsrcs.begin(), rsrcs.end(),
[&](Problem::ResourceType rsrc) {
return reservationTable[rsrc].lookup(candTime) ==
prob.getLimit(rsrc).value_or(0);
}))
++candTime;

// Fix the start time. As explained above, this cannot make the problem
Expand All @@ -958,8 +966,9 @@ LogicalResult SharedOperatorsSimplexScheduler::schedule() {
assert(succeeded(fixed));
(void)fixed;

// Record the operator use.
++reservationTable[rsrc][candTime];
// Record the resource uses.
for (auto rsrc : rsrcs)
++reservationTable[rsrc][candTime];

LLVM_DEBUG(dbgs() << "After scheduling " << startTimeVar
<< " to t=" << candTime << ":\n";
Expand Down Expand Up @@ -1375,9 +1384,9 @@ LogicalResult scheduling::scheduleSimplex(CyclicProblem &prob,
return simplex.schedule();
}

LogicalResult scheduling::scheduleSimplex(SharedOperatorsProblem &prob,
LogicalResult scheduling::scheduleSimplex(SharedResourcesProblem &prob,
Operation *lastOp) {
SharedOperatorsSimplexScheduler simplex(prob, lastOp);
SharedResourcesSimplexScheduler simplex(prob, lastOp);
return simplex.schedule();
}

Expand Down
4 changes: 2 additions & 2 deletions test/Dialect/SSP/roundtrip.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
}
}

// CHECK: ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
// CHECK: ssp.instance @multiple_oprs of "SharedResourcesProblem" {
// CHECK: library {
// CHECK: operator_type @slowAdd [latency<3>]
// CHECK: operator_type @fastAdd [latency<1>]
Expand All @@ -185,7 +185,7 @@ ssp.instance @mco_outgoing_delays of "ChainingProblem" {
// CHECK: operation<@_1>() [t<10>]
// CHECK: }
// CHECK: }
ssp.instance @multiple_oprs of "SharedOperatorsProblem" {
ssp.instance @multiple_oprs of "SharedResourcesProblem" {
library {
operator_type @slowAdd [latency<3>]
operator_type @fastAdd [latency<1>]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: circt-opt %s -ssp-roundtrip=verify -verify-diagnostics -split-input-file

// expected-error@+1 {{Operator type 'limited' using limited resource 'limited_rsrc' has zero latency.}}
ssp.instance @limited_but_zero_latency of "SharedOperatorsProblem" {
ssp.instance @limited_but_zero_latency of "SharedResourcesProblem" {
library {
operator_type @limited [latency<0>]
}
Expand All @@ -16,7 +16,7 @@ ssp.instance @limited_but_zero_latency of "SharedOperatorsProblem" {
// -----

// expected-error@+1 {{Resource type 'limited_rsrc' is oversubscribed}}
ssp.instance @oversubscribed of "SharedOperatorsProblem" {
ssp.instance @oversubscribed of "SharedResourcesProblem" {
library {
operator_type @limited [latency<1>]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// RUN: %if or-tools %{ circt-opt %s -ssp-schedule=scheduler=cpsat | FileCheck %s -check-prefixes=CHECK,CPSAT %}

// CHECK-LABEL: full_load
ssp.instance @full_load of "SharedOperatorsProblem" {
ssp.instance @full_load of "SharedResourcesProblem" {
library {
operator_type @L1_3 [latency<3>]
operator_type @_1 [latency<1>]
Expand All @@ -25,7 +25,7 @@ ssp.instance @full_load of "SharedOperatorsProblem" {
}

// CHECK-LABEL: partial_load
ssp.instance @partial_load of "SharedOperatorsProblem" {
ssp.instance @partial_load of "SharedResourcesProblem" {
library {
operator_type @L3_3 [latency<3>]
operator_type @_1 [latency<1>]
Expand All @@ -47,7 +47,7 @@ ssp.instance @partial_load of "SharedOperatorsProblem" {
}

// CHECK-LABEL: multiple
ssp.instance @multiple of "SharedOperatorsProblem" {
ssp.instance @multiple of "SharedResourcesProblem" {
library {
operator_type @L3_2 [latency<3>]
operator_type @L1_1 [latency<1>]
Expand All @@ -69,3 +69,26 @@ ssp.instance @multiple of "SharedOperatorsProblem" {
operation<@_1> @last(%5) [t<11>]
}
}

// CHECK-LABEL: if_else
ssp.instance @if_else_exclusive of "SharedResourcesProblem" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a couple more tests for multi-resource operations that contain potential resource conflicts. I'd like to see:

  • Two ops competing for a set of common resources
  • Limits > 1

library {
operator_type @ThenOperator [latency<2>]
operator_type @ElseOperator [latency<3>]
operator_type @_1 [latency<2>]
}
resource {
resource_type @adder_then_br [limit<1>]
resource_type @mem_port_then_br [limit<1>]
resource_type @adder_else_br [limit<1>]
resource_type @mem_port_else_br [limit<1>]
}
graph {
%0 = operation<@ThenOperator> @compute_then_br() uses[@adder_then_br, @mem_port_then_br] [t<0>]
%1 = operation<@ElseOperator> @compute_else_br() uses[@adder_else_br, @mem_port_else_br] [t<0>]
%2 = operation<@_1>(%0, %1) [t<4>]
// SIMPLEX: @last(%{{.*}}) [t<5>]
// CPSAT: @last(%{{.*}}) [t<5>]
operation<@_1> @last(%2) [t<7>]
}
}