From 32eb209db0ddf800791756f8aca44d5076ffd112 Mon Sep 17 00:00:00 2001 From: Nadav Rotem Date: Fri, 7 Sep 2018 17:18:12 -0700 Subject: [PATCH] [Placeholder] Allow executing Placeholders in the execution engine. Implement the logic for executing Placeholders in the execution engine. This commit changes the backend interface and adds the ability to pass Placeholder variables in addition to Variables. This change breaks the out-of-tree backends. This is the first step in adding support for placeholder variables in the execution engine. The commit adds support to the interpreter but not to the OpenCL and CPU backends. --- include/glow/Backends/Backend.h | 7 +++++-- include/glow/Backends/CompiledFunction.h | 8 ++++++++ include/glow/ExecutionEngine/ExecutionEngine.h | 6 +++++- lib/Backends/CPU/CPUBackend.cpp | 3 ++- lib/Backends/CPU/CPUBackend.h | 3 ++- lib/Backends/Interpreter/Interpreter.cpp | 5 +++-- lib/Backends/Interpreter/Interpreter.h | 3 ++- .../Interpreter/InterpreterFunction.cpp | 15 ++++++++++----- lib/Backends/Interpreter/InterpreterFunction.h | 3 ++- lib/Backends/OpenCL/OpenCL.cpp | 3 ++- lib/Backends/OpenCL/OpenCL.h | 3 ++- lib/ExecutionEngine/ExecutionEngine.cpp | 15 +++++++++++++-- tests/unittests/BackendCorrectnessTest.cpp | 8 +++++--- tests/unittests/BackendTest.cpp | 18 +++++++++++++++++- tests/unittests/BackendTestUtils.h | 3 ++- tests/unittests/quantizationTest.cpp | 5 +++-- 16 files changed, 83 insertions(+), 25 deletions(-) diff --git a/include/glow/Backends/Backend.h b/include/glow/Backends/Backend.h index 101787861b..be8443c07f 100644 --- a/include/glow/Backends/Backend.h +++ b/include/glow/Backends/Backend.h @@ -39,9 +39,12 @@ class Backend { /// Dtor. virtual ~Backend() = default; - /// Generate code for input function \param IR. + /// Generate code for input function \param IR. \p placeholders is a list of + /// Placeholders that are mapped to the concrete input tensor for the + /// specific function. virtual std::unique_ptr - compile(std::unique_ptr IR) const = 0; + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const = 0; /// Save the bundle for \p IR for a later standalone execution /// in \p outputDir. Make \p networkName the function name for diff --git a/include/glow/Backends/CompiledFunction.h b/include/glow/Backends/CompiledFunction.h index 040981f003..d90efbb9a2 100644 --- a/include/glow/Backends/CompiledFunction.h +++ b/include/glow/Backends/CompiledFunction.h @@ -16,8 +16,16 @@ #ifndef GLOW_BACKENDS_COMPILEDFUNCTION_H #define GLOW_BACKENDS_COMPILEDFUNCTION_H +#include + namespace glow { +class Placeholder; +class Tensor; + +/// Maps placeholders to the tensors that back them. +using PlaceholderMap = std::unordered_map; + /// Interface for executing a compiled function. class CompiledFunction { public: diff --git a/include/glow/ExecutionEngine/ExecutionEngine.h b/include/glow/ExecutionEngine/ExecutionEngine.h index 458b3f2a06..59b7923a3e 100644 --- a/include/glow/ExecutionEngine/ExecutionEngine.h +++ b/include/glow/ExecutionEngine/ExecutionEngine.h @@ -67,7 +67,11 @@ class ExecutionEngine final { /// Optimize the graph, generate IR, optimize IR and compile it for a /// specific target. This method should be invoked before the run method. - void compile(CompilationMode mode, Function *F); + /// The placeholder variables in \p placeholders are mapped to the concrete + /// tensor values in the compiled instance of the function. + void compile(CompilationMode mode, Function *F, + llvm::ArrayRef placeholders = {}, + llvm::ArrayRef inputs = {}); /// Save a bundle for a standalone execution. This method takes care of /// everything when preparing the bundle for saving. There is no need to diff --git a/lib/Backends/CPU/CPUBackend.cpp b/lib/Backends/CPU/CPUBackend.cpp index 27084ca148..992347c421 100644 --- a/lib/Backends/CPU/CPUBackend.cpp +++ b/lib/Backends/CPU/CPUBackend.cpp @@ -123,7 +123,8 @@ CPUBackend::createIRGen(IRFunction *IR, } std::unique_ptr -CPUBackend::compile(std::unique_ptr IR) const { +CPUBackend::compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const { AllocationsInfo allocationsInfo; std::unique_ptr irgen = createIRGen(IR.get(), allocationsInfo); irgen->initTargetMachine(target.empty() ? "" : target.getValue(), diff --git a/lib/Backends/CPU/CPUBackend.h b/lib/Backends/CPU/CPUBackend.h index e2887691e2..f6f16da076 100644 --- a/lib/Backends/CPU/CPUBackend.h +++ b/lib/Backends/CPU/CPUBackend.h @@ -43,7 +43,8 @@ class CPUBackend : public Backend { ~CPUBackend() override = default; std::unique_ptr - compile(std::unique_ptr IR) const override; + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override; void save(std::unique_ptr IR, llvm::StringRef outputDir, llvm::StringRef networkName) const override; diff --git a/lib/Backends/Interpreter/Interpreter.cpp b/lib/Backends/Interpreter/Interpreter.cpp index 299251273d..ee446f1598 100644 --- a/lib/Backends/Interpreter/Interpreter.cpp +++ b/lib/Backends/Interpreter/Interpreter.cpp @@ -24,8 +24,9 @@ using namespace glow; std::unique_ptr -Interpreter::compile(std::unique_ptr IR) const { - return llvm::make_unique(std::move(IR)); +Interpreter::compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const { + return llvm::make_unique(std::move(IR), placeholders); } bool Interpreter::isOpSupported(Kinded::Kind opKind, ElemKind elementTy) const { diff --git a/lib/Backends/Interpreter/Interpreter.h b/lib/Backends/Interpreter/Interpreter.h index 6cc847c7bf..9914ab9f77 100644 --- a/lib/Backends/Interpreter/Interpreter.h +++ b/lib/Backends/Interpreter/Interpreter.h @@ -35,7 +35,8 @@ class Interpreter final : public Backend { ~Interpreter() override = default; std::unique_ptr - compile(std::unique_ptr IR) const override; + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override; bool isOpSupported(Kinded::Kind opKind, ElemKind elementTy) const override; diff --git a/lib/Backends/Interpreter/InterpreterFunction.cpp b/lib/Backends/Interpreter/InterpreterFunction.cpp index 2e48a89acb..b9454340eb 100644 --- a/lib/Backends/Interpreter/InterpreterFunction.cpp +++ b/lib/Backends/Interpreter/InterpreterFunction.cpp @@ -24,17 +24,22 @@ using namespace glow; -InterpreterFunction::InterpreterFunction(std::unique_ptr F) +InterpreterFunction::InterpreterFunction(std::unique_ptr F, + const PlaceholderMap &placeholders) : F_(std::move(F)) { + + // Register the concrete tensors that back the placeholder tensors. + for (auto &ph : placeholders) { + auto *w = F_->getWeightForNode(ph.first); + assert(!externalTensors_.count(w) && "The tensor is already registered"); + externalTensors_[w] = ph.second; + } + for (auto &v : F_->getGraph()->getParent()->getVars()) { auto *w = F_->getWeightForNode(v); assert(!externalTensors_.count(w) && "The tensor is already registered"); externalTensors_[w] = &v->getPayload(); } - - for (auto *W : F_->getWeights()) { - getOrCreateTensor(W); - } } InterpreterFunction::~InterpreterFunction() { diff --git a/lib/Backends/Interpreter/InterpreterFunction.h b/lib/Backends/Interpreter/InterpreterFunction.h index aa97d98265..222e93affd 100644 --- a/lib/Backends/Interpreter/InterpreterFunction.h +++ b/lib/Backends/Interpreter/InterpreterFunction.h @@ -48,7 +48,8 @@ class InterpreterFunction final : public CompiledFunction { std::unordered_map externalTensors_; public: - InterpreterFunction(std::unique_ptr F); + InterpreterFunction(std::unique_ptr F, + const PlaceholderMap &placeholders); /// \name CompiledFunction interface ///@{ diff --git a/lib/Backends/OpenCL/OpenCL.cpp b/lib/Backends/OpenCL/OpenCL.cpp index d7ac955625..63ba9e5e23 100644 --- a/lib/Backends/OpenCL/OpenCL.cpp +++ b/lib/Backends/OpenCL/OpenCL.cpp @@ -1571,6 +1571,7 @@ cl_mem OpenCLFunction::allocDeviceBuffer(uint64_t size) { void OpenCLFunction::freeDeviceBuffer(cl_mem buf) { clReleaseMemObject(buf); } std::unique_ptr -OCLBackend::compile(std::unique_ptr IR) const { +OCLBackend::compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const { return llvm::make_unique(std::move(IR)); } diff --git a/lib/Backends/OpenCL/OpenCL.h b/lib/Backends/OpenCL/OpenCL.h index b9f5f9c540..7eb08e13d0 100644 --- a/lib/Backends/OpenCL/OpenCL.h +++ b/lib/Backends/OpenCL/OpenCL.h @@ -171,7 +171,8 @@ class OCLBackend final : public Backend { ~OCLBackend() override = default; std::unique_ptr - compile(std::unique_ptr IR) const override; + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override; bool transformPostLowering(Function *F, CompilationMode mode) const override; diff --git a/lib/ExecutionEngine/ExecutionEngine.cpp b/lib/ExecutionEngine/ExecutionEngine.cpp index a54aa622ba..f62f76a097 100644 --- a/lib/ExecutionEngine/ExecutionEngine.cpp +++ b/lib/ExecutionEngine/ExecutionEngine.cpp @@ -169,8 +169,19 @@ std::unique_ptr ExecutionEngine::generateIR(CompilationMode mode, return IR; } -void ExecutionEngine::compile(CompilationMode mode, Function *F) { - function_ = backend_->compile(generateIR(mode, F)); +void ExecutionEngine::compile(CompilationMode mode, Function *F, + llvm::ArrayRef placeholders, + llvm::ArrayRef inputs) { + PlaceholderMap pmap; + assert(placeholders.size() == inputs.size() && + "Invalid number of placeholders"); + + for (size_t i = 0, e = placeholders.size(); i < e; i++) { + pmap[placeholders[i]] = inputs[i]; + } + + auto IR = generateIR(mode, F); + function_ = backend_->compile(std::move(IR), pmap); } void ExecutionEngine::save(CompilationMode mode, Function *F, diff --git a/tests/unittests/BackendCorrectnessTest.cpp b/tests/unittests/BackendCorrectnessTest.cpp index a15d387ec2..ab5f8dcd8c 100644 --- a/tests/unittests/BackendCorrectnessTest.cpp +++ b/tests/unittests/BackendCorrectnessTest.cpp @@ -239,8 +239,9 @@ class MockCPUBackend : public Backend { public: MockCPUBackend() { backend_.reset(createBackend(BackendKind::CPU)); } std::unique_ptr - compile(std::unique_ptr IR) const override { - return backend_->compile(std::move(IR)); + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override { + return backend_->compile(std::move(IR), placeholders); } bool isOpSupported(Kinded::Kind opKind, ElemKind elementTy) const override { return true; @@ -304,7 +305,8 @@ TEST_P(CPUOnly, dataParallelStackingTest) { } MockCPUBackend backend; - backend.compile(std::move(M))->execute(); + PlaceholderMap empty; + backend.compile(std::move(M), empty)->execute(); auto H = var->getHandle(); EXPECT_EQ(H.at(0), 3); EXPECT_EQ(H.at(1), 4); diff --git a/tests/unittests/BackendTest.cpp b/tests/unittests/BackendTest.cpp index d9ce593183..af3ad4b7f8 100644 --- a/tests/unittests/BackendTest.cpp +++ b/tests/unittests/BackendTest.cpp @@ -145,7 +145,8 @@ TEST_P(BackendTest, debugPrint) { IRBuilder(IR.get()).createDebugPrintInst("print", *IR->getWeights().begin()); std::unique_ptr backend(createBackend(GetParam())); - auto function = backend->compile(std::move(IR)); + PlaceholderMap empty; + auto function = backend->compile(std::move(IR), empty); function->execute(); } @@ -176,6 +177,21 @@ TEST_P(BackendTest, decoupleCodegenFromGraph) { EXPECT_NEAR(HX.at({2}), 9, 1E-5); } +/// Check that we can pass information to the execution engine using Placeholder +/// variables and read it back using Save nodes (in variables). +TEST(Placeholder, simplePlaceholderValue) { + Tensor data{99.0, 35.0, 2.0, 3.0}; + ExecutionEngine EE{BackendKind::Interpreter}; + auto &mod = EE.getModule(); + Function *F = mod.createFunction("main"); + auto *input = mod.createPlaceholder(ElemKind::FloatTy, {4}, "input"); + SaveNode *S = F->createSave("ret", input); + EE.compile(CompilationMode::Infer, F, {input}, {&data}); + EE.run(); + auto &res = S->getVariable()->getPayload(); + EXPECT_TRUE(res.isEqual(data)); +} + INSTANTIATE_TEST_CASE_P(Interpreter, BackendTest, ::testing::Values(BackendKind::Interpreter)); diff --git a/tests/unittests/BackendTestUtils.h b/tests/unittests/BackendTestUtils.h index f96e0b1d5c..8deca0a287 100644 --- a/tests/unittests/BackendTestUtils.h +++ b/tests/unittests/BackendTestUtils.h @@ -26,7 +26,8 @@ class MockBackend : public Backend { void execute() override {} }; std::unique_ptr - compile(std::unique_ptr IR) const override { + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override { return llvm::make_unique(); } bool isOpSupported(Kinded::Kind opKind, ElemKind elementTy) const override { diff --git a/tests/unittests/quantizationTest.cpp b/tests/unittests/quantizationTest.cpp index 53c9d1bc40..302752caa9 100644 --- a/tests/unittests/quantizationTest.cpp +++ b/tests/unittests/quantizationTest.cpp @@ -615,8 +615,9 @@ class MockQuantBackend : public Backend { backend_.reset(createBackend(BackendKind::Interpreter)); } std::unique_ptr - compile(std::unique_ptr IR) const override { - return backend_->compile(std::move(IR)); + compile(std::unique_ptr IR, + const PlaceholderMap &placeholders) const override { + return backend_->compile(std::move(IR), placeholders); } bool isOpSupported(Kinded::Kind opKind, ElemKind elementTy) const override { if (opKind == Kinded::Kind::SoftMaxNodeKind ||