From 8beb0cbe3e6051edd7cfdbeff32d91f01e9d0f46 Mon Sep 17 00:00:00 2001 From: Julien Henry Date: Tue, 12 Apr 2022 10:56:52 -0400 Subject: [PATCH] delta-max-bound sips heuristic --- src/ast/utility/SipsMetric.cpp | 120 ++++++++++++++++++++++++--------- src/ast/utility/SipsMetric.h | 92 ++++++++++++++++--------- 2 files changed, 146 insertions(+), 66 deletions(-) diff --git a/src/ast/utility/SipsMetric.cpp b/src/ast/utility/SipsMetric.cpp index 5a6efdeac3e..5f98a042d5f 100644 --- a/src/ast/utility/SipsMetric.cpp +++ b/src/ast/utility/SipsMetric.cpp @@ -40,10 +40,17 @@ namespace souffle::ast { +SipsMetric::SipsMetric(const TranslationUnit& tu) : program(tu.getProgram()) { + sccGraph = &tu.getAnalysis(); +} + std::vector 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(*clause), + [&](auto* atom) { return contains(sccRelations, program.getRelation(*atom)); }); BindingStore bindingStore(clause); auto atoms = getBodyLiterals(*clause); @@ -52,7 +59,7 @@ std::vector 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::distance(costs.begin(), std::min_element(costs.begin(), costs.end()))); @@ -74,11 +81,9 @@ std::vector StaticSipsMetric::getReordering( return newOrder; } -SelingerProfileSipsMetric::SelingerProfileSipsMetric(const TranslationUnit& tu) { +SelingerProfileSipsMetric::SelingerProfileSipsMetric(const TranslationUnit& tu) : SipsMetric(tu) { profileUseAnalysis = &tu.getAnalysis(); polyAnalysis = &tu.getAnalysis(); - sccGraph = &tu.getAnalysis(); - program = &tu.getProgram(); } std::vector SelingerProfileSipsMetric::getReordering( @@ -94,10 +99,10 @@ std::vector SelingerProfileSipsMetric::getReordering( } auto constraints = ast::getBodyLiterals(*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(*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!"); @@ -458,7 +463,7 @@ Own 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& sccAtoms, std::size_t version, ast2ram::TranslationMode mode) const { using namespace souffle::ast2ram; @@ -550,28 +555,32 @@ std::unique_ptr SipsMetric::create(const std::string& heuristic, con if (Global::config().has("auto-schedule")) { return mk(tu); } else if (heuristic == "strict") - return mk(); + return mk(tu); else if (heuristic == "all-bound") - return mk(); + return mk(tu); else if (heuristic == "naive") - return mk(); + return mk(tu); else if (heuristic == "max-bound") - return mk(); + return mk(tu); + else if (heuristic == "delta-max-bound") + return mk(tu); else if (heuristic == "max-ratio") - return mk(); + return mk(tu); else if (heuristic == "least-free") - return mk(); + return mk(tu); else if (heuristic == "least-free-vars") - return mk(); + return mk(tu); else if (heuristic == "input") - return mk(tu.getProgram(), tu.getAnalysis()); + return mk(tu); // default is all-bound return create("all-bound", tu); } -std::vector StrictSips::evaluateCosts( - const std::vector atoms, const BindingStore& /* bindingStore */) const { +std::vector StrictSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector atoms, + const BindingStore& /* bindingStore */, std::size_t /*version*/, + ast2ram::TranslationMode /*mode*/) const { // Goal: Always choose the left-most atom std::vector cost; for (const auto* atom : atoms) { @@ -581,8 +590,9 @@ std::vector StrictSips::evaluateCosts( return cost; } -std::vector AllBoundSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector AllBoundSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector atoms, + const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { // Goal: Prioritise atoms with all arguments bound std::vector cost; for (const auto* atom : atoms) { @@ -599,8 +609,9 @@ std::vector AllBoundSips::evaluateCosts( return cost; } -std::vector NaiveSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector NaiveSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector 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 cost; for (const auto* atom : atoms) { @@ -623,8 +634,9 @@ std::vector NaiveSips::evaluateCosts( return cost; } -std::vector MaxBoundSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector MaxBoundSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector 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 cost; for (const auto* atom : atoms) { @@ -650,8 +662,9 @@ std::vector MaxBoundSips::evaluateCosts( return cost; } -std::vector MaxRatioSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector MaxRatioSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector atoms, + const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { // Goal: prioritise max ratio of bound args std::vector cost; for (const auto* atom : atoms) { @@ -677,8 +690,9 @@ std::vector MaxRatioSips::evaluateCosts( return cost; } -std::vector LeastFreeSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector LeastFreeSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector 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 cost; for (const auto* atom : atoms) { @@ -692,8 +706,9 @@ std::vector LeastFreeSips::evaluateCosts( return cost; } -std::vector LeastFreeVarsSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +std::vector LeastFreeVarsSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector 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 cost; for (const auto* atom : atoms) { @@ -714,8 +729,12 @@ std::vector LeastFreeVarsSips::evaluateCosts( return cost; } -std::vector InputSips::evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const { +InputSips::InputSips(const TranslationUnit& tu) + : StaticSipsMetric(tu), ioTypes(tu.getAnalysis()) {} + +std::vector InputSips::evaluateCosts(const Clause* /*clause*/, + const std::vector& /*sccAtoms*/, const std::vector atoms, + const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { // Goal: prioritise (1) all-bound, (2) input, then (3) rest std::vector cost; for (const auto* atom : atoms) { @@ -740,4 +759,39 @@ std::vector InputSips::evaluateCosts( return cost; } +std::vector DeltaMaxBoundSips::evaluateCosts(const Clause* clause, + const std::vector& sccAtoms, const std::vector 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 cost; + for (const auto* atom : atoms) { + if (atom == nullptr) { + cost.push_back(std::numeric_limits::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 diff --git a/src/ast/utility/SipsMetric.h b/src/ast/utility/SipsMetric.h index 38934a0c522..c9946e6b095 100644 --- a/src/ast/utility/SipsMetric.h +++ b/src/ast/utility/SipsMetric.h @@ -51,6 +51,8 @@ using PowerSet = std::vector>; */ class SipsMetric { public: + SipsMetric(const TranslationUnit& tu); + virtual ~SipsMetric() = default; /** @@ -63,6 +65,14 @@ class SipsMetric { /** Create a SIPS metric based on a given heuristic. */ static std::unique_ptr create(const std::string& heuristic, const TranslationUnit& tu); + + std::string getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom, + const std::vector& sccAtoms, std::size_t version, + ast2ram::TranslationMode mode) const; + +protected: + const ast::Program& program; + const ast::analysis::SCCGraphAnalysis* sccGraph = nullptr; }; class SelingerProfileSipsMetric : public SipsMetric { @@ -80,20 +90,18 @@ class SelingerProfileSipsMetric : public SipsMetric { }; const PowerSet& getSubsets(std::size_t N, std::size_t K) const; - std::string getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom, - const std::vector& sccAtoms, std::size_t version, - ast2ram::TranslationMode mode) const; + Own translateConstant(const ast::Constant& constant) const; - const ast::analysis::SCCGraphAnalysis* sccGraph = nullptr; const ast::analysis::PolymorphicObjectsAnalysis* polyAnalysis = nullptr; const ast::analysis::ProfileUseAnalysis* profileUseAnalysis = nullptr; - ast::Program* program = nullptr; mutable std::map, PowerSet> cache; }; class StaticSipsMetric : public SipsMetric { public: + StaticSipsMetric(const TranslationUnit& tu) : SipsMetric(tu) {} + std::vector getReordering( const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const override; @@ -103,92 +111,110 @@ class StaticSipsMetric : public SipsMetric { * @param atoms atoms to choose from; may be nullptr * @param bindingStore the variables already bound to a value */ - virtual std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const = 0; + virtual std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const = 0; }; /** Goal: Always choose the left-most atom */ class StrictSips : public StaticSipsMetric { public: - StrictSips() = default; + StrictSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: Prioritise atoms with all arguments bound */ class AllBoundSips : public StaticSipsMetric { public: - AllBoundSips() = default; + AllBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: Prioritise (1) all bound, then (2) atoms with at least one bound argument, then (3) left-most */ class NaiveSips : public StaticSipsMetric { public: - NaiveSips() = default; + NaiveSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: prioritise (1) all-bound, then (2) max number of bound vars, then (3) left-most */ class MaxBoundSips : public StaticSipsMetric { public: - MaxBoundSips() = default; + MaxBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} + +protected: + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; +}; + +/** Goal: prioritise (1) delta, (2) all-bound, then (3) max number of bound vars, then (4) left-most */ +class DeltaMaxBoundSips : public StaticSipsMetric { +public: + DeltaMaxBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: prioritise max ratio of bound args */ class MaxRatioSips : public StaticSipsMetric { public: - MaxRatioSips() = default; + MaxRatioSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: choose the atom with the least number of unbound arguments */ class LeastFreeSips : public StaticSipsMetric { public: - LeastFreeSips() = default; + LeastFreeSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: choose the atom with the least amount of unbound variables */ class LeastFreeVarsSips : public StaticSipsMetric { public: - LeastFreeVarsSips() = default; + LeastFreeVarsSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; }; /** Goal: prioritise (1) all-bound, then (2) input, and then (3) left-most */ class InputSips : public StaticSipsMetric { public: - InputSips(const Program& program, const analysis::IOTypeAnalysis& ioTypes) - : program(program), ioTypes(ioTypes) {} + InputSips(const TranslationUnit& tu); protected: - std::vector evaluateCosts( - const std::vector atoms, const BindingStore& bindingStore) const override; + std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, + const std::vector atoms, const BindingStore& bindingStore, std::size_t version, + ast2ram::TranslationMode mode) const override; private: - const Program& program; const analysis::IOTypeAnalysis& ioTypes; };