Skip to content

Commit

Permalink
Merge pull request #1167 from b-scholz/Reordering
Browse files Browse the repository at this point in the history
Reordering conjunctive terms based on complexity
  • Loading branch information
mmcgr authored Nov 29, 2019
2 parents 8c7d775 + 094fee1 commit 1da3515
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ souffle_sources = \
InterpreterProgInterface.h \
InterpreterPreamble.h \
InterpreterRecords.h InterpreterRecords.cpp \
RamComplexityAnalysis.cpp RamComplexityAnalysis.h \
RamLevelAnalysis.cpp RamLevelAnalysis.h \
RamCondition.h \
RamNode.h \
Expand Down
65 changes: 65 additions & 0 deletions src/RamComplexityAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Souffle - A Datalog Compiler
* Copyright (c) 2019, The Souffle Developers. All rights reserved
* Licensed under the Universal Permissive License v 1.0 as shown at:
* - https://opensource.org/licenses/UPL
* - <souffle root>/licenses/SOUFFLE-UPL.txt
*/

/************************************************************************
*
* @file RamComplexityAnalysis.cpp
*
* Implementation of RAM Complexity Analysis
*
***********************************************************************/

#include "RamComplexityAnalysis.h"
#include "RamVisitor.h"
#include <algorithm>

namespace souffle {

int RamComplexityAnalysis::getComplexity(const RamNode* node) const {
// visitor
class ValueComplexityVisitor : public RamVisitor<int> {
public:
// conjunction
int visitConjunction(const RamConjunction& conj) override {
return visit(conj.getLHS()) + visit(conj.getRHS());
}

// negation
int visitNegation(const RamNegation& neg) override {
return visit(neg.getOperand());
}

// existence check
int visitExistenceCheck(const RamExistenceCheck& exists) override {
return 2;
}

// provenance existence check
int visitProvenanceExistenceCheck(const RamProvenanceExistenceCheck& provExists) override {
return 2;
}

// emptiness check
int visitEmptinessCheck(const RamEmptinessCheck& emptiness) override {
// emptiness check for nullary relations is for free; others have weight one
return (emptiness.getRelation().getArity() > 0) ? 1 : 0;
}

// default rule
int visitNode(const RamNode& node) override {
return 0;
}
};

assert((dynamic_cast<const RamExpression*>(node) != nullptr ||
dynamic_cast<const RamCondition*>(node) != nullptr) &&
"not an expression/condition/operation");
return ValueComplexityVisitor().visit(node);
}

} // end of namespace souffle
47 changes: 47 additions & 0 deletions src/RamComplexityAnalysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Souffle - A Datalog Compiler
* Copyright (c) 2019, The Souffle Developers. All rights reserved
* Licensed under the Universal Permissive License v 1.0 as shown at:
* - https://opensource.org/licenses/UPL
* - <souffle root>/licenses/SOUFFLE-UPL.txt
*/

/************************************************************************
*
* @file RamComplexityAnalysis.h
*
* Get the complexity of an expression/condition in terms of
* database operations. The complexity of an expression/condition is a
* weighted sum. The weights express the complexity of the terms.
***********************************************************************/

#pragma once

#include "RamAnalysis.h"

namespace souffle {

class RamNode;

/**
* @class RamComplexityAnalysis
* @brief A Ram Analysis for determining the number of relational
* operations in a condition / expression.
*
*
*/
class RamComplexityAnalysis : public RamAnalysis {
public:
RamComplexityAnalysis(const char* id) : RamAnalysis(id) {}

static constexpr const char* name = "complexity-analysis";

void run(const RamTranslationUnit& translationUnit) override {}

/**
* @brief Get complexity of a RAM expression/condition
*/
int getComplexity(const RamNode* value) const;
};

} // end of namespace souffle
41 changes: 38 additions & 3 deletions src/RamTransforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "RamTransforms.h"
#include "BinaryConstraintOps.h"
#include "RamComplexityAnalysis.h"
#include "RamCondition.h"
#include "RamExpression.h"
#include "RamNode.h"
Expand All @@ -25,6 +26,8 @@
#include "RamStatement.h"
#include "RamTypes.h"
#include "RamVisitor.h"
#include <algorithm>
#include <list>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -67,6 +70,41 @@ bool ExpandFilterTransformer::expandFilters(RamProgram& program) {
return changed;
}

