Skip to content

Commit 82ec3f7

Browse files
committed
[PhysOpt][Backend] Decouple physical optimization from specific backend.
Prior to this commit, the `PhysicalOptimizer` was only used in the WasmV8 backend. Now, the physical optimization takes place independently before calling a backend to execute the query. The interpreter currently does not support physical operators and thus ignores the given empty `PhysicalOptimizer` instance.
1 parent ee27a6d commit 82ec3f7

File tree

14 files changed

+191
-135
lines changed

14 files changed

+191
-135
lines changed

include/mutable/IR/PhysicalOptimizer.hpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,15 @@ struct MatchBase
172172
public:
173173
virtual ~MatchBase() { }
174174

175+
/** Executes this physical operator match. Recursively calls children operators to execute. The three callbacks
176+
* are specified by the parent operator and are used as follows: \p setup for some initializations, \p pipeline
177+
* for the actual computation, and \p teardown for post-processing. */
175178
virtual void execute(setup_t setup, pipeline_t pipeline, teardown_t teardown) const = 0;
176179

180+
/** Returns the matched logical operator for physical operators with singleton patterns. Must not be called for
181+
* physical operators with a pattern tree. */
182+
virtual const Operator & get_matched_singleton() const = 0;
183+
177184
double cost() const { return cost_; }
178185
private:
179186
void cost(double new_cost) { cost_ = new_cost; }
@@ -228,9 +235,8 @@ struct PhysicalOptimizer
228235

229236
/** Returns true iff a physical operator covering is found. */
230237
virtual bool has_plan() const = 0;
231-
232-
/** Executes the found physical operator covering. */
233-
virtual void execute() const = 0;
238+
/** Returns the found physical operator covering. */
239+
virtual const MatchBase & get_plan() const = 0;
234240

235241
virtual void accept(PhysOptVisitor &v) = 0;
236242
virtual void accept(ConstPhysOptVisitor &v) const = 0;
@@ -272,8 +278,9 @@ struct PhysicalOptimizerImpl : PhysicalOptimizer, ConstPostOrderOperatorVisitor
272278
}
273279

274280
bool has_plan() const override { return not table().back().empty(); }
281+
private:
275282
/** Returns the entry for the found physical operator covering. */
276-
const entry_type & get_plan() const {
283+
const entry_type & get_plan_entry() const {
277284
M_insist(has_plan(), "no physical operator covering found");
278285
typename PhysicalPlanTable::condition2entry_map_type::const_iterator it_best;
279286
double min_cost = std::numeric_limits<double>::infinity();
@@ -285,8 +292,8 @@ struct PhysicalOptimizerImpl : PhysicalOptimizer, ConstPostOrderOperatorVisitor
285292
}
286293
return it_best->entry;
287294
}
288-
289-
void execute() const override;
295+
public:
296+
const MatchBase & get_plan() const override { return get_plan_entry().match(); }
290297

291298
private:
292299
/** Handles the found match \p match with children entries \p children for the logical plan rooted in \p op. */

include/mutable/backend/Backend.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <mutable/IR/PhysicalOptimizer.hpp>
34
#include <mutable/mutable-config.hpp>
45
#include <memory>
56
#include <string>
@@ -16,8 +17,11 @@ struct M_EXPORT Backend
1617
{
1718
virtual ~Backend() { }
1819

19-
/** Executes the given `plan` using this `Backend`. */
20-
virtual void execute(const Operator &plan) const = 0;
20+
/** Registers all physical operators of this `Backend` in \p phys_opt. */
21+
virtual void register_operators(PhysicalOptimizer &phys_opt) const = 0;
22+
23+
/** Executes the already computed physical covering represented by \p plan using this `Backend`. */
24+
virtual void execute(const MatchBase &plan) const = 0;
2125
};
2226

2327
}

include/mutable/backend/WebAssembly.hpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ struct WasmEngine
3434

