未验证 提交 fdc4fe3b 编写于 作者: T TeFeng Chen 提交者: GitHub

cinn_instruction_run_op test (#39576)

* add cinn_instruction_run_op test code

* update several interfaces of CinnLaunchContext

* update several interfaces and add detail comments in CinnLaunchContext class

* to skip the bug of error message check

* fix ut test failed due to reliant interface updated
上级 5c66338f
...@@ -10,6 +10,10 @@ if (WITH_TESTING) ...@@ -10,6 +10,10 @@ if (WITH_TESTING)
cc_test(cinn_launch_context_test SRCS cinn_launch_context_test.cc DEPS ddim lod_tensor scope cinn_launch_context) cc_test(cinn_launch_context_test SRCS cinn_launch_context_test.cc DEPS ddim lod_tensor scope cinn_launch_context)
set_tests_properties(cinn_launch_context_test PROPERTIES LABELS "RUN_TYPE=CINN") set_tests_properties(cinn_launch_context_test PROPERTIES LABELS "RUN_TYPE=CINN")
SET(CINN_RUN_ENVIRONMENT "OMP_NUM_THREADS=1;runtime_include_dir=${PADDLE_BINARY_DIR}/third_party/CINN/src/external_cinn/cinn/runtime/cuda")
cc_test(cinn_launch_op_test SRCS cinn_launch_op_test.cc DEPS cinn_compiler cinn_launch_op elementwise_add_op) cc_test(cinn_launch_op_test SRCS cinn_launch_op_test.cc DEPS cinn_compiler cinn_launch_op elementwise_add_op)
set_tests_properties(cinn_launch_op_test PROPERTIES LABELS "RUN_TYPE=CINN" ENVIRONMENT "OMP_NUM_THREADS=1;runtime_include_dir=${PADDLE_BINARY_DIR}/third_party/CINN/src/external_cinn/cinn/runtime/cuda") set_tests_properties(cinn_launch_op_test PROPERTIES LABELS "RUN_TYPE=CINN" ENVIRONMENT "${CINN_RUN_ENVIRONMENT}")
cc_test(cinn_instruction_run_op_test SRCS cinn_instruction_run_op_test.cc DEPS cinn_compiler cinn_launch_op cinn_instruction_run_op elementwise_add_op)
set_tests_properties(cinn_instruction_run_op_test PROPERTIES LABELS "RUN_TYPE=CINN" ENVIRONMENT "${CINN_RUN_ENVIRONMENT}")
endif() endif()
/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <stdlib.h>
#include <string>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/paddle2cinn/cinn_compiler.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/operators/cinn/test_helper.h"
#include "paddle/fluid/platform/cpu_helper.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/fluid/platform/init.h"
USE_OP(cinn_launch);
USE_OP(cinn_instruction_run);
USE_OP_ITSELF(elementwise_add);
namespace paddle::operators {
using framework::paddle2cinn::CinnCompiler;
TEST(CinnInstructionOpTest, TestWithElementwiseAdd) {
paddle::framework::InitDevices();
platform::SetNumThreads(1);
// cache test graph into CinnCompiler
const std::string& test_op_out_name = "cinn_instruction_run_op_out";
const std::string& add_op_out_name = "add_op_out";
auto compilation_key = CinnCompiler::GetInstance()->AddGraph(
CreateOnlyElementwiseAddGraph("x", "y", test_op_out_name));
// create a cinn_launch_op and run firstly to launch the compilation
// of the above graph and cache the compiled object in CinnCompiler
auto cinn_launch_op = paddle::framework::OpRegistry::CreateOp(
"cinn_launch", {{"X", {"x", "y"}}}, {{"Out", {test_op_out_name}}},
{{"compilation_key", compilation_key}});
// create cinn_instruction_run_op and elementwise_add op
auto cinn_instruction_run_op = paddle::framework::OpRegistry::CreateOp(
"cinn_instruction_run", {{"X", {"x", "y"}}},
{{"Out", {test_op_out_name}}},
{{"cached_index", 0}, {"instruction_index", 1}});
auto elementwise_add_op = paddle::framework::OpRegistry::CreateOp(
"elementwise_add", {{"X", {"x"}}, {"Y", {"y"}}},
{{"Out", {add_op_out_name}}}, {{}});
// check case: a compiled object not cached before cinn_launch_op run,
// so a cinn_instruction_run_op will throw an error
framework::Scope scope;
platform::CPUPlace place;
InitVariablesWithRandomValue<float>({"x", "y"}, {10, 20}, place, &scope);
scope.Var(test_op_out_name)->GetMutable<LoDTensor>();
ASSERT_THROW(cinn_instruction_run_op->Run(scope, place),
paddle::platform::EnforceNotMet);
cinn_launch_op->Run(scope, place);
// Run ops and check the computation results
auto run_and_check_fn = [&](const platform::Place& place) {
framework::Scope scope;
InitVariablesWithRandomValue<float>({"x", "y"}, {10, 20}, place, &scope);
scope.Var(test_op_out_name)->GetMutable<LoDTensor>();
scope.Var(add_op_out_name)->GetMutable<LoDTensor>();
cinn_instruction_run_op->Run(scope, place);
elementwise_add_op->Run(scope, place);
CompareOpResult<float>(scope.GetVar(test_op_out_name),
scope.GetVar(add_op_out_name));
};
// CPU
run_and_check_fn(platform::CPUPlace());
run_and_check_fn(platform::CPUPlace());
#ifdef PADDLE_WITH_CUDA
// GPU
run_and_check_fn(platform::CUDAPlace());
run_and_check_fn(platform::CUDAPlace());
#endif
}
} // namespace paddle::operators
...@@ -13,26 +13,47 @@ ...@@ -13,26 +13,47 @@
// limitations under the License. // limitations under the License.
#include "paddle/fluid/operators/cinn/cinn_launch_context.h" #include "paddle/fluid/operators/cinn/cinn_launch_context.h"
#include <algorithm>
#include <functional> #include <functional>
#include <utility>
#include <vector> #include <vector>
#include "cinn/hlir/framework/scope.h"
#include "cinn/hlir/framework/tensor.h"
#include "cinn/runtime/cinn_runtime.h"
#include "paddle/fluid/framework/ddim.h"
#include "paddle/fluid/string/printf.h"
namespace paddle { namespace paddle {
namespace operators { namespace operators::details {
namespace details {
using LoDTensor = framework::LoDTensor;
CinnLaunchContext::CinnLaunchContext( CinnLaunchContext::CinnLaunchContext(
const std::unordered_map<std::string, std::string>& paddle2cinn_varmap, const std::unordered_map<std::string, std::string>& paddle2cinn_varmap,
const std::shared_ptr<CinnScope>& cinn_scope) const std::shared_ptr<CinnScope>& cinn_scope)
: paddle2cinn_varmap_(paddle2cinn_varmap), cinn_scope_(cinn_scope) { : cinn_scope_(cinn_scope) {
// generate all names of cinn used variables // generate all names of the cinn execution arguments
auto var_names = cinn_scope_->var_names(); auto var_names = cinn_scope_->var_names();
cinn_variable_names_.reserve(var_names.size()); cinn_argument_names_.reserve(var_names.size());
std::transform( std::transform(
var_names.begin(), var_names.end(), var_names.begin(), var_names.end(),
std::inserter(cinn_variable_names_, cinn_variable_names_.end()), std::inserter(cinn_argument_names_, cinn_argument_names_.end()),
[](const auto& name_view) { return std::string(name_view.data()); }); [](const auto& name_view) { return std::string(name_view.data()); });
// build the variable name map of cinn2paddle // build name map between the original variables and compiled ones
for (const auto& x : paddle2cinn_varmap_) { BuildVarNameMap(paddle2cinn_varmap, cinn_argument_names_);
}
void CinnLaunchContext::BuildVarNameMap(
const std::unordered_map<std::string, std::string>& compiled_varmap,
const std::unordered_set<std::string>& argument_names) {
for (const auto& x : compiled_varmap) {
if (!argument_names.count(x.second)) {
// exclude variables not used
continue;
}
// copy to local paddle2cinn map
paddle2cinn_varmap_.emplace(x.first, x.second);
// add an entry to local cinn2paddle map reversely
auto res = cinn2paddle_varmap_.emplace(x.second, x.first); auto res = cinn2paddle_varmap_.emplace(x.second, x.first);
PADDLE_ENFORCE_EQ( PADDLE_ENFORCE_EQ(
res.second, true, res.second, true,
...@@ -40,15 +61,21 @@ CinnLaunchContext::CinnLaunchContext( ...@@ -40,15 +61,21 @@ CinnLaunchContext::CinnLaunchContext(
"Cinn variable(%s) maps to more than one paddle variable(%s,%s)", "Cinn variable(%s) maps to more than one paddle variable(%s,%s)",
x.second, res.first->second, x.first)); x.second, res.first->second, x.first));
} }
// supplement the relations of the remain variables not appearing in above // supplement the relations of the remain variables
// map, // not appearing in above map, which are internal variables
// they are internal variables and here we use the name from cinn compiled. // and here we use the names from cinn compiled.
for (const auto& var_name : cinn_variable_names_) { for (const auto& var_name : argument_names) {
if (!cinn2paddle_varmap_.count(var_name)) { if (!cinn2paddle_varmap_.count(var_name)) {
cinn2paddle_varmap_.emplace(var_name, var_name); cinn2paddle_varmap_.emplace(var_name, var_name);
paddle2cinn_varmap_.emplace(var_name, var_name); paddle2cinn_varmap_.emplace(var_name, var_name);
} }
} }
PADDLE_ENFORCE_EQ(
paddle2cinn_varmap_.size(), cinn2paddle_varmap_.size(),
platform::errors::PreconditionNotMet(
"Size of variables is not euqal, paddle[%ld] vs cinn[%ld]",
paddle2cinn_varmap_.size(), cinn2paddle_varmap_.size()));
} }
void CinnLaunchContext::UpdateCapturedEnv(const framework::Scope& scope, void CinnLaunchContext::UpdateCapturedEnv(const framework::Scope& scope,
...@@ -74,31 +101,39 @@ bool CinnLaunchContext::IsArgumentsInitialized() const { ...@@ -74,31 +101,39 @@ bool CinnLaunchContext::IsArgumentsInitialized() const {
return true; return true;
} }
bool CinnLaunchContext::IsVariableUsed( bool CinnLaunchContext::IsVariableUsed(const std::string& var_name) const {
const std::string& paddle_var_name) const { return paddle2cinn_varmap_.count(var_name) > 0;
return paddle2cinn_varmap_.count(paddle_var_name) > 0 &&
cinn_variable_names_.count(paddle2cinn_varmap_.at(paddle_var_name)) >
0;
} }
CinnTensor CinnLaunchContext::GetCinnTensor(const std::string& var_name) { CinnTensor CinnLaunchContext::GetCinnTensor(const std::string& arg_name) {
PADDLE_ENFORCE_GT(cinn_variable_names_.count(var_name), 0, PADDLE_ENFORCE_GT(cinn_argument_names_.count(arg_name), 0,
platform::errors::NotFound( platform::errors::InvalidArgument(
"Variable(%s) not found in cinn scope.", var_name)); "Variable(%s) not found in cinn scope.", arg_name));
return cinn_scope_->GetTensor(var_name); return cinn_scope_->GetTensor(arg_name);
} }
std::unordered_set<std::string> CinnLaunchContext::GetInternalVariableNames() { std::unordered_set<std::string> CinnLaunchContext::ExtractInternalVarNames(
std::unordered_set<std::string> all_parameters(cinn_variable_names_); const std::vector<std::string>& input_var_names,
std::for_each(name2argument_.begin(), name2argument_.end(), const std::vector<std::string>& output_var_names) {
[&all_parameters](const auto& name2arg) { std::unordered_set<std::string> remain_var_names;
all_parameters.erase(name2arg.first); remain_var_names.reserve(paddle2cinn_varmap_.size());
}); std::transform(paddle2cinn_varmap_.begin(), paddle2cinn_varmap_.end(),
return all_parameters; std::inserter(remain_var_names, remain_var_names.end()),
[](const auto& name_pair) { return name_pair.first; });
// exclude the input variables and output variables
auto exclude_names_fn = [&remain_var_names](const std::string& var_name) {
remain_var_names.erase(var_name);
};
std::for_each(input_var_names.begin(), input_var_names.end(),
exclude_names_fn);
std::for_each(output_var_names.begin(), output_var_names.end(),
exclude_names_fn);
return remain_var_names;
} }
void CinnLaunchContext::CheckTensorEquivalent( void CinnLaunchContext::CheckTensorEquivalent(const std::string& var_name,
const std::string& paddle_var_name, const LoDTensor& paddle_tensor, const LoDTensor& paddle_tensor,
const CinnTensor& cinn_tensor) { const CinnTensor& cinn_tensor) {
// check dimension // check dimension
auto cinn_dims = framework::make_ddim(cinn_tensor->shape().data()); auto cinn_dims = framework::make_ddim(cinn_tensor->shape().data());
...@@ -106,24 +141,21 @@ void CinnLaunchContext::CheckTensorEquivalent( ...@@ -106,24 +141,21 @@ void CinnLaunchContext::CheckTensorEquivalent(
platform::errors::PreconditionNotMet( platform::errors::PreconditionNotMet(
"Tensors' shape in variable(%s) are not equivalent, " "Tensors' shape in variable(%s) are not equivalent, "
"paddle's shape = [%s], but cinn's shape = [%s].", "paddle's shape = [%s], but cinn's shape = [%s].",
paddle_var_name, paddle_tensor.dims(), cinn_dims)); var_name, paddle_tensor.dims(), cinn_dims));
// TODO(CtfGo): check the underlying data type after CINN ready // TODO(CtfGo): check the underlying data type after CINN ready
} }
void CinnLaunchContext::AssignExternalVariable( void CinnLaunchContext::AssignExternalVariable(const std::string& var_name) {
const std::string& paddle_var_name) { PADDLE_ENFORCE_EQ(IsVariableUsed(var_name), true,
PADDLE_ENFORCE_EQ( platform::errors::InvalidArgument(
IsVariableUsed(paddle_var_name), true, "Variable(%s) not applied in cinn", var_name));
platform::errors::InvalidArgument("Paddle variable(%s) not used by cinn", const auto& cinn_arg_name = paddle2cinn_varmap_.at(var_name);
paddle_var_name));
const auto& paddle_tensor = cached_scope_->GetVar(var_name)->Get<LoDTensor>();
const auto& cinn_var_name = paddle2cinn_varmap_.at(paddle_var_name); CinnTensor cinn_tensor = GetCinnTensor(cinn_arg_name);
const auto& paddle_tensor =
cached_scope_->GetVar(paddle_var_name)->Get<LoDTensor>();
CinnTensor cinn_tensor = GetCinnTensor(cinn_var_name);
if (paddle_tensor.IsInitialized()) { if (paddle_tensor.IsInitialized()) {
CheckTensorEquivalent(paddle_var_name, paddle_tensor, cinn_tensor); CheckTensorEquivalent(var_name, paddle_tensor, cinn_tensor);
} }
auto cinn_buffer = std::make_unique<cinn_buffer_t>(); auto cinn_buffer = std::make_unique<cinn_buffer_t>();
...@@ -131,9 +163,8 @@ void CinnLaunchContext::AssignExternalVariable( ...@@ -131,9 +163,8 @@ void CinnLaunchContext::AssignExternalVariable(
cinn_buffer->resize(cinn_tensor->shape().data().data(), cinn_buffer->resize(cinn_tensor->shape().data().data(),
cinn_tensor->shape().data().size()); cinn_tensor->shape().data().size());
cinn_buffer->external_malloc = new std::function<int(void*, cinn_buffer_t*)>( cinn_buffer->external_malloc = new std::function<int(void*, cinn_buffer_t*)>(
[this, paddle_var_name](void* ctx, cinn_buffer_t* buffer) { [this, var_name](void* ctx, cinn_buffer_t* buffer) {
auto* tensor = auto* tensor = cached_scope_->GetVar(var_name)->GetMutable<LoDTensor>();
cached_scope_->GetVar(paddle_var_name)->GetMutable<LoDTensor>();
tensor->Resize(framework::DDim(buffer->dims, buffer->dimensions)); tensor->Resize(framework::DDim(buffer->dims, buffer->dimensions));
buffer->memory = reinterpret_cast<uint8_t*>( buffer->memory = reinterpret_cast<uint8_t*>(
tensor->mutable_data<float>(*cached_place_)); tensor->mutable_data<float>(*cached_place_));
...@@ -147,25 +178,25 @@ void CinnLaunchContext::AssignExternalVariable( ...@@ -147,25 +178,25 @@ void CinnLaunchContext::AssignExternalVariable(
return 0; return 0;
}); });
return SetArgument(cinn_var_name, std::move(cinn_buffer)); return AppendArgument(cinn_arg_name, std::move(cinn_buffer));
} }
void CinnLaunchContext::AssignInternalVariable( void CinnLaunchContext::AssignInternalVariable(const std::string& var_name) {
const std::string& cinn_var_name) { PADDLE_ENFORCE_EQ(IsVariableUsed(var_name), true,
PADDLE_ENFORCE_GT( platform::errors::InvalidArgument(
cinn_variable_names_.count(cinn_var_name), 0, "Variable(%s) not applied in cinn", var_name));
platform::errors::InvalidArgument("Variable(%s) not found in cinn socpe.", const auto& cinn_arg_name = paddle2cinn_varmap_.at(var_name);
cinn_var_name));
CinnTensor cinn_tensor = GetCinnTensor(cinn_var_name); CinnTensor cinn_tensor = GetCinnTensor(cinn_arg_name);
auto cinn_buffer = std::make_unique<cinn_buffer_t>(); auto cinn_buffer = std::make_unique<cinn_buffer_t>();
// assign dimensions and alloc/free callback of cinn_buffer_t // assign dimensions and alloc/free callback of cinn_buffer_t
cinn_buffer->resize(cinn_tensor->shape().data().data(), cinn_buffer->resize(cinn_tensor->shape().data().data(),
cinn_tensor->shape().data().size()); cinn_tensor->shape().data().size());
cinn_buffer->external_malloc = new std::function<int(void*, cinn_buffer_t*)>( cinn_buffer->external_malloc = new std::function<int(void*, cinn_buffer_t*)>(
[this, cinn_var_name](void* ctx, cinn_buffer_t* buffer) { [this, var_name](void* ctx, cinn_buffer_t* buffer) {
auto* tensor = auto* tensor =
cached_temp_scope_->Var(cinn_var_name)->GetMutable<LoDTensor>(); cached_temp_scope_->Var(var_name)->GetMutable<LoDTensor>();
tensor->Resize(framework::DDim(buffer->dims, buffer->dimensions)); tensor->Resize(framework::DDim(buffer->dims, buffer->dimensions));
buffer->memory = reinterpret_cast<uint8_t*>( buffer->memory = reinterpret_cast<uint8_t*>(
tensor->mutable_data<float>(*cached_place_)); tensor->mutable_data<float>(*cached_place_));
...@@ -175,53 +206,51 @@ void CinnLaunchContext::AssignInternalVariable( ...@@ -175,53 +206,51 @@ void CinnLaunchContext::AssignInternalVariable(
// internal variables should release its buffer immediately // internal variables should release its buffer immediately
// if no instruction use it // if no instruction use it
cinn_buffer->external_free = new std::function<int(void*, cinn_buffer_t*)>( cinn_buffer->external_free = new std::function<int(void*, cinn_buffer_t*)>(
[this, cinn_var_name](void* ctx, cinn_buffer_t* buffer) { [this, var_name](void* ctx, cinn_buffer_t* buffer) {
auto* tensor = auto* tensor =
cached_temp_scope_->GetVar(cinn_var_name)->GetMutable<LoDTensor>(); cached_temp_scope_->GetVar(var_name)->GetMutable<LoDTensor>();
tensor->clear(); tensor->clear();
return 0; return 0;
}); });
return SetArgument(cinn_var_name, std::move(cinn_buffer)); return AppendArgument(cinn_arg_name, std::move(cinn_buffer));
} }
void CinnLaunchContext::SetArgument(const std::string& cinn_var_name, void CinnLaunchContext::AppendArgument(
std::unique_ptr<cinn_buffer_t>&& buffer) { const std::string& arg_name, std::unique_ptr<cinn_buffer_t>&& buffer) {
VLOG(4) << "SetArgument-" << name2argument_.size() << ": name(" name2argument_.emplace(arg_name, buffer.get());
<< cinn_var_name << "), dims("
<< framework::DDim(buffer->dims, buffer->dimensions) << ").";
name2argument_.emplace(cinn_var_name, buffer.get());
hold_buffers_.emplace_back(std::move(buffer)); hold_buffers_.emplace_back(std::move(buffer));
VLOG(4) << string::Sprintf(
"Append an argument:name(%s),dims(%s),argument size:(%lu)", arg_name,
framework::DDim(buffer->dims, buffer->dimensions).to_str(),
name2argument_.size());
} }
const std::map<std::string, cinn_pod_value_t>& const std::map<std::string, cinn_pod_value_t>&
CinnLaunchContext::FinalizeArguments() const { CinnLaunchContext::FinalizeArguments() const {
// Check all execution parameters are assigned valued. // Check all execution parameters are assigned valued.
std::for_each(cinn_variable_names_.begin(), cinn_variable_names_.end(), std::for_each(cinn_argument_names_.begin(), cinn_argument_names_.end(),
[this](const auto& var_name) { [this](const auto& arg_name) {
PADDLE_ENFORCE_GT(name2argument_.count(var_name), 0, PADDLE_ENFORCE_GT(
platform::errors::InvalidArgument( name2argument_.count(arg_name), 0,
"Variable(%s) is missed for launching " platform::errors::NotFound(
"compiled program execution", "Argument(%s) is missed for execution", arg_name));
var_name));
}); });
return name2argument_; return name2argument_;
} }
cinn_buffer_t* CinnLaunchContext::GetCinnBufferOfVar( cinn_buffer_t* CinnLaunchContext::GetCinnBufferOfVar(
const std::string& paddle_var_name) { const std::string& var_name) {
auto res = paddle2cinn_varmap_.find(paddle_var_name); auto it = paddle2cinn_varmap_.find(var_name);
PADDLE_ENFORCE_NE( PADDLE_ENFORCE_NE(
res, paddle2cinn_varmap_.end(), it, paddle2cinn_varmap_.end(),
platform::errors::InvalidArgument(
"Variable(%s) not found in compilation result", paddle_var_name));
auto it = name2argument_.find(res->second);
PADDLE_ENFORCE_NE(it, name2argument_.end(),
platform::errors::InvalidArgument( platform::errors::InvalidArgument(
"Argument(%s) not be initialized", res->second)); "Variable(%s) not found in compilation result", var_name));
return static_cast<cinn_buffer_t*>(it->second); auto res = name2argument_.find(it->second);
PADDLE_ENFORCE_NE(res, name2argument_.end(),
platform::errors::NotFound(
"Argument(%s) not be initialized", it->second));
return static_cast<cinn_buffer_t*>(res->second);
} }
} // namespace details } // namespace operators::details
} // namespace operators
} // namespace paddle } // namespace paddle
...@@ -19,21 +19,33 @@ ...@@ -19,21 +19,33 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "cinn/hlir/framework/scope.h" #include <vector>
#include "cinn/hlir/framework/tensor.h"
#include "cinn/runtime/cinn_runtime.h"
#include "paddle/fluid/framework/ddim.h"
#include "paddle/fluid/framework/lod_tensor.h" #include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/scope.h" #include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/platform/place.h"
// type declaration forward
struct cinn_buffer_t;
struct cinn_pod_value_t;
namespace cinn::hlir::framework {
class Tensor;
class Scope;
class Program;
} // namespace cinn::hlir::framework
namespace paddle { namespace paddle {
namespace operators { namespace operators::details {
namespace details {
using LoDTensor = framework::LoDTensor;
using CinnTensor = ::cinn::hlir::framework::Tensor; using CinnTensor = ::cinn::hlir::framework::Tensor;
using CinnScope = ::cinn::hlir::framework::Scope; using CinnScope = ::cinn::hlir::framework::Scope;
// This class is used to cache some reusable data among repeated
// executions for efficiency and it also provides easy interfaces
// to get details of the compilation result.
// A object of this class is constructed and saved in the
// compilation cache once a graph compiled by CINN.
// Generally speaking, here, a variable is refer to a Paddle
// Variable while a CINN variable is called an Argument.
class CinnLaunchContext { class CinnLaunchContext {
public: public:
explicit CinnLaunchContext( explicit CinnLaunchContext(
...@@ -48,36 +60,45 @@ class CinnLaunchContext { ...@@ -48,36 +60,45 @@ class CinnLaunchContext {
// Return whether execution arguments has been initialized // Return whether execution arguments has been initialized
bool IsArgumentsInitialized() const; bool IsArgumentsInitialized() const;
// Return whether a Paddle variable used on compiled kernels // Return whether a Paddle variable used in cinn execution
bool IsVariableUsed(const std::string& paddle_var_name) const; bool IsVariableUsed(const std::string& var_name) const;
// Assign tensor buffer to input or output variables // Assign tensor buffer to input or output variables
void AssignExternalVariable(const std::string& paddle_var_name); void AssignExternalVariable(const std::string& var_name);
// Assign tensor buffer to internal variables // Assign tensor buffer to internal variables
void AssignInternalVariable(const std::string& cinn_var_name); void AssignInternalVariable(const std::string& var_name);
// Extract internal variable names from CinnScope // Extract internal variable names from all applied variables
// by excluding used input and output variables // in execution by excluding the input and output variables
std::unordered_set<std::string> GetInternalVariableNames(); std::unordered_set<std::string> ExtractInternalVarNames(
const std::vector<std::string>& input_var_names,
const std::vector<std::string>& output_var_names);
// Finalize all execution arguments and return them // Finalize all execution arguments and return the name->argument map
const std::map<std::string, cinn_pod_value_t>& FinalizeArguments() const; const std::map<std::string, cinn_pod_value_t>& FinalizeArguments() const;
cinn_buffer_t* GetCinnBufferOfVar(const std::string& paddle_var_name); // Return the cinn_buffer_t* of a specific variable
cinn_buffer_t* GetCinnBufferOfVar(const std::string& var_name);
private: private:
// Get CinnTensor with CINN variable name // Get CinnTensor with CINN argument name
CinnTensor GetCinnTensor(const std::string& var_name); CinnTensor GetCinnTensor(const std::string& arg_name);
// Build the name maps of paddle->cinn and cinn->paddle
// Check whether tensors from Paddle and CINN of the same variable // in reverse for all variables used in cinn execution
void BuildVarNameMap(
const std::unordered_map<std::string, std::string>& compiled_varmap,
const std::unordered_set<std::string>& argument_names);
// Check whether the tensor in Paddle and the compiled
// tensor returned by CINN of a same variable
// are equivalent in type and dimension // are equivalent in type and dimension
void CheckTensorEquivalent(const std::string& var_name, void CheckTensorEquivalent(const std::string& var_name,
const LoDTensor& paddle_tensor, const framework::LoDTensor& paddle_tensor,
const CinnTensor& cinn_tensor); const CinnTensor& cinn_tensor);
// Set an argument with (cinn name)->(cinn_buffer_t) pair // Append an argument with (cinn name)->(cinn_buffer_t) pair
void SetArgument(const std::string& cinn_var_name, void AppendArgument(const std::string& arg_name,
std::unique_ptr<cinn_buffer_t>&& buffer); std::unique_ptr<cinn_buffer_t>&& buffer);
private: private:
...@@ -85,18 +106,17 @@ class CinnLaunchContext { ...@@ -85,18 +106,17 @@ class CinnLaunchContext {
const platform::Place* cached_place_ = nullptr; const platform::Place* cached_place_ = nullptr;
std::unique_ptr<framework::Scope> cached_temp_scope_ = nullptr; std::unique_ptr<framework::Scope> cached_temp_scope_ = nullptr;
// a variable name map from paddle to cinn // a name map from paddle variables to cinn execution arguments
std::unordered_map<std::string, std::string> paddle2cinn_varmap_; std::unordered_map<std::string, std::string> paddle2cinn_varmap_;
// a variable name map from cinn to paddle // a name map from cinn execution arguments to paddle variables
std::unordered_map<std::string, std::string> cinn2paddle_varmap_; std::unordered_map<std::string, std::string> cinn2paddle_varmap_;
// the variable scope of cinn // the names of the cinn arguments used in compiled executable program
std::unordered_set<std::string> cinn_argument_names_;
// the variable scope compiled from cinn
const std::shared_ptr<CinnScope> cinn_scope_; const std::shared_ptr<CinnScope> cinn_scope_;
// all names of cinn variables used by compiled executable program // because a cinn_pod_value_t does not own a cinn_buffer_t object,
std::unordered_set<std::string> cinn_variable_names_; // an extra stroage is necessary to keep those objects and they can
// because a cinn_pod_value_t does not own the cinn_buffer_t object,
// an extra stroage is necessary to keep the object and it can
// not be released until the runtime program finish execution. // not be released until the runtime program finish execution.
std::vector<std::unique_ptr<cinn_buffer_t>> hold_buffers_; std::vector<std::unique_ptr<cinn_buffer_t>> hold_buffers_;
...@@ -105,6 +125,5 @@ class CinnLaunchContext { ...@@ -105,6 +125,5 @@ class CinnLaunchContext {
std::map<std::string, cinn_pod_value_t> name2argument_; std::map<std::string, cinn_pod_value_t> name2argument_;
}; };
} // namespace details } // namespace operators::details
} // namespace operators
} // namespace paddle } // namespace paddle
...@@ -13,14 +13,17 @@ See the License for the specific language governing permissions and ...@@ -13,14 +13,17 @@ See the License for the specific language governing permissions and
limitations under the License. */ limitations under the License. */
#include "paddle/fluid/operators/cinn/cinn_launch_context.h" #include "paddle/fluid/operators/cinn/cinn_launch_context.h"
#include "cinn/hlir/framework/scope.h"
#include "cinn/hlir/framework/tensor.h"
#include "cinn/runtime/cinn_runtime.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "paddle/fluid/framework/ddim.h" #include "paddle/fluid/framework/ddim.h"
#include "paddle/fluid/framework/scope.h" #include "paddle/fluid/framework/scope.h"
namespace paddle { namespace paddle {
namespace operators { namespace operators::details {
namespace details {
using LoDTensor = framework::LoDTensor;
using CinnShape = ::cinn::hlir::framework::Shape; using CinnShape = ::cinn::hlir::framework::Shape;
std::unique_ptr<CinnLaunchContext> CreateDefaultLaunchContext() { std::unique_ptr<CinnLaunchContext> CreateDefaultLaunchContext() {
...@@ -86,7 +89,7 @@ TEST(CinnLaunchContextTest, TestAssignVariablePreCondition) { ...@@ -86,7 +89,7 @@ TEST(CinnLaunchContextTest, TestAssignVariablePreCondition) {
paddle::platform::EnforceNotMet); paddle::platform::EnforceNotMet);
} }
TEST(CinnLaunchContextTest, TestSetArgument) { TEST(CinnLaunchContextTest, TestAppendArgument) {
platform::CPUPlace cpu_place; platform::CPUPlace cpu_place;
platform::Place place(cpu_place); platform::Place place(cpu_place);
framework::Scope scope; framework::Scope scope;
...@@ -109,7 +112,8 @@ TEST(CinnLaunchContextTest, TestSetArgument) { ...@@ -109,7 +112,8 @@ TEST(CinnLaunchContextTest, TestSetArgument) {
ASSERT_THROW(launch_context->FinalizeArguments(), ASSERT_THROW(launch_context->FinalizeArguments(),
paddle::platform::EnforceNotMet); paddle::platform::EnforceNotMet);
// test get internal variables // test get internal variables
auto internal_variable_names = launch_context->GetInternalVariableNames(); auto internal_variable_names =
launch_context->ExtractInternalVarNames({"var1"}, {"var3"});
ASSERT_EQ(internal_variable_names.size(), 1); ASSERT_EQ(internal_variable_names.size(), 1);
EXPECT_EQ(*internal_variable_names.begin(), "cinn_var2"); EXPECT_EQ(*internal_variable_names.begin(), "cinn_var2");
...@@ -134,6 +138,5 @@ TEST(CinnLaunchContextTest, TestSetArgument) { ...@@ -134,6 +138,5 @@ TEST(CinnLaunchContextTest, TestSetArgument) {
EXPECT_FLOAT_EQ(shadow_data[10], 19.99f); EXPECT_FLOAT_EQ(shadow_data[10], 19.99f);
} }
} // namespace details } // namespace operators::details
} // namespace operators
} // namespace paddle } // namespace paddle
...@@ -155,7 +155,8 @@ class CinnLaunchOpKernel : public framework::OpKernel<T> { ...@@ -155,7 +155,8 @@ class CinnLaunchOpKernel : public framework::OpKernel<T> {
// Here we directly use the names from CinnScope as Paddle variable // Here we directly use the names from CinnScope as Paddle variable
// names, because they will not be used outside the graph // names, because they will not be used outside the graph
// and should be destructed after computation finished. // and should be destructed after computation finished.
auto internal_variable_names = launch_context->GetInternalVariableNames(); auto internal_variable_names = launch_context->ExtractInternalVarNames(
input_x_variable_names, output_variable_names);
for (const auto& var_name : internal_variable_names) { for (const auto& var_name : internal_variable_names) {
launch_context->AssignInternalVariable(var_name); launch_context->AssignInternalVariable(var_name);
} }
......
...@@ -18,154 +18,54 @@ limitations under the License. */ ...@@ -18,154 +18,54 @@ limitations under the License. */
#include <random> #include <random>
#include <string> #include <string>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "paddle/fluid/framework/ddim.h"
#include "paddle/fluid/framework/ir/graph.h"
#include "paddle/fluid/framework/op_registry.h" #include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/paddle2cinn/cinn_compiler.h" #include "paddle/fluid/framework/paddle2cinn/cinn_compiler.h"
#include "paddle/fluid/framework/scope.h" #include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/framework/tensor_util.h" #include "paddle/fluid/operators/cinn/test_helper.h"
#include "paddle/fluid/platform/cpu_helper.h" #include "paddle/fluid/platform/cpu_helper.h"
#include "paddle/fluid/platform/init.h" #include "paddle/fluid/platform/init.h"
USE_OP(cinn_launch); USE_OP(cinn_launch);
USE_OP_ITSELF(elementwise_add); USE_OP_ITSELF(elementwise_add);
namespace paddle { namespace paddle::operators {
namespace operators {
using framework::ir::Graph; using framework::paddle2cinn::CinnCompiler;
using framework::ir::Node;
std::unique_ptr<Graph> CreateOnlyElementwiseAddGraph( TEST(CinnLaunchOpTest, TestWithElementwiseAdd) {
const std::string& x_name, const std::string& y_name,
const std::string& out_name) {
auto g = std::make_unique<Graph>(framework::ProgramDesc());
framework::OpDesc feed_op_x, feed_op_y;
feed_op_x.SetType("feed");
feed_op_x.SetOutput("Out", {x_name});
feed_op_y.SetType("feed");
feed_op_y.SetOutput("Out", {y_name});
framework::VarDesc x_var(x_name);
framework::VarDesc y_var(y_name);
framework::VarDesc out_var(out_name);
framework::OpDesc elementwise_add_op;
elementwise_add_op.SetType("add");
elementwise_add_op.SetInput("X", {x_name});
elementwise_add_op.SetInput("Y", {y_name});
elementwise_add_op.SetOutput("Out", {out_name});
auto* feed_op_node_x = g->CreateOpNode(&feed_op_x);
auto* feed_op_node_y = g->CreateOpNode(&feed_op_y);
auto* elementwise_add_node = g->CreateOpNode(&elementwise_add_op);
auto* x_node = g->CreateVarNode(&x_var);
auto* y_node = g->CreateVarNode(&y_var);
auto* out_node = g->CreateVarNode(&out_var);
// fill op node
feed_op_node_x->outputs = {x_node};
feed_op_node_y->outputs = {y_node};
elementwise_add_node->inputs = {x_node, y_node};
elementwise_add_node->outputs = {out_node};
// fill variable node
x_node->inputs = {feed_op_node_x};
x_node->outputs = {elementwise_add_node};
y_node->inputs = {feed_op_node_y};
y_node->outputs = {elementwise_add_node};
out_node->inputs = {elementwise_add_node};
return g;
}
void CreateInputVariablesWithRandomData(
const std::vector<std::string>& variable_names,
const framework::DDim& common_ddim, framework::Scope* scope) {
std::random_device seed;
std::default_random_engine engine(seed());
std::uniform_real_distribution<float> dist(0.f, 2.f);
for (const auto& var_name : variable_names) {
auto* tensor = scope->Var(var_name)->GetMutable<LoDTensor>();
auto* data = tensor->mutable_data<float>(common_ddim, platform::CPUPlace());
for (auto i = 0; i < tensor->numel(); ++i) {
data[i] = dist(engine);
}
}
}
void CopyInputDataToPlace(const framework::Scope& scope,
const platform::Place& dst_place,
framework::Scope* dst_scope) {
for (const auto& var_name : scope.LocalVarNames()) {
const auto& src_tensor = scope.GetVar(var_name)->Get<LoDTensor>();
auto* dst_tensor = dst_scope->Var(var_name)->GetMutable<LoDTensor>();
paddle::framework::TensorCopySync(src_tensor, dst_place, dst_tensor);
}
}
TEST(CinnLaunchOpTest, TestElementwiseAddPass) {
paddle::framework::InitDevices(); paddle::framework::InitDevices();
platform::SetNumThreads(1); platform::SetNumThreads(1);
// cache test graph into CinnCompiler // cache test graph into CinnCompiler
const auto& test_out_name = "test_out"; const std::string& test_op_out_name = "cinn_launch_op_out";
const auto& expected_out_name = "expected_out"; const std::string& add_op_out_name = "add_op_out";
auto compilation_key = CinnCompiler::GetInstance()->AddGraph( auto compilation_key = CinnCompiler::GetInstance()->AddGraph(
CreateOnlyElementwiseAddGraph("test_x", "test_y", test_out_name)); CreateOnlyElementwiseAddGraph("x", "y", test_op_out_name));
// create cinn_launch_op and elementwise_add op // create cinn_launch_op and elementwise_add op
auto cinn_launch_op = paddle::framework::OpRegistry::CreateOp( auto cinn_launch_op = paddle::framework::OpRegistry::CreateOp(
"cinn_launch", {{"X", {"test_x", "test_y"}}}, {{"Out", {test_out_name}}}, "cinn_launch", {{"X", {"x", "y"}}}, {{"Out", {test_op_out_name}}},
{{"compilation_key", compilation_key}}); {{"compilation_key", compilation_key}});
auto elementwise_add_op = paddle::framework::OpRegistry::CreateOp( auto elementwise_add_op = paddle::framework::OpRegistry::CreateOp(
"elementwise_add", {{"X", {"test_x"}}, {"Y", {"test_y"}}}, "elementwise_add", {{"X", {"x"}}, {"Y", {"y"}}},
{{"Out", {expected_out_name}}}, {{}}); {{"Out", {add_op_out_name}}}, {{}});
// prepare input data
framework::Scope init_scope;
CreateInputVariablesWithRandomData({"test_x", "test_y"}, {10, 20},
&init_scope);
// Run ops and check the computation results // Run ops and check the computation results
auto run_and_check_fn = [&](const platform::Place& place) { auto run_and_check_fn = [&](const platform::Place& place) {
framework::Scope scope; framework::Scope scope;
CopyInputDataToPlace(init_scope, place, &scope); InitVariablesWithRandomValue<float>({"x", "y"}, {10, 20}, place, &scope);
scope.Var(test_out_name)->GetMutable<LoDTensor>(); scope.Var(test_op_out_name)->GetMutable<LoDTensor>();
scope.Var(expected_out_name)->GetMutable<LoDTensor>(); scope.Var(add_op_out_name)->GetMutable<LoDTensor>();
cinn_launch_op->Run(scope, place);
platform::Place run_place(place); elementwise_add_op->Run(scope, place);
cinn_launch_op->Run(scope, run_place); CompareOpResult<float>(scope.GetVar(test_op_out_name),
elementwise_add_op->Run(scope, run_place); scope.GetVar(add_op_out_name));
LoDTensor test_out, expected_out;
paddle::framework::TensorCopySync(
scope.Var(test_out_name)->Get<LoDTensor>(), platform::CPUPlace(),
&test_out);
paddle::framework::TensorCopySync(
scope.Var(expected_out_name)->Get<LoDTensor>(), platform::CPUPlace(),
&expected_out);
ASSERT_TRUE(test_out.IsInitialized());
ASSERT_TRUE(expected_out.IsInitialized());
ASSERT_EQ(test_out.dims(), expected_out.dims());
const auto* test_data = test_out.data<float>();
const auto* excepted_data = expected_out.data<float>();
for (auto i = 0; i < expected_out.numel(); ++i) {
EXPECT_FLOAT_EQ(test_data[i], excepted_data[i]);
}
}; };
LOG(INFO) << "Check compute result on cpu"; // CPU
run_and_check_fn(platform::CPUPlace()); run_and_check_fn(platform::CPUPlace());
run_and_check_fn(platform::CPUPlace()); run_and_check_fn(platform::CPUPlace());
#ifdef PADDLE_WITH_CUDA #ifdef PADDLE_WITH_CUDA
// create an new elementwise_add op // GPU
// because the above one cached the cpu kernel
LOG(INFO) << "Check compute result on gpu";
cinn_launch_op = paddle::framework::OpRegistry::CreateOp(
"cinn_launch", {{"X", {"test_x", "test_y"}}}, {{"Out", {test_out_name}}},
{{"compilation_key", compilation_key}});
elementwise_add_op = paddle::framework::OpRegistry::CreateOp(
"elementwise_add", {{"X", {"test_x"}}, {"Y", {"test_y"}}},
{{"Out", {expected_out_name}}}, {{}});
run_and_check_fn(platform::CUDAPlace()); run_and_check_fn(platform::CUDAPlace());
run_and_check_fn(platform::CUDAPlace()); run_and_check_fn(platform::CUDAPlace());
#endif #endif
...@@ -175,8 +75,6 @@ namespace details { ...@@ -175,8 +75,6 @@ namespace details {
// Testing helper function used on CinnLaunchOpKernel in the following: // Testing helper function used on CinnLaunchOpKernel in the following:
// firstly build test data, then check both expected and illegal situations // firstly build test data, then check both expected and illegal situations
using CinnShape = ::cinn::hlir::framework::Shape;
TEST(CinnLaunchOpHelperTest, TestPlaceToCinnTarget) { TEST(CinnLaunchOpHelperTest, TestPlaceToCinnTarget) {
ASSERT_EQ(PlaceToCinnTarget(platform::CPUPlace()), ASSERT_EQ(PlaceToCinnTarget(platform::CPUPlace()),
::cinn::common::DefaultHostTarget()); ::cinn::common::DefaultHostTarget());
...@@ -187,5 +85,4 @@ TEST(CinnLaunchOpHelperTest, TestPlaceToCinnTarget) { ...@@ -187,5 +85,4 @@ TEST(CinnLaunchOpHelperTest, TestPlaceToCinnTarget) {
} }
} // namespace details } // namespace details
} // namespace operators } // namespace paddle::operators
} // namespace paddle
/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#pragma once
#include <memory>
#include <random>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/ddim.h"
#include "paddle/fluid/framework/ir/graph.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/scope.h"
namespace paddle::operators {
using LoDTensor = framework::LoDTensor;
using Variable = framework::Variable;
using Graph = framework::ir::Graph;
using Node = framework::ir::Node;
std::unique_ptr<Graph> CreateOnlyElementwiseAddGraph(
const std::string& x_name, const std::string& y_name,
const std::string& out_name) {
auto g = std::make_unique<Graph>(framework::ProgramDesc());
framework::OpDesc feed_op_x, feed_op_y;
feed_op_x.SetType("feed");
feed_op_x.SetOutput("Out", {x_name});
feed_op_y.SetType("feed");
feed_op_y.SetOutput("Out", {y_name});
framework::VarDesc x_var(x_name);
framework::VarDesc y_var(y_name);
framework::VarDesc out_var(out_name);
framework::OpDesc elementwise_add_op;
elementwise_add_op.SetType("add");
elementwise_add_op.SetInput("X", {x_name});
elementwise_add_op.SetInput("Y", {y_name});
elementwise_add_op.SetOutput("Out", {out_name});
auto* feed_op_node_x = g->CreateOpNode(&feed_op_x);
auto* feed_op_node_y = g->CreateOpNode(&feed_op_y);
auto* elementwise_add_node = g->CreateOpNode(&elementwise_add_op);
auto* x_node = g->CreateVarNode(&x_var);
auto* y_node = g->CreateVarNode(&y_var);
auto* out_node = g->CreateVarNode(&out_var);
// fill op node
feed_op_node_x->outputs = {x_node};
feed_op_node_y->outputs = {y_node};
elementwise_add_node->inputs = {x_node, y_node};
elementwise_add_node->outputs = {out_node};
// fill variable node
x_node->inputs = {feed_op_node_x};
x_node->outputs = {elementwise_add_node};
y_node->inputs = {feed_op_node_y};
y_node->outputs = {elementwise_add_node};
out_node->inputs = {elementwise_add_node};
return g;
}
template <typename DataType>
void InitVariablesWithRandomValue(const std::vector<std::string>& var_names,
const framework::DDim& common_ddim,
const platform::Place& place,
framework::Scope* scope) {
std::random_device seed;
std::default_random_engine engine(seed());
std::uniform_real_distribution<float> dist(0, 100);
LoDTensor tmp_tensor;
auto* tmp_data =
tmp_tensor.mutable_data<DataType>(common_ddim, platform::CPUPlace());
for (const auto& var_name : var_names) {
auto* tensor = scope->Var(var_name)->GetMutable<LoDTensor>();
for (auto i = 0; i < tensor->numel(); ++i) {
tmp_data[i] = static_cast<DataType>(dist(engine));
}
paddle::framework::TensorCopySync(tmp_tensor, place, tensor);
}
}
template <typename DataType>
void CompareOpResult(Variable* test_out, Variable* expected_out) {
LoDTensor test_tensor, expected_tensor;
paddle::framework::TensorCopySync(test_out->Get<LoDTensor>(),
platform::CPUPlace(), &test_tensor);
paddle::framework::TensorCopySync(expected_out->Get<LoDTensor>(),
platform::CPUPlace(), &expected_tensor);
ASSERT_TRUE(test_tensor.IsInitialized());
ASSERT_TRUE(expected_tensor.IsInitialized());
ASSERT_EQ(test_tensor.dims(), expected_tensor.dims());
const auto* test_data = test_tensor.data<DataType>();
const auto* excepted_data = expected_tensor.data<DataType>();
for (auto i = 0; i < expected_tensor.numel(); ++i) {
EXPECT_EQ(test_data[i], excepted_data[i]);
}
}
} // namespace paddle::operators
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册