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

Compile time optimizations #134

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/commands/execution_ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "../query_ctx.h"
#include "../errors/errors.h"
#include "../execution_plan/execution_plan_clone.h"
#include "../execution_plan/optimizations/optimizer.h"

static ExecutionType _GetExecutionTypeFromAST
(
Expand Down Expand Up @@ -191,6 +192,9 @@ ExecutionCtx *ExecutionCtx_FromQuery
return NULL;
}

// apply compile time optimizations
Optimizer_CompileTimeOptimize(plan);

ExecutionCtx *exec_ctx = _ExecutionCtx_New(ast, plan, exec_type);
ret = Cache_SetGetValue(cache, q_str, exec_ctx);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/execution_plan/execution_plan.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ ExecutionPlan *ExecutionPlan_FromTLS_AST(void) {
void ExecutionPlan_PreparePlan(ExecutionPlan *plan) {
// Plan should be prepared only once.
ASSERT(!plan->prepared);
optimizePlan(plan);
Optimizer_RuntimeOptimize(plan);
plan->prepared = true;
}

Expand Down
19 changes: 12 additions & 7 deletions src/execution_plan/execution_plan_build/execution_plan_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,25 @@ OpBase *ExecutionPlan_LocateReferences

// populates `ops` with all operations with a type in `types` in an
// execution plan, based at `root`
static void _ExecutionPlan_CollectOpsMatchingTypes(OpBase *root, const OPType *types, int type_count,
OpBase ***ops) {
static void _ExecutionPlan_CollectOpsMatchingTypes
(
OpBase *root,
const OPType *types,
int type_count,
OpBase ***ops
) {
for(int i = 0; i < type_count; i++) {
// Check to see if the op's type matches any of the types we're searching for.
// check to see if the op's type matches any of the types provided
if(root->type == types[i]) {
array_append(*ops, root);
break;
}
}

for(int i = 0; i < root->childCount; i++) {
// Recursively visit children.
_ExecutionPlan_CollectOpsMatchingTypes(root->children[i], types, type_count, ops);
// recursively visit children
_ExecutionPlan_CollectOpsMatchingTypes(root->children[i], types,
type_count, ops);
}
}

Expand All @@ -236,8 +242,7 @@ OpBase **ExecutionPlan_CollectOps
OPType type
) {
OpBase **ops = array_new(OpBase *, 0);
const OPType type_arr[1] = {type};
_ExecutionPlan_CollectOpsMatchingTypes(root, type_arr, 1, &ops);
_ExecutionPlan_CollectOpsMatchingTypes(root, &type, 1, &ops);
return ops;
}

Expand Down
55 changes: 43 additions & 12 deletions src/execution_plan/ops/op_cond_var_len_traverse.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,35 @@ static inline void _setTraverseDirection(CondVarLenTraverse *op, const QGEdge *e
}
}