3535
public:
3636
unsigned id; ///< a unique ID
37-
const Operator &plan; ///< current plan
37+
const MatchBase &plan; ///< current plan
3838
///> factory used to create the result set data layout
3939
std::unique_ptr<const storage::DataLayoutFactory> result_set_factory;
4040
memory::AddressSpace vm; ///< WebAssembly module instance's virtual address space aka.\ *linear memory*
4141
uint32_t heap = 0; ///< beginning of the heap, encoded as offset from the beginning of the virtual address space
4242

43-
WasmContext(uint32_t id, config_t configuration, const Operator &plan, std::size_t size);
43+
WasmContext(uint32_t id, const MatchBase &plan, config_t configuration, std::size_t size);
4444

4545
bool config(config_t cfg) const { return bool(cfg & config_); }
4646

@@ -61,11 +61,11 @@ struct WasmEngine
6161
public:
6262
/** Creates a new `WasmContext` for ID `id` with `size` bytes of virtual address space. */
6363
static WasmContext & Create_Wasm_Context_For_ID(unsigned id,
64+
const MatchBase &plan,
6465
WasmContext::config_t configuration = WasmContext::config_t(0x0),
65-
const Operator &plan = NoOpOperator(std::cout),
6666
std::size_t size = WASM_MAX_MEMORY)
6767
{
68-
auto wasm_context = std::make_unique<WasmContext>(id, configuration, plan, size);
68+
auto wasm_context = std::make_unique<WasmContext>(id, plan, configuration, size);
6969
auto [it, inserted] = contexts_.emplace(id, std::move(wasm_context));
7070
M_insist(inserted, "WasmContext with that ID already exists");
7171
return *it->second;
@@ -74,12 +74,12 @@ struct WasmEngine
7474
/** If none exists, creates a new `WasmContext` for ID `id` with `size` bytes of virtual address space. */
7575
static std::pair<std::reference_wrapper<WasmContext>, bool>
7676
Ensure_Wasm_Context_For_ID(unsigned id,
77+
const MatchBase &plan,
7778
WasmContext::config_t configuration = WasmContext::config_t(0x0),
78-
const Operator &plan = NoOpOperator(std::cout),
7979
std::size_t size = WASM_MAX_MEMORY)
8080
{
8181
auto [it, inserted] = contexts_.try_emplace(id, lazy_construct(
82-
[&](){ return std::make_unique<WasmContext>(id, configuration, plan, size); }
82+
[&](){ return std::make_unique<WasmContext>(id, plan, configuration, size); }
8383
));
8484
return { std::ref(*it->second), inserted };
8585
}
@@ -109,11 +109,11 @@ struct WasmEngine
109109
WasmEngine(const WasmEngine&) = delete;
110110
WasmEngine(WasmEngine&&) = default;
111111

112-
/** Compiles the given `plan` for this `WasmEngine`. */
113-
virtual void compile(const Operator &plan) const = 0;
112+
/** Compiles the already computed physical covering represented by \p plan using this `WasmEngine`. */
113+
virtual void compile(const MatchBase &plan) const = 0;
114114

115-
/** Executes the given `plan` on this `WasmEngine`. */
116-
virtual void execute(const Operator &plan) = 0;
115+
/** Executes the already computed physical covering represented by \p plan using this `WasmEngine`. */
116+
virtual void execute(const MatchBase &plan) = 0;
117117
};
118118

119119
/** A `Backend` to execute a plan on a specific `WasmEngine`. */
@@ -128,8 +128,9 @@ struct WasmBackend : Backend
128128
/** Returns this backend's `WasmEngine`. */
129129
const WasmEngine & engine() const { return *engine_; }
130130

131-
/** Executes the given `plan` with this backend. */
132-
void execute(const Operator &plan) const override;
131+
void register_operators(PhysicalOptimizer &phys_opt) const override;
132+
133+
void execute(const MatchBase &plan) const override;
133134
};
134135

135136
}

src/IR/PhysicalOptimizer.cpp

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
#include <mutable/IR/PhysicalOptimizer.hpp>
22