bool ReorderConditionsTransformer::reorderConditions(RamProgram& program) {
// flag to determine whether the RAM program has changed
bool changed = false;

visitDepthFirst(program, [&](const RamQuery& query) {
std::function<std::unique_ptr<RamNode>(std::unique_ptr<RamNode>)> filterRewriter =
[&](std::unique_ptr<RamNode> node) -> std::unique_ptr<RamNode> {
if (const RamFilter* filter = dynamic_cast<RamFilter*>(node.get())) {
const RamCondition* condition = &filter->getCondition();
std::vector<std::unique_ptr<RamCondition>> condList = toConjunctionList(condition);
std::vector<const RamCondition*> sortedConds;
for (auto& cond : condList) {
sortedConds.push_back(cond.get());
}
std::sort(sortedConds.begin(), sortedConds.end(),
[&](const RamCondition* a, const RamCondition* b) {
return rca->getComplexity(a) < rca->getComplexity(b);
});

if (!std::equal(sortedConds.begin(), sortedConds.end(), condList.begin(),
[](const RamCondition* a, const auto& b) { return *a == *b; })) {
changed = true;
node = std::make_unique<RamFilter>(
std::unique_ptr<RamCondition>(toCondition(sortedConds)),
std::unique_ptr<RamOperation>(filter->getOperation().clone()));
}
}
node->apply(makeLambdaRamMapper(filterRewriter));
return node;
};
const_cast<RamQuery*>(&query)->apply(makeLambdaRamMapper(filterRewriter));
});
return changed;
}

bool CollapseFiltersTransformer::collapseFilters(RamProgram& program) {
// flag to determine whether the RAM program has changed
bool changed = false;
Expand Down Expand Up @@ -644,9 +682,6 @@ bool ParallelTransformer::parallelizeOperations(RamProgram& program) {

// parallelize the most outer loop only
// most outer loops can be scan/choice/indexScan/indexChoice
//
// TODO (b-scholz): renumbering may be necessary since some operations
// may have reduced a loop to a filter operation.
visitDepthFirst(program, [&](const RamQuery& query) {
std::function<std::unique_ptr<RamNode>(std::unique_ptr<RamNode>)> parallelRewriter =
[&](std::unique_ptr<RamNode> node) -> std::unique_ptr<RamNode> {
Expand Down
53 changes: 53 additions & 0 deletions src/RamTransforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include "RamComplexityAnalysis.h"
#include "RamIndexAnalysis.h"
#include "RamLevelAnalysis.h"
#include "RamTransformer.h"
Expand Down Expand Up @@ -70,6 +71,58 @@ class ExpandFilterTransformer : public RamTransformer {
}
};

/**
* @class ReorderConditionsTransformer
* @brief Reorders conjunctive terms depending on cost, i.e.,
* cheap terms should be executed first.
*
* For example ..
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
* QUERY
* ...
* IF C(1) /\ C(2) /\ ... /\ C(N) then
* ...
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* will be rewritten to
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
* QUERY
* ...
* IF C(i(1)) /\ C(i(2)) /\ ... /\ C(i(N)) then
* ...
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* where C(i(1)) <= C(i(2)) <= .... <= C(i(N)).
*
* The terms are sorted according to their complexity class.
*
*/

class ReorderConditionsTransformer : public RamTransformer {
public:
std::string getName() const override {
return "ReorderConditionsTransformer";
}

/**
* @brief Reorder conjunctive terms in filter operations
* @param program Program that is transformed
* @return Flag showing whether the program has been changed
* by the transformation
*/
bool reorderConditions(RamProgram& program);

protected:
RamComplexityAnalysis* rca{nullptr};

bool transform(RamTranslationUnit& translationUnit) override {
rca = translationUnit.getAnalysis<RamComplexityAnalysis>();
return reorderConditions(*translationUnit.getProgram());
}
};

/**
* @class CollapseFiltersTransformer
* @brief Transforms consecutive filters into a single filter containing a conjunction
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ int main(int argc, char** argv) {
std::make_unique<RamLoopTransformer>(std::make_unique<RamTransformerSequence>(
std::make_unique<HoistAggregateTransformer>(), std::make_unique<TupleIdTransformer>())),
std::make_unique<ExpandFilterTransformer>(), std::make_unique<HoistConditionsTransformer>(),
std::make_unique<CollapseFiltersTransformer>(), std::make_unique<ReorderConditionsTransformer>(),
std::make_unique<RamConditionalTransformer>(
// job count of 0 means all cores are used.
[]() -> bool { return std::stoi(Global::config().get("jobs")) != 1; },
Expand Down

0 comments on commit 1da3515

Please sign in to comment.