/** * Copyright 2020 Huawei Technologies Co., Ltd * * 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. */ #ifndef MINDSPORE_SERVING_TEST_SERVABLE_COMMON_H #define MINDSPORE_SERVING_TEST_SERVABLE_COMMON_H #include #include #include #include #include #include "common/common_test.h" #include "master/server.h" #include "worker/worker.h" #include "worker/notfiy_master/local_notify.h" #include "worker/context.h" #include "worker/local_servable/local_sevable.h" #include "master/grpc/grpc_process.h" #include "mindspore_serving/proto/ms_service.pb.h" namespace mindspore { namespace serving { #define ExpectContainMsg(error_msg, expected_msg) \ { \ auto error_msg_str = error_msg; \ EXPECT_TRUE(error_msg_str.find(expected_msg) != std::string::npos); \ if (error_msg_str.find(expected_msg) == std::string::npos) { \ std::cout << "error_msg: " << error_msg_str << ", expected_msg: " << expected_msg << std::endl; \ } \ } class TestMasterWorker : public UT::Common { public: TestMasterWorker() = default; void Init(std::string servable_dir, std::string servable_name, int version_number, std::string model_file) { servable_dir_ = servable_dir; servable_name_ = servable_name; version_number_ = version_number; model_file_ = model_file; servable_name_path_ = servable_dir_ + "/" + servable_name_; version_number_path_ = servable_name_path_ + "/" + std::to_string(version_number_); model_name_path_ = version_number_path_ + "/" + model_file_; __mode_t access_mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; mkdir(servable_dir_.c_str(), access_mode); mkdir(servable_name_path_.c_str(), access_mode); mkdir(version_number_path_.c_str(), access_mode); std::ofstream fp(model_name_path_); fp << "model content"; fp.close(); model_name_path_list_.emplace(model_name_path_); version_number_path_list_.emplace(version_number_path_); servable_name_path_list_.emplace(servable_name_path_); servable_dir_list_.emplace(servable_dir_); } virtual void SetUp() {} virtual void TearDown() { for (auto &item : model_name_path_list_) { remove(item.c_str()); } for (auto &item : version_number_path_list_) { rmdir(item.c_str()); } for (auto &item : servable_name_path_list_) { rmdir(item.c_str()); } for (auto &item : servable_dir_list_) { rmdir(item.c_str()); } Worker::GetInstance().Clear(); Server::Instance().Clear(); } void StartAddServable() { auto status = StartServable(servable_dir_, servable_name_, 0); ASSERT_TRUE(status.IsSuccess()); } void RegisterAddServable(bool with_batch_dim = false) { DeclareServable(servable_name_, model_file_, "mindir", with_batch_dim); // register_method RegisterMethod(servable_name_, "add_common", {"x1", "x2"}, {"y"}, 2, 1); } static Status StartServable(const std::string &servable_dir, const std::string &servable_name, int version_number) { auto notify_master = std::make_shared(); ServableContext::Instance()->SetDeviceId(0); ServableContext::Instance()->SetDeviceTypeStr("Ascend"); auto servable = std::make_shared(); auto status = servable->StartServable(servable_dir, servable_name, version_number); if (status != SUCCESS) { return status; } status = Worker::GetInstance().StartServable(servable, notify_master); return status; } static void DeclareServable(const std::string &servable_name, const std::string &servable_file, const std::string &model_type, bool with_batch_dim = false) { ServableMeta servable_meta; servable_meta.common_meta.servable_name = servable_name; servable_meta.common_meta.with_batch_dim = with_batch_dim; servable_meta.local_meta.servable_file = servable_file; servable_meta.local_meta.SetModelFormat(model_type); // declare_servable ServableStorage::Instance().DeclareServable(servable_meta); } static Status RegisterMethod(const std::string &servable_name, const std::string &method_name, const std::vector &input_names, const std::vector &output_names, size_t servable_input_count, size_t servable_output_count) { auto status = ServableStorage::Instance().RegisterInputOutputInfo(servable_name, servable_input_count, servable_output_count); if (status != SUCCESS) { return status; } MethodSignature method_signature; method_signature.servable_name = servable_name; method_signature.method_name = method_name; method_signature.inputs = input_names; method_signature.outputs = output_names; // method input 0 and input 1 as servable input method_signature.servable_inputs = {{kPredictPhaseTag_Input, 0}, {kPredictPhaseTag_Input, 1}}; // servable output as method output method_signature.returns = {{kPredictPhaseTag_Predict, 0}}; ServableStorage::Instance().RegisterMethod(method_signature); return SUCCESS; } std::string servable_dir_; std::string servable_name_; int version_number_ = 0; std::string model_file_; std::string model_name_path_; std::string version_number_path_; std::string servable_name_path_; std::set servable_dir_list_; std::set model_name_path_list_; std::set version_number_path_list_; std::set servable_name_path_list_; }; class TestMasterWorkerClient : public TestMasterWorker { public: TestMasterWorkerClient() = default; static void InitTensor(proto::Tensor *tensor, const std::vector &shape, proto::DataType data_type, const void *data, size_t data_size) { MSI_EXCEPTION_IF_NULL(tensor); tensor->set_dtype(data_type); auto proto_shape = tensor->mutable_shape(); for (auto item : shape) { proto_shape->add_dims(item); } tensor->set_data(data, data_size); } static std::vector InitOneInstanceRequest(proto::PredictRequest *request, const std::string &servable_name, const std::string &method_name, int version_number) { MSI_EXCEPTION_IF_NULL(request); auto request_servable_spec = request->mutable_servable_spec(); request_servable_spec->set_name(servable_name); request_servable_spec->set_method_name(method_name); request_servable_spec->set_version_number(version_number); std::vector x1_data = {1.1, 2.2, 3.3, 4.4}; std::vector x2_data = {1.2, 2.3, 3.4, 4.5}; std::vector y_data; for (size_t i = 0; i < x1_data.size(); i++) { y_data.push_back(x1_data[i] + x2_data[i]); } auto instance = request->add_instances(); auto &input_map = (*instance->mutable_items()); // input x1 InitTensor(&input_map["x1"], {2, 2}, proto::MS_FLOAT32, x1_data.data(), x1_data.size() * sizeof(float)); // input x2 InitTensor(&input_map["x2"], {2, 2}, proto::MS_FLOAT32, x2_data.data(), x2_data.size() * sizeof(float)); return y_data; } template static std::vector> InitMultiInstancesRequest(proto::PredictRequest *request, const std::string &servable_name, const std::string &method_name, int version_number, size_t instances_count) { MSI_EXCEPTION_IF_NULL(request); auto request_servable_spec = request->mutable_servable_spec(); request_servable_spec->set_name(servable_name); request_servable_spec->set_method_name(method_name); request_servable_spec->set_version_number(version_number); auto data_type = proto::MS_FLOAT32; if (std::string(typeid(IN_DT).name()) == std::string(typeid(int32_t).name())) { data_type = proto::MS_INT32; } std::vector> y_data_list; for (size_t k = 0; k < instances_count; k++) { std::vector x1_data_org = {1.1, 2.2, 3.3, 4.4}; std::vector x2_data_org = {6.6, 7.7, 8.8, 9.9}; std::vector x1_data; std::vector x2_data; std::vector y_data; for (size_t i = 0; i < x1_data_org.size(); i++) { x1_data.push_back(static_cast(x1_data_org[i] * (k + 1))); x2_data.push_back(static_cast(x2_data_org[i] * (k + 1))); y_data.push_back(static_cast(x1_data[i] + x2_data[i])); } y_data_list.push_back(y_data); auto instance = request->add_instances(); auto &input_map = (*instance->mutable_items()); // input x1 InitTensor(&input_map["x1"], {2, 2}, data_type, x1_data.data(), x1_data.size() * sizeof(IN_DT)); // input x2 InitTensor(&input_map["x2"], {2, 2}, data_type, x2_data.data(), x2_data.size() * sizeof(IN_DT)); } return y_data_list; } template static std::vector> InitMultiInstancesShape2Request(proto::PredictRequest *request, const std::string &servable_name, const std::string &method_name, int version_number, size_t instances_count) { MSI_EXCEPTION_IF_NULL(request); auto request_servable_spec = request->mutable_servable_spec(); request_servable_spec->set_name(servable_name); request_servable_spec->set_method_name(method_name); request_servable_spec->set_version_number(version_number); auto data_type = proto::MS_FLOAT32; if (std::string(typeid(IN_DT).name()) == std::string(typeid(int32_t).name())) { data_type = proto::MS_INT32; } std::vector> y_data_list; for (size_t k = 0; k < instances_count; k++) { std::vector x1_data_org = {1.1, 2.2}; std::vector x2_data_org = {8.8, 9.9}; std::vector x1_data; std::vector x2_data; std::vector y_data; for (size_t i = 0; i < x1_data_org.size(); i++) { x1_data.push_back(static_cast(x1_data_org[i] * (k + 1))); x2_data.push_back(static_cast(x2_data_org[i] * (k + 1))); y_data.push_back(x1_data[i] + x2_data[i]); } y_data_list.push_back(y_data); auto instance = request->add_instances(); auto &input_map = (*instance->mutable_items()); // input x1 InitTensor(&input_map["x1"], {2}, data_type, x1_data.data(), x1_data.size() * sizeof(IN_DT)); // input x2 InitTensor(&input_map["x2"], {2}, data_type, x2_data.data(), x2_data.size() * sizeof(IN_DT)); } return y_data_list; } template static void CheckMultiInstanceResult(const proto::PredictReply &reply, const std::vector> &y_data_list, size_t instances_count) { // checkout output ASSERT_EQ(reply.instances_size(), instances_count); ASSERT_EQ(reply.error_msg_size(), 0); auto data_type = proto::MS_FLOAT32; if (std::string(typeid(OUT_DT).name()) == std::string(typeid(int32_t).name())) { data_type = proto::MS_INT32; } std::vector shape; if (y_data_list[0].size() == 4) { shape = {2, 2}; } else { shape = {2}; } for (size_t k = 0; k < instances_count; k++) { auto &output_instance = reply.instances(k); ASSERT_EQ(output_instance.items_size(), 1); auto &output_items = output_instance.items(); ASSERT_EQ(output_items.begin()->first, "y"); auto &output_tensor = output_items.begin()->second; CheckTensor(output_tensor, shape, data_type, y_data_list[k].data(), y_data_list[k].size() * sizeof(OUT_DT)); } } template static void CheckInstanceResult(const proto::PredictReply &reply, const std::vector &y_data) { // checkout output ASSERT_EQ(reply.instances_size(), 1); ASSERT_EQ(reply.error_msg_size(), 0); auto data_type = proto::MS_FLOAT32; if (std::string(typeid(OUT_DT).name()) == std::string(typeid(int32_t).name())) { data_type = proto::MS_INT32; } std::vector shape; if (y_data.size() == 4) { shape = {2, 2}; } else { shape = {2}; } auto &output_instance = reply.instances(0); ASSERT_EQ(output_instance.items_size(), 1); auto &output_items = output_instance.items(); ASSERT_EQ(output_items.begin()->first, "y"); auto &output_tensor = output_items.begin()->second; CheckTensor(output_tensor, shape, data_type, y_data.data(), y_data.size() * sizeof(OUT_DT)); } static void CheckTensor(const proto::Tensor &output_tensor, const std::vector &shape, proto::DataType data_type, const void *data, size_t data_size) { EXPECT_EQ(output_tensor.dtype(), data_type); // check shape [2,2] auto &output_tensor_shape = output_tensor.shape(); ASSERT_EQ(output_tensor_shape.dims_size(), shape.size()); std::vector proto_shape; for (size_t i = 0; i < output_tensor_shape.dims_size(); i++) { proto_shape.push_back(output_tensor_shape.dims(i)); } EXPECT_EQ(proto_shape, shape); // check data ASSERT_EQ(output_tensor.data().size(), data_size); switch (data_type) { case proto::MS_FLOAT32: { auto data_len = data_size / sizeof(float); auto real_data = reinterpret_cast(output_tensor.data().data()); auto expect_data = reinterpret_cast(data); for (size_t i = 0; i < data_len; i++) { EXPECT_EQ(real_data[i], expect_data[i]); if (real_data[i] != expect_data[i]) { break; } } break; } case proto::MS_INT32: { auto data_len = data_size / sizeof(int32_t); auto real_data = reinterpret_cast(output_tensor.data().data()); auto expect_data = reinterpret_cast(data); for (size_t i = 0; i < data_len; i++) { EXPECT_EQ(real_data[i], expect_data[i]); if (real_data[i] != expect_data[i]) { break; } } break; } default: FAIL(); } } static grpc::Status Dispatch(const proto::PredictRequest &request, proto::PredictReply *reply) { MSServiceImpl impl(Server::Instance().GetDispatcher()); grpc::ServerContext context; auto promise = std::make_shared>(); auto future = promise->get_future(); PredictOnFinish callback = [promise]() { promise->set_value(); }; impl.PredictAsync(&request, reply, callback); future.get(); return grpc::Status::OK; } }; } // namespace serving } // namespace mindspore #endif // MINDSPORE_SERVING_TEST_SERVABLE_COMMON_H