static inline void CondVarLenTraverseToString(const OpBase *ctx, sds *buf) {
static inline void CondVarLenTraverseToString
(
const OpBase *ctx,
sds *buf
) {
// TODO: tmp, improve TraversalToString
CondVarLenTraverse *op = (CondVarLenTraverse *)ctx;
AlgebraicExpression_Optimize(&op->ae);
TraversalToString(ctx, buf, op->ae);
}

void CondVarLenTraverseOp_ExpandInto(CondVarLenTraverse *op) {
// Expand into doesn't performs any modifications.
void CondVarLenTraverseOp_ExpandInto
(
CondVarLenTraverse *op
) {
// expand into doesn't performs any modifications
array_clear(op->op.modifies);
op->expandInto = true;
op->op.type = OPType_CONDITIONAL_VAR_LEN_TRAVERSE_EXPAND_INTO;
op->op.name = "Conditional Variable Length Traverse (Expand Into)";
}

inline void CondVarLenTraverseOp_SetFilter(CondVarLenTraverse *op,
FT_FilterNode *ft) {
ASSERT(op != NULL);
ASSERT(ft != NULL);
inline void CondVarLenTraverseOp_SetFilter
(
CondVarLenTraverse *op,
FT_FilterNode *ft
) {
ASSERT(op != NULL);
ASSERT(ft != NULL);
ASSERT(op->ft == NULL);

op->ft = ft;
Expand Down Expand Up @@ -326,12 +336,33 @@ static OpResult CondVarLenTraverseReset(OpBase *ctx) {
return OP_OK;
}

static OpBase *CondVarLenTraverseClone(const ExecutionPlan *plan, const OpBase *opBase) {
ASSERT(opBase->type == OPType_CONDITIONAL_VAR_LEN_TRAVERSE);
static OpBase *CondVarLenTraverseClone
(
const ExecutionPlan *plan,
const OpBase *opBase
) {
ASSERT(opBase->type == OPType_CONDITIONAL_VAR_LEN_TRAVERSE ||
opBase->type == OPType_CONDITIONAL_VAR_LEN_TRAVERSE_EXPAND_INTO);

// clone "conditional var length traversal" operation
// handels the case where the operation had been modified into an
// "conditional var len expand into"
CondVarLenTraverse *op = (CondVarLenTraverse *) opBase;
OpBase *op_clone = NewCondVarLenTraverseOp(plan, QueryCtx_GetGraph(),
AlgebraicExpression_Clone(op->ae));
return op_clone;
OpBase *clone = NewCondVarLenTraverseOp(plan, QueryCtx_GetGraph(),
AlgebraicExpression_Clone(op->ae));

// clone filter tree
if(op->ft != NULL) {
CondVarLenTraverseOp_SetFilter((CondVarLenTraverse*)clone,
FilterTree_Clone(op->ft));
}

// switch to expand into
if(opBase->type == OPType_CONDITIONAL_VAR_LEN_TRAVERSE_EXPAND_INTO) {
CondVarLenTraverseOp_ExpandInto((CondVarLenTraverse*) clone);
}

return clone;
}

static void CondVarLenTraverseFree(OpBase *ctx) {
Expand Down
11 changes: 7 additions & 4 deletions src/execution_plan/ops/op_edge_by_index_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,14 @@ static OpResult EdgeIndexScanInit

// source and destination nodes may or may not already be resolved
// missing nodes will be resolved by this operation
const char *src_alias = QGNode_Alias(QGEdge_Src(op->edge));
const char *dest_alias = QGNode_Alias(QGEdge_Dest(op->edge));
const char *src_alias = QGNode_Alias(QGEdge_Src(op->edge));
const char *dest_alias = QGNode_Alias(QGEdge_Dest(op->edge));

op->srcAware = OpBase_ChildrenAware((OpBase *)op, src_alias, &op->srcRecIdx);
op->destAware = OpBase_ChildrenAware((OpBase *)op, dest_alias, &op->destRecIdx);
op->srcAware = OpBase_ChildrenAware((OpBase *)op, src_alias,
&op->srcRecIdx);

op->destAware = OpBase_ChildrenAware((OpBase *)op, dest_alias,
&op->destRecIdx);

if(!op->srcAware) {
op->srcRecIdx = OpBase_Modifies((OpBase *)op, src_alias);
Expand Down
21 changes: 14 additions & 7 deletions src/execution_plan/optimizations/apply_join.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,26 @@ static void _reduce_cp_to_hashjoin(ExecutionPlan *plan, OpBase *cp) {
array_free(filter_ops);
}

// TODO: Consider changing Cartesian Products such that each has exactly two child operations.
/* Try to replace Cartesian Products (cross joins) with Value Hash Joins.
* This is viable when a Cartesian Product is combining two streams that each satisfies
* one side of an EQUALS filter operation, like:
* MATCH (a), (b) WHERE ID(a) = ID(b) */
void applyJoin(ExecutionPlan *plan) {
OpBase **cps = ExecutionPlan_CollectOps(plan->root, OPType_CARTESIAN_PRODUCT);
// TODO: consider changing Cartesian Products such that each has exactly two
// child operations

// try to replace Cartesian Products (cross joins) with Value Hash Joins
// this is viable when a Cartesian Product is combining two streams that each
// satisfies one side of an EQUALS filter operation like:
// MATCH (a), (b) WHERE ID(a) = ID(b)
void applyJoin
(
ExecutionPlan *plan
) {
OpBase **cps = ExecutionPlan_CollectOps(plan->root,
OPType_CARTESIAN_PRODUCT);
uint cp_count = array_len(cps);

for(uint i = 0; i < cp_count; i++) {
OpBase *cp = cps[i];
_reduce_cp_to_hashjoin(plan, cp);
}

array_free(cps);
}

10 changes: 5 additions & 5 deletions src/execution_plan/optimizations/apply_skip.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#include "../ops/op_sort.h"
#include "../ops/op_skip.h"

/* applySkip will traverse the given execution plan looking for Skip operations.
* Once one is found, all relevant child operations (e.g. Sort) will be
* notified about the current skip value.
* This is beneficial as a number of different optimizations can be applied
* once a skip is known */
// applySkip will traverse the given execution plan looking for Skip operations
// Once one is found, all relevant child operations (e.g. Sort) will be
// notified about the current skip value
// this is beneficial as a number of different optimizations can be applied
// once a skip is known

static void notify_skip(OpBase *op, uint skip) {
OPType t = op->type;
Expand Down
70 changes: 44 additions & 26 deletions src/execution_plan/optimizations/compact_filters.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,77 @@
#include "../ops/op_filter.h"
#include "../../errors/errors.h"
#include "../../filter_tree/filter_tree.h"
#include "../execution_plan_build/execution_plan_util.h"
#include "../execution_plan_build/execution_plan_modify.h"

/* The compact filters optimizer scans an execution plan for filters that can be
* compressed. In case the filter is compressed into a final constant 'true' value,
* the filter operation will be removed from the execution plan. */
// the compact filters optimizer scans an execution plan for filters that can be
// compressed. in case the filter is compressed into a final constant 'true'
// value, the filter operation will be removed from the execution plan

// Try to compact a filter.
static inline bool _compactFilter(OpBase *op) {
// try to compact a filter
static inline bool _compactFilter
(
OpBase *op
) {
ASSERT(op->type == OPType_FILTER);

OpFilter *filter_op = (OpFilter *)op;
return FilterTree_Compact(filter_op->filterTree);
}

// In case the compacted filter resolved to 'true', remove it from the execution plan.
static void _removeTrueFilter(ExecutionPlan *plan, OpBase *op) {
ASSERT(op->type == OPType_FILTER);
// in case the compacted filter resolved to 'true'
// remove it from the execution plan
static void _removeTrueFilter
(
ExecutionPlan *plan,
OpBase *op
) {
ASSERT(OpBase_Type(op) == OPType_FILTER);

OpFilter *filter_op = (OpFilter *)op;
FT_FilterNode *root = filter_op->filterTree;
// We can only have a contant expression in this point (after compaction).

// we can only have a contant expression at this point (after compaction)
ASSERT(root->t == FT_N_EXP);
// Evaluate the expression, and check if it is a 'true' value.

// evaluate the expression, and check if it is a 'true' value
SIValue bool_val = AR_EXP_Evaluate(root->exp.exp, NULL);
if(SI_TYPE(bool_val) != T_BOOL && SI_TYPE(bool_val) != T_NULL) {
// Value did not resolve to boolean, emit an error.
// Value did not resolve to boolean, emit an error
Error_SITypeMismatch(bool_val, T_BOOL);
SIValue_Free(bool_val);
return;
}

if(!SIValue_IsNull(bool_val) && SIValue_IsTrue(bool_val)) {
ExecutionPlan_RemoveOp(plan, op);
OpBase_Free(op);
}
}

static void _compactFilters(ExecutionPlan *plan, OpBase *op) {
if(op == NULL) return;

// Try to compact the filter.
bool compact = false;
if(op->type == OPType_FILTER) {
compact = _compactFilter(op);
}
static void _compactFilters
(
ExecutionPlan *plan,
OpBase *op
) {
OpBase **filters = ExecutionPlan_CollectOps(op, OPType_FILTER);
int n = array_len(filters);

// Try to compact children.
for(int i = 0; i < op->childCount; i++) {
_compactFilters(plan, op->children[i]);
for(int i = 0; i < n; i++) {
OpBase *op = filters[i];
// try to compact the filter
if(_compactFilter(op)) {
// if there was a compaction, try to remove 'true' filters
_removeTrueFilter(plan, op);
}
}

// If there was a compaction, try to remove 'true' filters.
if(compact) _removeTrueFilter(plan, op);
array_free(filters);
}

void compactFilters(ExecutionPlan *plan) {
void compactFilters
(
ExecutionPlan *plan
) {
_compactFilters(plan, plan->root);
}