3-
#include "backend/WasmDSL.hpp"
4-
#include "backend/WasmMacro.hpp"
5-
#include "backend/WasmUtil.hpp"
6-
73

84
using namespace m;
9-
using namespace m::wasm;
105

116

127
/*======================================================================================================================
@@ -23,20 +18,6 @@ M_LCOV_EXCL_STOP
2318
* PhysicalOptimizerImpl
2419
*====================================================================================================================*/
2520

26-
template<typename PhysicalPlanTable>
27-
void PhysicalOptimizerImpl<PhysicalPlanTable>::execute() const
28-
{
29-
/* Emit code for run function which computes the last pipeline and calls other pipeline functions. */
30-
FUNCTION(run, void(void))
31-
{
32-
auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
33-
get_plan().match().execute(setup_t::Make_Without_Parent(), pipeline_t(), teardown_t::Make_Without_Parent());
34-
}
35-
36-
/* Call run function. */
37-
run();
38-
}
39-
4021
template<typename PhysicalPlanTable>
4122
void PhysicalOptimizerImpl<PhysicalPlanTable>::accept(PhysOptVisitor &v) { v(*this); }
4223
template<typename PhysicalPlanTable>
@@ -68,14 +49,14 @@ void PhysicalOptimizerImpl<PhysicalPlanTable>::dot_plan(std::ostream &out) const
6849
<< " graph [fontname = \"DejaVu Sans\"];\n"
6950
<< " node [fontname = \"DejaVu Sans\"];\n"
7051
<< " edge [fontname = \"DejaVu Sans\"];\n";
71-
dot_plan_helper(get_plan(), out);
52+
dot_plan_helper(get_plan_entry(), out);
7253
out << "}\n";
7354
};
7455

7556
template<typename PhysicalPlanTable>
7657
void PhysicalOptimizerImpl<PhysicalPlanTable>::dump_plan(std::ostream &out) const
7758
{
78-
out << get_plan().match() << std::endl;
59+
out << get_plan() << std::endl;
7960
}
8061
template<typename PhysicalPlanTable>
8162
void PhysicalOptimizerImpl<PhysicalPlanTable>::dump_plan() const { dump_plan(std::cerr); }

src/backend/Interpreter.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ struct Interpreter : Backend, ConstOperatorVisitor
216216
public:
217217
Interpreter() = default;
218218

219-
void execute(const Operator &plan) const override { (*const_cast<Interpreter*>(this))(plan); }
219+
void register_operators(PhysicalOptimizer &phys_opt) const override { /* nothing to be done */ }
220+
221+
void execute(const MatchBase &plan) const override { M_unreachable("currently not supported"); }
220222

221223
using ConstOperatorVisitor::operator();
222224
#define DECLARE(CLASS) void operator()(Const<CLASS> &op) override;

src/backend/V8Engine.cpp

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ struct V8Engine : m::WasmEngine
7272
static inline v8::Platform *PLATFORM_ = nullptr;
7373
v8::ArrayBuffer::Allocator *allocator_ = nullptr;
7474
v8::Isolate *isolate_ = nullptr;
75-
std::unique_ptr<PhysicalOptimizer> phys_opt_ = std::make_unique<PhysicalOptimizerImpl<ConcretePhysicalPlanTable>>();
7675

7776
/*----- Objects for remote debugging via CDT. --------------------------------------------------------------------*/
7877
std::unique_ptr<V8InspectorClientImpl> inspector_;
@@ -90,8 +89,8 @@ struct V8Engine : m::WasmEngine
9089
}
9190

9291
void initialize();
93-
void compile(const m::Operator &plan) const override;
94-
void execute(const m::Operator &plan) override;
92+
void compile(const MatchBase &plan) const override;
93+
void execute(const MatchBase &plan) override;
9594
};
9695

9796

