Skip to content
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

delta-max-bound sips heuristic #2260

Merged
merged 1 commit into from
Apr 14, 2022
Merged
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
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