Skip to content

Commit

Permalink
Merge pull request #2260 from julienhenry/delta-max-bound-sips
Browse files Browse the repository at this point in the history
delta-max-bound sips heuristic
  • Loading branch information
b-scholz committed Apr 14, 2022
2 parents 8fbd7c6 + 8beb0cb commit 40ef962
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 66 deletions.
120 changes: 87 additions & 33 deletions src/ast/utility/SipsMetric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@

namespace souffle::ast {

SipsMetric::SipsMetric(const TranslationUnit& tu) : program(tu.getProgram()) {
sccGraph = &tu.getAnalysis<ast::analysis::SCCGraphAnalysis>();
}

std::vector<std::size_t> StaticSipsMetric::getReordering(
const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const {
(void)version;
(void)mode;
std::size_t relStratum = sccGraph->getSCC(program.getRelation(*clause));
auto sccRelations = sccGraph->getInternalRelations(relStratum);

auto sccAtoms = filter(ast::getBodyLiterals<ast::Atom>(*clause),
[&](auto* atom) { return contains(sccRelations, program.getRelation(*atom)); });

BindingStore bindingStore(clause);
auto atoms = getBodyLiterals<Atom>(*clause);
Expand All @@ -52,7 +59,7 @@ std::vector<std::size_t> StaticSipsMetric::getReordering(
std::size_t numAdded = 0;
while (numAdded < atoms.size()) {
// grab the index of the next atom, based on the SIPS function
const auto& costs = evaluateCosts(atoms, bindingStore);
const auto& costs = evaluateCosts(clause, sccAtoms, atoms, bindingStore, version, mode);
assert(atoms.size() == costs.size() && "each atom should have exactly one cost");
std::size_t minIdx = static_cast<std::size_t>(
std::distance(costs.begin(), std::min_element(costs.begin(), costs.end())));
Expand All @@ -74,11 +81,9 @@ std::vector<std::size_t> StaticSipsMetric::getReordering(
return newOrder;
}

SelingerProfileSipsMetric::SelingerProfileSipsMetric(const TranslationUnit& tu) {
SelingerProfileSipsMetric::SelingerProfileSipsMetric(const TranslationUnit& tu) : SipsMetric(tu) {
profileUseAnalysis = &tu.getAnalysis<ast::analysis::ProfileUseAnalysis>();
polyAnalysis = &tu.getAnalysis<ast::analysis::PolymorphicObjectsAnalysis>();
sccGraph = &tu.getAnalysis<ast::analysis::SCCGraphAnalysis>();
program = &tu.getProgram();
}

std::vector<std::size_t> SelingerProfileSipsMetric::getReordering(
Expand All @@ -94,10 +99,10 @@ std::vector<std::size_t> SelingerProfileSipsMetric::getReordering(
}

auto constraints = ast::getBodyLiterals<ast::BinaryConstraint>(*clause);
std::size_t relStratum = sccGraph->getSCC(program->getRelation(*clause));
std::size_t relStratum = sccGraph->getSCC(program.getRelation(*clause));
auto sccRelations = sccGraph->getInternalRelations(relStratum);
auto sccAtoms = filter(ast::getBodyLiterals<ast::Atom>(*clause),
[&](auto* atom) { return contains(sccRelations, program->getRelation(*atom)); });
[&](auto* atom) { return contains(sccRelations, program.getRelation(*atom)); });

assert(profileUseAnalysis->hasAutoSchedulerStats() && "Must have stats in order to auto-schedule!");

Expand Down Expand Up @@ -458,7 +463,7 @@ Own<ram::Expression> SelingerProfileSipsMetric::translateConstant(const ast::Con
fatal("unaccounted-for constant");
}

std::string SelingerProfileSipsMetric::getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom,
std::string SipsMetric::getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom,
const std::vector<ast::Atom*>& sccAtoms, std::size_t version, ast2ram::TranslationMode mode) const {
using namespace souffle::ast2ram;

Expand Down Expand Up @@ -550,28 +555,32 @@ std::unique_ptr<SipsMetric> SipsMetric::create(const std::string& heuristic, con
if (Global::config().has("auto-schedule")) {
return mk<SelingerProfileSipsMetric>(tu);
} else if (heuristic == "strict")
return mk<StrictSips>();
return mk<StrictSips>(tu);
else if (heuristic == "all-bound")
return mk<AllBoundSips>();
return mk<AllBoundSips>(tu);
else if (heuristic == "naive")
return mk<NaiveSips>();
return mk<NaiveSips>(tu);
else if (heuristic == "max-bound")
return mk<MaxBoundSips>();
return mk<MaxBoundSips>(tu);
else if (heuristic == "delta-max-bound")
return mk<DeltaMaxBoundSips>(tu);
else if (heuristic == "max-ratio")
return mk<MaxRatioSips>();
return mk<MaxRatioSips>(tu);
else if (heuristic == "least-free")
return mk<LeastFreeSips>();
return mk<LeastFreeSips>(tu);
else if (heuristic == "least-free-vars")
return mk<LeastFreeVarsSips>();
return mk<LeastFreeVarsSips>(tu);
else if (heuristic == "input")
return mk<InputSips>(tu.getProgram(), tu.getAnalysis<analysis::IOTypeAnalysis>());
return mk<InputSips>(tu);

// default is all-bound
return create("all-bound", tu);
}

std::vector<double> StrictSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& /* bindingStore */) const {
std::vector<double> StrictSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& /* bindingStore */, std::size_t /*version*/,
ast2ram::TranslationMode /*mode*/) const {
// Goal: Always choose the left-most atom
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -581,8 +590,9 @@ std::vector<double> StrictSips::evaluateCosts(
return cost;
}

std::vector<double> AllBoundSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> AllBoundSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: Prioritise atoms with all arguments bound
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -599,8 +609,9 @@ std::vector<double> AllBoundSips::evaluateCosts(
return cost;
}

std::vector<double> NaiveSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> NaiveSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: Prioritise (1) all bound, then (2) atoms with at least one bound argument, then (3) left-most
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -623,8 +634,9 @@ std::vector<double> NaiveSips::evaluateCosts(
return cost;
}

std::vector<double> MaxBoundSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> MaxBoundSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: prioritise (1) all-bound, then (2) max number of bound vars, then (3) left-most
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -650,8 +662,9 @@ std::vector<double> MaxBoundSips::evaluateCosts(
return cost;
}

std::vector<double> MaxRatioSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> MaxRatioSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: prioritise max ratio of bound args
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -677,8 +690,9 @@ std::vector<double> MaxRatioSips::evaluateCosts(
return cost;
}

std::vector<double> LeastFreeSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> LeastFreeSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: choose the atom with the least number of unbound arguments
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -692,8 +706,9 @@ std::vector<double> LeastFreeSips::evaluateCosts(
return cost;
}

std::vector<double> LeastFreeVarsSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
std::vector<double> LeastFreeVarsSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: choose the atom with the least amount of unbound variables
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -714,8 +729,12 @@ std::vector<double> LeastFreeVarsSips::evaluateCosts(
return cost;
}

std::vector<double> InputSips::evaluateCosts(
const std::vector<Atom*> atoms, const BindingStore& bindingStore) const {
InputSips::InputSips(const TranslationUnit& tu)
: StaticSipsMetric(tu), ioTypes(tu.getAnalysis<analysis::IOTypeAnalysis>()) {}

std::vector<double> InputSips::evaluateCosts(const Clause* /*clause*/,
const std::vector<ast::Atom*>& /*sccAtoms*/, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const {
// Goal: prioritise (1) all-bound, (2) input, then (3) rest
std::vector<double> cost;
for (const auto* atom : atoms) {
Expand All @@ -740,4 +759,39 @@ std::vector<double> InputSips::evaluateCosts(
return cost;
}

std::vector<double> DeltaMaxBoundSips::evaluateCosts(const Clause* clause,
const std::vector<ast::Atom*>& sccAtoms, const std::vector<Atom*> atoms,
const BindingStore& bindingStore, std::size_t version, ast2ram::TranslationMode mode) const {
auto isDeltaRelation = [&](const Atom* atom) {
std::string name = getClauseAtomName(*clause, atom, sccAtoms, version, mode);
return isPrefix("@delta_", name);
};

std::vector<double> cost;
for (const auto* atom : atoms) {
if (atom == nullptr) {
cost.push_back(std::numeric_limits<double>::max());
continue;
}

std::size_t arity = atom->getArity();
std::size_t numBound = bindingStore.numBoundArguments(atom);
if (arity == numBound) {
// Always better than anything else
cost.push_back(0.0);
} else if (isDeltaRelation(atom)) {
// Better than any other atom that is not fully bounded
cost.push_back(1.0);
} else if (numBound == 0) {
// Always worse than any number of bound vars
cost.push_back(4.0);
} else {
// Between 2 and 3, decreasing with more num bound
cost.push_back(2.0 + (1.0 / (double)numBound));
}
}
assert(atoms.size() == cost.size() && "each atom should have exactly one cost");
return cost;
}

} // namespace souffle::ast
Loading

0 comments on commit 40ef962

Please sign in to comment.