@@ -294,7 +293,8 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
294293
{
295294
auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
296295

297-
auto &schema = context.plan.schema();
296+
auto &root_op = context.plan.get_matched_singleton();
297+
auto &schema = root_op.schema();
298298
auto deduplicated_schema = schema.deduplicate();
299299
auto deduplicated_schema_without_constants = deduplicated_schema.drop_constants();
300300

@@ -332,7 +332,7 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
332332
};
333333
return find_projection_impl(op, find_projection_impl);
334334
};
335-
auto &projections = find_projection(context.plan).projections();
335+
auto &projections = find_projection(root_op).projections();
336336

337337
///> helper function to print given `ast::Constant` \p c of `Type` \p type to \p out
338338
auto print_constant = [](std::ostringstream &out, const ast::Constant &c, const Type *type){
@@ -389,7 +389,7 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
389389

390390
if (deduplicated_schema_without_constants.num_entries() == 0) {
391391
/* Schema contains only constants. Create simple loop to generate `num_tuples` constant result tuples. */
392-
if (auto callback_op = cast<const CallbackOperator>(&context.plan)) {
392+
if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
393393
Tuple tup(schema); // tuple entries which are not set are implicitly NULL
394394
for (std::size_t i = 0; i < schema.num_entries(); ++i) {
395395
auto &e = schema[i];
@@ -399,7 +399,7 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
399399
}
400400
for (std::size_t i = 0; i < num_tuples; ++i)
401401
callback_op->callback()(schema, tup);
402-
} else if (auto print_op = cast<const PrintOperator>(&context.plan)) {
402+
} else if (auto print_op = cast<const PrintOperator>(&root_op)) {
403403
std::ostringstream tup;
404404
for (std::size_t i = 0; i < schema.num_entries(); ++i) {
405405
auto &e = schema[i];
@@ -419,7 +419,7 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
419419
auto layout = context.result_set_factory->make(deduplicated_schema_without_constants);
420420

421421
/* Extract results. */
422-
if (auto callback_op = cast<const CallbackOperator>(&context.plan)) {
422+
if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
423423
auto loader = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
424424
deduplicated_schema_without_constants);
425425
if (schema.num_entries() == deduplicated_schema.num_entries()) {
@@ -468,7 +468,7 @@ void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value>
468468
callback_op->callback()(schema, tup_dupl);
469469
}
470470
}
471-
} else if (auto print_op = cast<const PrintOperator>(&context.plan)) {
471+
} else if (auto print_op = cast<const PrintOperator>(&root_op)) {
472472
/* Compute a `Tuple` with duplicates and constants. */
473473
Tuple tup(deduplicated_schema_without_constants);
474474
Tuple *args[] = { &tup };
@@ -599,12 +599,7 @@ struct CollectStringLiterals : ConstOperatorVisitor, ast::ConstASTExprVisitor
599599
* V8Engine implementation
600600
*====================================================================================================================*/
601601

602-
V8Engine::V8Engine()
603-
{
604-
register_wasm_operators(*phys_opt_);
605-
606-
initialize();
607-
}
602+
V8Engine::V8Engine() { initialize(); }
608603

609604
V8Engine::~V8Engine()
610605
{
@@ -661,24 +656,27 @@ void V8Engine::initialize()
661656
isolate_ = v8::Isolate::New(create_params);
662657
}
663658

664-
void V8Engine::compile(const Operator &plan) const
659+
void V8Engine::compile(const MatchBase &plan) const
665660
{
666661
#if 1
667662
/*----- Add print function. --------------------------------------------------------------------------------------*/
668663
Module::Get().emit_function_import<void(uint32_t)>("print");
669664
#endif
670665

666+
/*----- Emit code for run function which computes the last pipeline and calls other pipeline functions. ----------*/
667+
FUNCTION(run, void(void))
668+
{
669+
auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
670+
plan.execute(setup_t::Make_Without_Parent(), pipeline_t(), teardown_t::Make_Without_Parent()); // emit code
671+
}
672+
671673
/*----- Create function `main` which executes the given query. ---------------------------------------------------*/
672674
m::wasm::Function<uint32_t(uint32_t)> main("main");
673675
BLOCK_OPEN(main.body())
674676
{
675677
auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
676-
677-
/*----- Compile plan. ----------------------------------------------------------------------------------------*/
678-
phys_opt_->execute(); // emit code
679-
680-
/*----- Return size of result set. ---------------------------------------------------------------------------*/
681-
main.emit_return(CodeGenContext::Get().num_tuples());
678+
run(); // call run function
679+
main.emit_return(CodeGenContext::Get().num_tuples()); // return size of result set
682680
}
683681

684682
/*----- Export main. ---------------------------------------------------------------------------------------------*/
@@ -719,21 +717,12 @@ void V8Engine::compile(const Operator &plan) const
719717
#endif
720718
}
721719

722-
void V8Engine::execute(const Operator &plan)
720+
void V8Engine::execute(const MatchBase &plan)
723721
{
724-
Module::Init();
725-
CodeGenContext::Init(); // fresh context
726-
727722
Catalog &C = Catalog::Get();
728723

729-
M_TIME_EXPR(phys_opt_->cover(plan), "Compute optimal physical operator covering", C.timer());
730-
if (Options::Get().physplan) phys_opt_->dump_plan();
731-
if (Options::Get().physplandot) {
732-
Diagnostic diag(Options::Get().has_color, std::cout, std::cerr);
733-
DotTool dot(diag);
734-
phys_opt_->dot_plan(dot.stream());
735-
dot.show("physical_plan", true);
736-
}
724+
Module::Init();
725+
CodeGenContext::Init(); // fresh context
737726

738727
M_insist(bool(isolate_), "must have an isolate");
739728
v8::Locker locker(isolate_);
@@ -755,7 +744,7 @@ void V8Engine::execute(const Operator &plan)
755744
WasmContext::config_t wasm_config{0};
756745
if (options::cdt_port < 1024)
757746
wasm_config |= WasmContext::TRAP_GUARD_PAGES;
758-
auto &wasm_context = Create_Wasm_Context_For_ID(Module::ID(), wasm_config, plan);
747+
auto &wasm_context = Create_Wasm_Context_For_ID(Module::ID(), plan, wasm_config);
759748

760749
auto imports = v8::Object::New(isolate_);
761750
auto env = create_env(*isolate_, plan);
@@ -795,10 +784,11 @@ void V8Engine::execute(const Operator &plan)
795784
"Execute machine code", C.timer());
796785

797786
/* Print total number of result tuples. */
798-
if (auto print_op = cast<const PrintOperator>(&plan)) {
787+
auto &root_op = plan.get_matched_singleton();
788+
if (auto print_op = cast<const PrintOperator>(&root_op)) {
799789
if (not Options::Get().quiet)
800790
print_op->out << num_rows << " rows\n";
801-
} else if (auto noop_op = cast<const NoOpOperator>(&plan)) {
791+
} else if (auto noop_op = cast<const NoOpOperator>(&root_op)) {
802792
if (not Options::Get().quiet)
803793
noop_op->out << num_rows << " rows\n";
804794
}
@@ -905,7 +895,7 @@ v8::Local<v8::WasmModuleObject> m::wasm::detail::instantiate(v8::Isolate &isolat
905895
->CallAsConstructor(Ctx, 2, instance_args).ToLocalChecked().As<v8::WasmModuleObject>();
906896
}
907897

908-
v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const Operator &plan)
898+
v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const MatchBase &plan)
909899
{
910900
auto &context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
911901
auto Ctx = isolate.GetCurrentContext();
@@ -931,7 +921,7 @@ v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const Op
931921

932922
/* Map all string literals into the Wasm module. */
933923
M_insist(Is_Page_Aligned(context.heap));
934-
auto literals = CollectStringLiterals::Collect(plan);
924+
auto literals = CollectStringLiterals::Collect(plan.get_matched_singleton());
935925
std::size_t bytes = 0;
936926
for (auto literal : literals)
937927
bytes += strlen(literal) + 1;

0 commit comments

Comments
 (0)