/** * 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_CCSRC_PS_WORKER_H_ #define MINDSPORE_CCSRC_PS_WORKER_H_ #include #include #include #include #include #include #include #include "ps/ps.h" #include "utils/log_adapter.h" #include "ir/tensor.h" #include "ps/util.h" #include "ps/common.h" #include "ps/worker_proxy.h" #include "utils/shape_utils.h" namespace mindspore { namespace ps { template class Worker { public: static Worker &GetInstance() { static Worker instance; return instance; } void Run(); void Push(const std::vector &keys, std::vector addrs, const ShapeVector &sizes); void Pull(const size_t key, void *dev_addr, const size_t size); size_t SetParamKey(const std::string ¶m_name); void SetParamInitInServer(const std::string ¶m_name, bool init_in_server); bool GetParamInitInServer(const std::string ¶m_name); void SetKeyOptimId(size_t key, const std::string &optimizer_name); void SetOptimInputShapes(size_t key, const ShapeVector &shape); void AddEmbeddingTable(const ::ps::Key &key, const size_t &row_count); void InitPSEmbeddingTable(const std::vector &keys, std::vector shapes, const ShapeVector &sizes); void InitPSParamAndOptim(const AnfNodePtr &input_node, const tensor::TensorPtr &tensor); void DoPSEmbeddingLookup(const ::ps::SArray<::ps::Key> &keys, const ::ps::SArray &lookup_ids, const ::ps::SArray &lens, ::ps::SArray *lookup_result, int cmd); void Finalize(); private: Worker() : kv_worker_(nullptr), running_(false), key_cnt_(0) {} ~Worker() = default; Worker(const Worker &) = delete; Worker &operator=(const Worker &) = delete; bool IsKeyInit(const size_t key); size_t GetParamKey(const std::string ¶m_name); void InitPSOptimId(const size_t param_key); void InitPSOptimInputShapes(const size_t key); void InitPSParamData(const std::vector &keys, void *origin_addr, size_t size); static void EmbeddingLookupIdSlicer(const ::ps::KVPairs &send, const std::vector<::ps::Range> &ranges, std::vector>> *sliced) {} std::shared_ptr> kv_worker_; bool running_; size_t key_cnt_; std::map param_to_key_; std::map init_keys_; std::map key_to_optimId_; std::map> key_to_optim_shapes_; std::map param_to_init_in_server_; }; template void Worker::Run() { if (running_) { MS_LOG(INFO) << "'Worker is already running."; return; } MS_LOG(INFO) << "Worker starts connecting to scheduler and server..."; ::ps::Start(0); MS_LOG(INFO) << "Worker connected successfully."; if (!::ps::IsWorker()) { MS_LOG(EXCEPTION) << "The role is not worker."; } kv_worker_ = std::make_shared>(0, 0, 1, 2); running_ = true; } template void Worker::Push(const std::vector &keys, std::vector addrs, const ShapeVector &sizes) { if (keys.size() == 0) { MS_LOG(EXCEPTION) << "key size should be greater than zero"; } if (key_to_optimId_.count(keys[0]) == 0) { MS_LOG(EXCEPTION) << "no optim id found for key" << keys[0]; } Key key = keys[0]; int optim_id = key_to_optimId_[key]; bool is_sparse = false; if (optim_id == 1 || optim_id == 2 || optim_id == 3) { is_sparse = true; } int grad_index = -1; int indice_index = -1; // Sparse adam gradient if (optim_id == 1 || optim_id == 2) { grad_index = 6; indice_index = 7; // Sparse ftrl gradient } else if (optim_id == 3) { grad_index = 0; indice_index = 1; } size_t total_size = std::accumulate(sizes.begin(), sizes.end(), 0, std::plus()); ::ps::SArray total_buffer(total_size, 0); size_t offset = 0; size_t dst_size = 0; size_t src_size = 0; for (size_t i = 0; i < sizes.size(); i++) { void *dst_data = total_buffer.data() + offset / sizeof(T); void *src_data = reinterpret_cast(addrs[i]); MS_EXCEPTION_IF_NULL(dst_data); MS_EXCEPTION_IF_NULL(src_data); dst_size = sizes[i] * sizeof(T); src_size = sizes[i] * sizeof(T); auto ret = memcpy_s(dst_data, dst_size, src_data, src_size); if (ret != 0) { MS_LOG(EXCEPTION) << "memcpy_s error, errorno(" << ret << ")"; return; } offset += sizes[i] * sizeof(T); } while (!kv_worker_->IsReadyForPush(keys[0])) { continue; } if (!is_sparse) { kv_worker_->PushData(::ps::SArray<::ps::Key>(keys), total_buffer, ::ps::SArray(sizes)); } else { std::vector &var_shape = key_to_optim_shapes_[key][0]; int first_dim_size = var_shape[0]; int outer_dim_size = std::accumulate(var_shape.begin() + 1, var_shape.end(), 1, std::multiplies()); kv_worker_->PushSparseData(::ps::SArray<::ps::Key>(keys), total_buffer, ::ps::SArray(sizes), grad_index, indice_index, first_dim_size, outer_dim_size); } } template void Worker::Pull(const size_t key, void *dev_addr, const size_t size) { MS_EXCEPTION_IF_NULL(dev_addr); ::ps::SArray variables(size / sizeof(T), 0); while (!kv_worker_->IsReadyForPull(key)) { continue; } kv_worker_->PullData({key}, &variables); size_t dst_size = size; size_t src_size = size; auto ret = memcpy_s(dev_addr, dst_size, variables.data(), src_size); if (ret != 0) { MS_LOG(EXCEPTION) << "memcpy_s error, errorno(" << ret << ")"; return; } } template void Worker::DoPSEmbeddingLookup(const ::ps::SArray<::ps::Key> &keys, const ::ps::SArray &lookup_ids, const ::ps::SArray &lens, ::ps::SArray *lookup_result, int cmd) { MS_EXCEPTION_IF_NULL(lookup_result); kv_worker_->EmbeddingLookup(keys, lookup_ids, lens, lookup_result, cmd); } template void Worker::Finalize() { if (running_) { MS_LOG(INFO) << "Worker starts finalizing..."; kv_worker_->Finalize(); kv_worker_.reset(); running_ = false; MS_LOG(INFO) << "Worker finalized successfully."; } } template void Worker::InitPSParamData(const std::vector &keys, void *origin_addr, size_t size) { MS_EXCEPTION_IF_NULL(origin_addr); ::ps::SArray addr(reinterpret_cast(origin_addr), size / sizeof(T)); ::ps::SArray<::ps::Key> key(keys); ::ps::SArray lens; lens.push_back(addr.size()); kv_worker_->PushData(key, addr, lens, kInitWeightsCmd); init_keys_[key[0]] = true; } template void Worker::SetOptimInputShapes(size_t key, const ShapeVector &shape) { if (key_to_optim_shapes_.find(key) == key_to_optim_shapes_.end()) { key_to_optim_shapes_[key] = {shape}; } else { key_to_optim_shapes_[key].push_back(shape); } } template void Worker::InitPSOptimInputShapes(const size_t key) { ::ps::SArray<::ps::Key> keys; ::ps::SArray shape_len; ::ps::SArray all_shape; std::vector shapes = key_to_optim_shapes_[key]; for (auto shape : shapes) { keys.push_back(key); if (shape.size() == 0) { shape_len.push_back(1); all_shape.push_back(1); } else { shape_len.push_back(SizeToInt(shape.size())); for (auto dim : shape) { all_shape.push_back(static_cast(dim)); } } } MS_LOG(INFO) << "keys:" << keys; MS_LOG(INFO) << "shape_len:" << shape_len; MS_LOG(INFO) << "all_shape:" << all_shape; if (!init_keys_[key]) { init_keys_[key] = true; } kv_worker_->PushData(keys, all_shape, shape_len, kInitOptimInputsShapeCmd); } template bool Worker::IsKeyInit(const size_t key) { if (init_keys_.find(key) == init_keys_.end() || !init_keys_[key]) { return false; } return true; } template size_t Worker::SetParamKey(const std::string ¶m_name) { size_t key = UINT64_MAX; if (param_to_key_.count(param_name)) { key = param_to_key_[param_name]; MS_LOG(INFO) << param_name << " key is already set: key value is " << key; } else { key = key_cnt_++; param_to_key_[param_name] = key; MS_LOG(INFO) << "Set key " << key << " for parameter " << param_name; } return key; } template void Worker::SetParamInitInServer(const std::string ¶m_name, bool init_in_server) { MS_LOG(INFO) << "Set parameter " << param_name << " init_in_server:" << init_in_server; param_to_init_in_server_[param_name] = init_in_server; } template bool Worker::GetParamInitInServer(const std::string ¶m_name) { if (param_to_init_in_server_.count(param_name) == 0) { return false; } return param_to_init_in_server_[param_name]; } template size_t Worker::GetParamKey(const std::string ¶m_name) { size_t key = kInvalidKey; if (param_to_key_.find(param_name) != param_to_key_.end()) { key = param_to_key_[param_name]; MS_LOG(INFO) << "Get key of parameter " << param_name << " key is " << key; } return key; } template void Worker::SetKeyOptimId(size_t key, const std::string &optimizer_name) { key_to_optimId_[key] = Util::optimizer_id(optimizer_name); } template void Worker::InitPSOptimId(const size_t param_key) { if (key_to_optimId_.count(param_key) == 0) { MS_LOG(EXCEPTION) << "Can't find optimizer id of parameter key " << param_key; } int optim_id = key_to_optimId_[param_key]; ::ps::SArray<::ps::Key> keys = {param_key}; ::ps::SArray optim_id_vals = {static_cast(optim_id)}; ::ps::SArray optim_id_lens = {optim_id_vals.size()}; kv_worker_->PushData(keys, optim_id_vals, optim_id_lens, kInitWeightToOptimIdCmd); } template void Worker::InitPSEmbeddingTable(const std::vector &keys, std::vector shapes, const ShapeVector &sizes) { bool has_init = IsKeyInit(keys[0]); if (has_init) { MS_LOG(DEBUG) << "The key embedding table of key " << keys[0] << " is initialized."; return; } ::ps::SArray shapes_val; for (auto dim : shapes) { shapes_val.push_back(static_cast(dim)); } kv_worker_->Wait(kv_worker_->InitEmbeddingTable(::ps::SArray<::ps::Key>(keys), shapes_val, ::ps::SArray(sizes))); } template void Worker::InitPSParamAndOptim(const AnfNodePtr &input_node, const tensor::TensorPtr &tensor) { MS_EXCEPTION_IF_NULL(tensor); MS_EXCEPTION_IF_NULL(input_node); auto pk_node = input_node->cast(); MS_EXCEPTION_IF_NULL(pk_node); const std::string ¶m_name = pk_node->fullname_with_scope(); void *param_data = tensor->data_c(); size_t param_size = LongToSize(tensor->data().nbytes()); if (param_size > INT_MAX) { MS_LOG(EXCEPTION) << "PS mode max weight size is " << INT_MAX << ", " << param_name << " size is " << param_size; } size_t param_key = GetParamKey(param_name); if (param_key == kInvalidKey) { MS_LOG(DEBUG) << "Parameter " << param_name << " has no key assigned."; return; } bool init_in_server = false; auto param_info_ptr = pk_node->param_info(); if (param_info_ptr != nullptr && param_info_ptr->init_in_server()) { init_in_server = true; } SetParamInitInServer(param_name, init_in_server); bool init = IsKeyInit(param_key); if (!init) { MS_LOG(INFO) << "Init paramter and optimizer in parameter server side for " << param_name << ", whether init in server: " << init_in_server; kv_worker_->AddKeyToServerId(param_key); if (!init_in_server) { InitPSParamData({param_key}, param_data, param_size); } InitPSOptimId(param_key); InitPSOptimInputShapes(param_key); } } template void Worker::AddEmbeddingTable(const ::ps::Key &key, const size_t &row_count) { bool has_init = IsKeyInit(key); if (has_init) { return; } kv_worker_->AddEmbeddingTable(key, row_count); } static Worker &worker = Worker::GetInstance(); } // namespace ps } // namespace mindspore #endif // MINDSPORE_CCSRC_PS_WORKER_H_