From 3b3981565eec50aeb101b31916f568ac1bc94683 Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Tue, 27 Mar 2018 22:07:15 +0800 Subject: [PATCH 1/7] mpi enabled design doc --- .../design/dist_train/mpi_enabled_design.md | 56 +++++++++++++++++++ paddle/fluid/operators/detail/mpi_utils.cpp | 4 ++ paddle/fluid/operators/detail/mpi_utils.h | 8 +++ 3 files changed, 68 insertions(+) create mode 100644 doc/fluid/design/dist_train/mpi_enabled_design.md create mode 100644 paddle/fluid/operators/detail/mpi_utils.cpp create mode 100644 paddle/fluid/operators/detail/mpi_utils.h diff --git a/doc/fluid/design/dist_train/mpi_enabled_design.md b/doc/fluid/design/dist_train/mpi_enabled_design.md new file mode 100644 index 0000000000000..19f4298d71ca9 --- /dev/null +++ b/doc/fluid/design/dist_train/mpi_enabled_design.md @@ -0,0 +1,56 @@ +#MPI-enabled PaddlePaddle Design doc +## Overview +We will introduce Open MPI API to PaddlePaddle, which can bring two benefits to PaddlePaddle: +1. Enable RDMA with PaddlePaddle, which bring high performance low latency networks. +2. Enable GPUDriect with PaddlePaddle, which bring highest throughput and lowest latency GPU read and write. + +## Global Config +Launch the script using the 'mpirun' launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. By doing this, We can number the actors (trainer/pserver/master) whith o .. (n-1). The actor's number is the Rank of the calling process in group of comm (integer), The MPI processes identify each other using an Rank ID. We have to create a mapping between PaddlePaddle's actors and there Rank ID, so that we can communicate with the correct destinations when using MPI operations. + **We have to store the Rank ID and the mapping in global variables.** + +#Utils +We will build mpi_send_recv_utils Class to unify package interface about MPI Send and Receive. +```c++ +#mpi send and receive utils +class Mpi_ISend { + +} +class Mpi_IRecv { + +} + +class MPIUtils { + public: + const int GetRankID(const std::string& task_id); + void InitMPI(); + private: + std::map name_to_id_; +} + +``` +```c++ +class MPIServer { + public: + SetCond(); + ShutDown(); + WaitClientGet(); + reset(); + Push(); + SetScope(); + SetDevCtx(); + get(); +} +``` + +## New OP +We won't replace all the gRPC requests to MPI requests, the standard gRPC library is used for all administrative operations and the MPI API will used to transfer tensor or selectRows to Pservers. Base of this idea, we create two new operators to handle requests and receives, the two operators are send_mpi_op and listenandserve_mpi_op. They are a little similar with [send_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/send_op.cc) and [listen_and_serv_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/listen_and_serv_op.cc). + +### send_mpi_op +vary similar with send_op, we will replace grpc with mpi send service. +### listenandserve_mpi_op +vary similar with listen_and_serv_op, we will replace grpc with mpi receive service. +## Build args +Beause MPI or CUDA need hardware supported, so we will add some build args to control compiling. +**The specific arguments is under design** +## Execute args +Launch the script using the 'mpirun' launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_utils.cpp b/paddle/fluid/operators/detail/mpi_utils.cpp new file mode 100644 index 0000000000000..adf4a3b92541b --- /dev/null +++ b/paddle/fluid/operators/detail/mpi_utils.cpp @@ -0,0 +1,4 @@ +// +// Created by tangwei12 on 2018/3/27. +// + diff --git a/paddle/fluid/operators/detail/mpi_utils.h b/paddle/fluid/operators/detail/mpi_utils.h new file mode 100644 index 0000000000000..fb2f141246118 --- /dev/null +++ b/paddle/fluid/operators/detail/mpi_utils.h @@ -0,0 +1,8 @@ +// +// Created by tangwei12 on 2018/3/27. +// + +#ifndef PADDLE_MPI_UTILS_H +#define PADDLE_MPI_UTILS_H + +#endif //PADDLE_MPI_UTILS_H From b1adcd4641831f6f8e2d52ba439490487b8da5aa Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Tue, 27 Mar 2018 22:07:59 +0800 Subject: [PATCH 2/7] mpi sever code --- paddle/fluid/operators/detail/mpi_client.h | 53 +++++++++++++++++++ paddle/fluid/operators/detail/mpi_server.h | 23 ++++++++ paddle/fluid/operators/detail/mpi_utils.cpp | 44 ++++++++++++++++ paddle/fluid/operators/detail/mpi_utils.h | 58 ++++++++++++++++++--- 4 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 paddle/fluid/operators/detail/mpi_client.h create mode 100644 paddle/fluid/operators/detail/mpi_server.h diff --git a/paddle/fluid/operators/detail/mpi_client.h b/paddle/fluid/operators/detail/mpi_client.h new file mode 100644 index 0000000000000..14dcd678a09e8 --- /dev/null +++ b/paddle/fluid/operators/detail/mpi_client.h @@ -0,0 +1,53 @@ + +/* Copyright (c) 2016 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 +#include +#include + +#include "paddle/fluid/framework/data_type.h" +#include "paddle/fluid/framework/lod_tensor.h" +#include "paddle/fluid/framework/scope.h" +#include "paddle/fluid/framework/selected_rows.h" + +namespace paddle { +namespace operators { +namespace detail { +class MPIClient { + public: + bool AsyncSendVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out = 600 * 1000); + + bool AsyncGetVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out = 600 * 1000); + + void AsyncSendBatchBarrier(const std::string& ep, + int64_t time_out = 600 * 1000); + + void AsyncSendFetchBarrier(const std::string& ep, + int64_t time_out = 600 * 1000); + bool Wait(); + + private: + int64_t req_count_ = 0; +}; +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/fluid/operators/detail/mpi_server.h b/paddle/fluid/operators/detail/mpi_server.h new file mode 100644 index 0000000000000..dda99318afa3d --- /dev/null +++ b/paddle/fluid/operators/detail/mpi_server.h @@ -0,0 +1,23 @@ + +/* Copyright (c) 2016 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 +namespace paddle { +namespace operators { +namespace detail { +class MPIServer { + public: + private: +}; +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/fluid/operators/detail/mpi_utils.cpp b/paddle/fluid/operators/detail/mpi_utils.cpp index adf4a3b92541b..6560761e6b9f1 100644 --- a/paddle/fluid/operators/detail/mpi_utils.cpp +++ b/paddle/fluid/operators/detail/mpi_utils.cpp @@ -2,3 +2,47 @@ // Created by tangwei12 on 2018/3/27. // +#include + +#include "paddle/fluid/operators/detail/mpi_utils.h" + +#define max_worker_name_length 128 + +namespace paddle { +namespace operators { +namespace detail { +MPIUtils::MPIUtils(const std::string& worker_name) { + InitMPI(); + + int rank = 0, size = 1; + char my_name[max_work_group_size]; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + snprintf(my_name, max_worker_name_length, worker_name.c_str()); + + std::vector worker_names(size * max_worker_name_length); + MPI_Allgather(my_name, max_worker_name_length, MPI_CHAR, &worker_names[0], + max_worker_name_length, MPI_CHAR, MPI_COMM_WORLD); + for (int i = 0; i < number_of_procs; i++) { + name_to_id_[std::string(&worker_names[i * 128])] = i; + } +} + +void MPIUtils::InitMPI() { + int flag = 0; + MPI_CHECK(MPI_Initialized(&flag)); + + if (!flag) { + int rank = 0, size = 1, len = -1; + char host_name[max_worker_name_length]; + + MPI_Init(0, 0); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + MPI_Get_processor_name(host_name, &len) + } +}; +} // namespace detail + +} // namespace operators +} // namespace paddle \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_utils.h b/paddle/fluid/operators/detail/mpi_utils.h index fb2f141246118..1f5ffdb18cf3b 100644 --- a/paddle/fluid/operators/detail/mpi_utils.h +++ b/paddle/fluid/operators/detail/mpi_utils.h @@ -1,8 +1,54 @@ -// -// Created by tangwei12 on 2018/3/27. -// +/* Copyright (c) 2016 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. */ -#ifndef PADDLE_MPI_UTILS_H -#define PADDLE_MPI_UTILS_H +#pragma once +#include +#include +#include +#include -#endif //PADDLE_MPI_UTILS_H +namespace paddle { +namespace operators { +namespace detail { +class MPIUtils { + public: + MPIUtils(const std::string& worker_name); + const int GetRankID(const std::string& task_id); + + private: + void InitMPI(); + std::map name_id_map; +}; + +class MPIIsend { + public: + void init(); + int isFinished(); + void send(); + ~MPIIsend(); + + private: + int done1; + int done2; + sendrecv::VariableMessage req; +}; + +class MPIIrecv { + public: + void init(); + int isFinished(); + void recv(); + ~MPIIrecv(); +}; + +} // namespace detail +} // namespace operators +} // namespace paddle From 94ce020241cbc081a95089e677a79e340f3a4e0a Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Wed, 28 Mar 2018 15:50:59 +0800 Subject: [PATCH 3/7] mpi tools --- paddle/fluid/operators/detail/mpi_client.cpp | 29 ++++++++++++ paddle/fluid/operators/detail/mpi_client.h | 37 ++++++++-------- paddle/fluid/operators/detail/mpi_utils.cpp | 46 +++++++++++++++++++- paddle/fluid/operators/detail/mpi_utils.h | 21 ++++----- 4 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 paddle/fluid/operators/detail/mpi_client.cpp diff --git a/paddle/fluid/operators/detail/mpi_client.cpp b/paddle/fluid/operators/detail/mpi_client.cpp new file mode 100644 index 0000000000000..6890e437ef18a --- /dev/null +++ b/paddle/fluid/operators/detail/mpi_client.cpp @@ -0,0 +1,29 @@ + +/* Copyright (c) 2016 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 "mpi_client.h" +#include "mpi_utils.h" + +namespace paddle { +namespace operators { +namespace detail { +bool MPIClient::AsyncSendVariable() { + char* msg = "123456787654"; + int dst = 1; + MPIIsend send = MPIIsend(dst, msg); +} + +bool MPIClient::Wait() {} + +} // namespace detail +} // namespace operators +} // namespace paddle \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_client.h b/paddle/fluid/operators/detail/mpi_client.h index 14dcd678a09e8..a01e5b2d119c4 100644 --- a/paddle/fluid/operators/detail/mpi_client.h +++ b/paddle/fluid/operators/detail/mpi_client.h @@ -26,23 +26,26 @@ namespace operators { namespace detail { class MPIClient { public: - bool AsyncSendVariable(const std::string& ep, - const platform::DeviceContext& ctx, - const framework::Scope& scope, - const std::string& var_name, - int64_t time_out = 600 * 1000); - - bool AsyncGetVariable(const std::string& ep, - const platform::DeviceContext& ctx, - const framework::Scope& scope, - const std::string& var_name, - int64_t time_out = 600 * 1000); - - void AsyncSendBatchBarrier(const std::string& ep, - int64_t time_out = 600 * 1000); - - void AsyncSendFetchBarrier(const std::string& ep, - int64_t time_out = 600 * 1000); + // bool AsyncSendVariable(const std::string& ep, + // const platform::DeviceContext& ctx, + // const framework::Scope& scope, + // const std::string& var_name, + // int64_t time_out = 600 * 1000); + + // bool AsyncGetVariable(const std::string& ep, + // const platform::DeviceContext& ctx, + // const framework::Scope& scope, + // const std::string& var_name, + // int64_t time_out = 600 * 1000); + + // void AsyncSendBatchBarrier(const std::string& ep, + // int64_t time_out = 600 * 1000); + + // void AsyncSendFetchBarrier(const std::string& ep, + // int64_t time_out = 600 * 1000); + + bool AsyncSendVariable(); + bool Wait(); private: diff --git a/paddle/fluid/operators/detail/mpi_utils.cpp b/paddle/fluid/operators/detail/mpi_utils.cpp index 6560761e6b9f1..370294fe21358 100644 --- a/paddle/fluid/operators/detail/mpi_utils.cpp +++ b/paddle/fluid/operators/detail/mpi_utils.cpp @@ -3,10 +3,13 @@ // #include +#include -#include "paddle/fluid/operators/detail/mpi_utils.h" +#include +#include "mpi_utils.h" #define max_worker_name_length 128 +#define mpi_tag = 2008 namespace paddle { namespace operators { @@ -42,6 +45,47 @@ void MPIUtils::InitMPI() { MPI_Get_processor_name(host_name, &len) } }; + +MPIIsend::MPIIsend(int dst, const char* req) { + done1 = 0; + done2 = 0; + length = strlen(req); + req = req; +} + +MPIIsend::Send() { + MPI_Isend(&req, length, MPI_CHAR, dst, mpi_tag, MPI_COMM_WORLD, + &msg1_); + MPI_Test(&msg1_, &done1_, MPI_STATUS_IGNORE) +} + + bool MPIIsend::IsFinished() { + MPI_Status status; + if (!done1_) MPI_Test(&msg1_, &done1_, &status); + return done1; + } + +MPIIsend::~MPIIsend(){ + MPI_Wait(&msg1_, MPI_STATUS_IGNORE); + MPI_Free_mem(req); +} + +MPIIrecv::MPIIrecv(){ + +} + +MPIIrecv::Recv(){ + +} + +MPIIrecv::IsFinished(){ + +} + +MPIIrecv::~MPIIrecv(){ + +} + } // namespace detail } // namespace operators diff --git a/paddle/fluid/operators/detail/mpi_utils.h b/paddle/fluid/operators/detail/mpi_utils.h index 1f5ffdb18cf3b..a754439c26873 100644 --- a/paddle/fluid/operators/detail/mpi_utils.h +++ b/paddle/fluid/operators/detail/mpi_utils.h @@ -10,7 +10,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include +#include #include #include #include @@ -30,22 +30,23 @@ class MPIUtils { class MPIIsend { public: - void init(); - int isFinished(); - void send(); + MPIIsend(int dst, const char* buf); + bool IsFinished(); + void Send(); ~MPIIsend(); private: - int done1; - int done2; - sendrecv::VariableMessage req; + int done1; + int length; + char* req; + MPI_Request msg1_; }; class MPIIrecv { public: - void init(); - int isFinished(); - void recv(); +MPIIrecv(); +bool IsFinished(); + void Recv(); ~MPIIrecv(); }; From 9d256dd10ac3822e56e7962dede03fa01cceb451 Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Thu, 29 Mar 2018 18:14:25 +0800 Subject: [PATCH 4/7] mpi enabled design doc --- .../design/dist_train/mpi_enabled_design.md | 77 ++++++------------ .../design/dist_train/src/mpi_module.png | Bin 0 -> 106872 bytes 2 files changed, 27 insertions(+), 50 deletions(-) create mode 100644 doc/fluid/design/dist_train/src/mpi_module.png diff --git a/doc/fluid/design/dist_train/mpi_enabled_design.md b/doc/fluid/design/dist_train/mpi_enabled_design.md index 19f4298d71ca9..2548c6cdf59a9 100644 --- a/doc/fluid/design/dist_train/mpi_enabled_design.md +++ b/doc/fluid/design/dist_train/mpi_enabled_design.md @@ -1,56 +1,33 @@ #MPI-enabled PaddlePaddle Design doc -## Overview + +# Background +Now, PaddlePaddle Fluid with Distribution has relatively large network bottleneck, We want to use RDMA and GPUDriect to improve and solve it, so we enabled the features to PaddlePaddle with the help of MPI. + We will introduce Open MPI API to PaddlePaddle, which can bring two benefits to PaddlePaddle: -1. Enable RDMA with PaddlePaddle, which bring high performance low latency networks. -2. Enable GPUDriect with PaddlePaddle, which bring highest throughput and lowest latency GPU read and write. +1. Enable RDMA with PaddlePaddle, which bring high-performance low latency networks. +2. Enable GPUDriect with PaddlePaddle, which bring the highest throughput and lowest latency GPU read and write. -## Global Config -Launch the script using the 'mpirun' launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. By doing this, We can number the actors (trainer/pserver/master) whith o .. (n-1). The actor's number is the Rank of the calling process in group of comm (integer), The MPI processes identify each other using an Rank ID. We have to create a mapping between PaddlePaddle's actors and there Rank ID, so that we can communicate with the correct destinations when using MPI operations. +## Execute args +Launch the script using the ```mpirun``` launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. By doing this, We can number the actors (trainer/pserver/master) with o .. (n-1). The node's number is the Rank of the calling process in a group of comm (integer), The MPI processes identify each other using a Rank ID. We have to create a mapping between PaddlePaddle's actors and their Rank ID so that we can communicate with the correct destinations when using MPI operations. **We have to store the Rank ID and the mapping in global variables.** -#Utils -We will build mpi_send_recv_utils Class to unify package interface about MPI Send and Receive. -```c++ -#mpi send and receive utils -class Mpi_ISend { - -} -class Mpi_IRecv { - -} - -class MPIUtils { - public: - const int GetRankID(const std::string& task_id); - void InitMPI(); - private: - std::map name_to_id_; -} - -``` -```c++ -class MPIServer { - public: - SetCond(); - ShutDown(); - WaitClientGet(); - reset(); - Push(); - SetScope(); - SetDevCtx(); - get(); -} -``` - -## New OP -We won't replace all the gRPC requests to MPI requests, the standard gRPC library is used for all administrative operations and the MPI API will used to transfer tensor or selectRows to Pservers. Base of this idea, we create two new operators to handle requests and receives, the two operators are send_mpi_op and listenandserve_mpi_op. They are a little similar with [send_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/send_op.cc) and [listen_and_serv_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/listen_and_serv_op.cc). - -### send_mpi_op -vary similar with send_op, we will replace grpc with mpi send service. -### listenandserve_mpi_op -vary similar with listen_and_serv_op, we will replace grpc with mpi receive service. +## New OP/MODULE +We won't replace all the gRPC requests to MPI requests, the standard gRPC library is used for all administrative operations and the MPI API will be used to transfer tensor or selectRows to Pservers. The base of this idea, we create two new operators to handle requests and receives, the two operators are send_mpi_op and listenandserve_mpi_op. They are a little similar with [send_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/send_op.cc) and [listen_and_serv_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/listen_and_serv_op.cc), also, We will build a new module to package MPI send and receive process. + +### mpi_module +We will build a new module to package MPI send and receive process. MPI send and recvice are defferent to gRPC, the MPI [recvice](https://www.open-mpi.org/doc/v1.8/man3/MPI_Irecv.3.php) must know receive buffer size and receive buffer element. For this reason, We have to make conmunications twice, the first one is to send metadata about gradient through gRPC, the second one is the real conmunications through MPI which send gradient data to mpi_listenandserve_op. +The detail flow is below: +![](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/fluid/design/dist_train/src/mpi_module.png) + +### mpi_send_op +Very similar with ```send_op```, we will replace gRPC code which used to send gradient with ```mpi_module```, at the same time , we will wrap it with ```framework::Async```. + +### mpi_listenandserve_op +Very similar with ```listen_and_serv_op```, we will replace gRPC code which used to receive gradient with ```mpi_module```, at the same time , we will wrap it with ```framework::Async```. + +### modify distribute_transpiler.py +Need to add args to distinguish use MPI or not. if confirm to use MPI, we will modify ```send_op``` to ```mpi_send_op``` in distribute_transpiler, and modify ```listenandserve_op``` to ```mpi_listenandserve_op``` also. + ## Build args -Beause MPI or CUDA need hardware supported, so we will add some build args to control compiling. -**The specific arguments is under design** -## Execute args -Launch the script using the 'mpirun' launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. \ No newline at end of file +Because MPI or CUDA need hardware supported, so we will add some build args to control compiling. +**The specific arguments are under design** \ No newline at end of file diff --git a/doc/fluid/design/dist_train/src/mpi_module.png b/doc/fluid/design/dist_train/src/mpi_module.png new file mode 100644 index 0000000000000000000000000000000000000000..e6b6a3e5d6f68baeeb67d7f71154bd8d85f32b6f GIT binary patch literal 106872 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 literal 0 HcmV?d00001 From 900b3e97de99c7a2fb493f577d554032fd6331d4 Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Tue, 3 Apr 2018 13:05:49 +0800 Subject: [PATCH 5/7] send mpi and rpc framework --- paddle/fluid/operators/detail/mpi_utils.cpp | 114 ++++++++++---------- paddle/fluid/operators/detail/mpi_utils.h | 100 ++++++++++------- 2 files changed, 120 insertions(+), 94 deletions(-) diff --git a/paddle/fluid/operators/detail/mpi_utils.cpp b/paddle/fluid/operators/detail/mpi_utils.cpp index 370294fe21358..d3191c156318d 100644 --- a/paddle/fluid/operators/detail/mpi_utils.cpp +++ b/paddle/fluid/operators/detail/mpi_utils.cpp @@ -12,81 +12,79 @@ #define mpi_tag = 2008 namespace paddle { -namespace operators { -namespace detail { -MPIUtils::MPIUtils(const std::string& worker_name) { - InitMPI(); + namespace operators { + namespace detail { + MPIUtils::MPIUtils(const std::string &worker_name) { + InitMPI(); - int rank = 0, size = 1; - char my_name[max_work_group_size]; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - snprintf(my_name, max_worker_name_length, worker_name.c_str()); + int rank = 0, size = 1; + char my_name[max_work_group_size]; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + snprintf(my_name, max_worker_name_length, worker_name.c_str()); - std::vector worker_names(size * max_worker_name_length); - MPI_Allgather(my_name, max_worker_name_length, MPI_CHAR, &worker_names[0], - max_worker_name_length, MPI_CHAR, MPI_COMM_WORLD); - for (int i = 0; i < number_of_procs; i++) { - name_to_id_[std::string(&worker_names[i * 128])] = i; - } -} + std::vector worker_names(size * max_worker_name_length); + MPI_Allgather(my_name, max_worker_name_length, MPI_CHAR, &worker_names[0], + max_worker_name_length, MPI_CHAR, MPI_COMM_WORLD); + for (int i = 0; i < number_of_procs; i++) { + name_to_id_[std::string(&worker_names[i * 128])] = i; + } + } -void MPIUtils::InitMPI() { - int flag = 0; - MPI_CHECK(MPI_Initialized(&flag)); + void MPIUtils::InitMPI() { + int flag = 0; + MPI_CHECK(MPI_Initialized(&flag)); - if (!flag) { - int rank = 0, size = 1, len = -1; - char host_name[max_worker_name_length]; + if (!flag) { + int rank = 0, size = 1, len = -1; + char host_name[max_worker_name_length]; - MPI_Init(0, 0); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - MPI_Get_processor_name(host_name, &len) - } -}; + MPI_Init(0, 0); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + MPI_Get_processor_name(host_name, &len); + } + }; -MPIIsend::MPIIsend(int dst, const char* req) { - done1 = 0; - done2 = 0; - length = strlen(req); - req = req; -} -MPIIsend::Send() { - MPI_Isend(&req, length, MPI_CHAR, dst, mpi_tag, MPI_COMM_WORLD, - &msg1_); - MPI_Test(&msg1_, &done1_, MPI_STATUS_IGNORE) -} + MPISend::MPISend(const Meta &meta) { + done1_ = 1; + done2_ = 0; + this->meta = meta; + } - bool MPIIsend::IsFinished() { - MPI_Status status; - if (!done1_) MPI_Test(&msg1_, &done1_, &status); - return done1; - } + MPISend::Send() { + MPI_Send(&meta.request, meta.count, meta.datatype, meta.dst, meta.tag, + MPI_COMM_WORLD); + done2_ = 1; + } -MPIIsend::~MPIIsend(){ - MPI_Wait(&msg1_, MPI_STATUS_IGNORE); - MPI_Free_mem(req); -} + bool MPISend::IsReady() { + return true; + } -MPIIrecv::MPIIrecv(){ + bool MPISend::IsFinished() { return done1_ && done2_; } -} + MPISend::~MPISend() { MPI_Free_mem(meta); } -MPIIrecv::Recv(){ -} + MPIRecv::MPIRecv(const Meta &meta) { + this->meta = meta; + } -MPIIrecv::IsFinished(){ + MPIRecv::Recv() {} -} + bool MPIRecv::IsReady() { + return true; + } -MPIIrecv::~MPIIrecv(){ + MPIRecv::IsFinished() {} -} + MPIRecv::~MPIRecv() { + MPI_Free_mem(meta); + } -} // namespace detail + } // namespace detail -} // namespace operators + } // namespace operators } // namespace paddle \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_utils.h b/paddle/fluid/operators/detail/mpi_utils.h index a754439c26873..05801020bf353 100644 --- a/paddle/fluid/operators/detail/mpi_utils.h +++ b/paddle/fluid/operators/detail/mpi_utils.h @@ -10,46 +10,74 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once + #include #include #include #include namespace paddle { -namespace operators { -namespace detail { -class MPIUtils { - public: - MPIUtils(const std::string& worker_name); - const int GetRankID(const std::string& task_id); - - private: - void InitMPI(); - std::map name_id_map; -}; - -class MPIIsend { - public: - MPIIsend(int dst, const char* buf); - bool IsFinished(); - void Send(); - ~MPIIsend(); - - private: - int done1; - int length; - char* req; - MPI_Request msg1_; -}; - -class MPIIrecv { - public: -MPIIrecv(); -bool IsFinished(); - void Recv(); - ~MPIIrecv(); -}; - -} // namespace detail -} // namespace operators + namespace operators { + namespace detail { + class MPIUtils { + public: + MPIUtils(const std::string &worker_name); + + const int GetRankID(const std::string &task_id); + + private: + void InitMPI(); + + std::map name_id_map; + }; + + class Meta { + public: + int src; + int dst; + MPI_Datatype datatype; + char *request; + int count; + int tag; + int device; + }; + + class MPISend { + public: + MPISend(const Meta &meta); + + bool IsFinished(); + + bool IsReady(); + + void Send(); + + ~MPISend(); + + private: + int done1_; + int done2_; + Meta *meta; + }; + + class MPIRecv { + public: + MPIRecv(const Meta &meta); + + bool IsReady(); + + bool IsFinished(); + + void Recv(); + + ~MPIRecv(); + + private: + int done1_; + int done2_; + Meta *meta; + }; + + } // namespace detail + } // namespace operators } // namespace paddle From 5bf671b4aa22950d4d83e333792dfc1670639e82 Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Mon, 16 Apr 2018 15:14:22 +0800 Subject: [PATCH 6/7] structure optimized, more detail --- .../design/dist_train/mpi_enabled_design.md | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/doc/fluid/design/dist_train/mpi_enabled_design.md b/doc/fluid/design/dist_train/mpi_enabled_design.md index 2548c6cdf59a9..4ad3afc7b7522 100644 --- a/doc/fluid/design/dist_train/mpi_enabled_design.md +++ b/doc/fluid/design/dist_train/mpi_enabled_design.md @@ -1,33 +1,46 @@ -#MPI-enabled PaddlePaddle Design doc +# MPI-enabled PaddlePaddle Design doc # Background -Now, PaddlePaddle Fluid with Distribution has relatively large network bottleneck, We want to use RDMA and GPUDriect to improve and solve it, so we enabled the features to PaddlePaddle with the help of MPI. +When we do distribute multi GPU training, the communication overhead between servers become the major bottleneck, because of the following reasons: +1. Must copy at least once from GPU to CPU memory so that the data can be ready to transfer. And for the pserver side, copy data from CPU to GPU introduce more overhead. +2. GPU->CPU data transfer is 10 times slower than data transfer between GPUs or between PCIe devices. +3. TCP connections can not make full use of RDMA 100Gb devices. -We will introduce Open MPI API to PaddlePaddle, which can bring two benefits to PaddlePaddle: +We will use OpenMPI API to PaddlePaddle, which can bring two benefits to PaddlePaddle: 1. Enable RDMA with PaddlePaddle, which bring high-performance low latency networks. 2. Enable GPUDriect with PaddlePaddle, which bring the highest throughput and lowest latency GPU read and write. -## Execute args -Launch the script using the ```mpirun``` launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. By doing this, We can number the actors (trainer/pserver/master) with o .. (n-1). The node's number is the Rank of the calling process in a group of comm (integer), The MPI processes identify each other using a Rank ID. We have to create a mapping between PaddlePaddle's actors and their Rank ID so that we can communicate with the correct destinations when using MPI operations. - **We have to store the Rank ID and the mapping in global variables.** +# Change list +* Compile args: Need add compile args to enable MPI support. +* Execute args: Need add execute args to assign when and how to use MPI operations. +* New ops: Need new op ```mpi_send_op``` and ```mpi_listenandserve_op``` to support MPI send and receive. +* Transpiler optimized: Which can add ```mpi_send_op``` and ```mpi_listenandserve_op``` to the running graph. +* MPI utils package: Need MPI utils package as the low-level API supported. + +## Compile args +Because MPI or CUDA need hardware supported, so we will add compile args to enable MPI support and control compiling.Add ```WITH_MPI``` compile args to control MPI to use or not. If the ```WITH_MPI``` is ```ON```, compile system will find openMPI codes in configuration. We should prepare openMPI environment before compiling. -## New OP/MODULE -We won't replace all the gRPC requests to MPI requests, the standard gRPC library is used for all administrative operations and the MPI API will be used to transfer tensor or selectRows to Pservers. The base of this idea, we create two new operators to handle requests and receives, the two operators are send_mpi_op and listenandserve_mpi_op. They are a little similar with [send_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/send_op.cc) and [listen_and_serv_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/listen_and_serv_op.cc), also, We will build a new module to package MPI send and receive process. +## Execute args +Launch the script using the ```mpirun``` launcher, For example: ```mpirun -np 3 -hosts node1,node2,node3 python train.py```. By doing this, We can number the actors (trainer/pserver/master) with o .. (n-1). The node's number is the Rank of the calling process in a group of comm (integer), The MPI processes identify each other using a Rank ID. We have to create a mapping between PaddlePaddle's nodes and their Rank ID so that we can communicate with the correct destinations when using MPI operations. -### mpi_module -We will build a new module to package MPI send and receive process. MPI send and recvice are defferent to gRPC, the MPI [recvice](https://www.open-mpi.org/doc/v1.8/man3/MPI_Irecv.3.php) must know receive buffer size and receive buffer element. For this reason, We have to make conmunications twice, the first one is to send metadata about gradient through gRPC, the second one is the real conmunications through MPI which send gradient data to mpi_listenandserve_op. -The detail flow is below: -![](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/fluid/design/dist_train/src/mpi_module.png) +## New ops +We won't replace all the gRPC requests to MPI requests, the standard gRPC library is used for all administrative operations and the MPI API will be used to transfer tensor or selectRows to Pservers. The base of this idea, we create two new operators to handle requests and receives, the two operators are ```mpi_send_op``` and ```mpi_listenandserve_op```. They are a little similar to [send_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/send_op.cc) and [listen_and_serv_op](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/operators/listen_and_serv_op.cc), also, We will build a new module to package MPI send and receive process. ### mpi_send_op -Very similar with ```send_op```, we will replace gRPC code which used to send gradient with ```mpi_module```, at the same time , we will wrap it with ```framework::Async```. +Very similar with ```send_op```, we will replace gRPC code which used to send gradient with ```mpi_module```, at the same time, we will wrap it with ```framework::Async```. ### mpi_listenandserve_op -Very similar with ```listen_and_serv_op```, we will replace gRPC code which used to receive gradient with ```mpi_module```, at the same time , we will wrap it with ```framework::Async```. - -### modify distribute_transpiler.py -Need to add args to distinguish use MPI or not. if confirm to use MPI, we will modify ```send_op``` to ```mpi_send_op``` in distribute_transpiler, and modify ```listenandserve_op``` to ```mpi_listenandserve_op``` also. - -## Build args -Because MPI or CUDA need hardware supported, so we will add some build args to control compiling. -**The specific arguments are under design** \ No newline at end of file +Very similar with ```listen_and_serv_op```, we will replace gRPC code which used to receive gradient with ```mpi_module```, at the same time, we will wrap it with ```framework::Async```. + +## Transpiler optimized +**We can get env ```OMPI_COMM_WORLD_SIZE``` and ```OMPI_COMM_WORLD_RANK``` to distinguish use MPI or not, If we use openMPI, the variable in env must exist.** + if confirm to use MPI, we will modify ```send_op``` to ```mpi_send_op``` in distribute_transpiler, and modify ```listenandserve_op``` to ```mpi_listenandserve_op``` also. + +## MPI utils package +In this package, We will write openMPI low-level API to use MPI. +The API included in this package are: +* MPI send and receive module, We will build a new module to package MPI send and receive process. MPI send and receive are different to gRPC, the MPI [recvice](https://www.open-mpi.org/doc/v1.8/man3/MPI_Irecv.3.php) must know receive buffer size and receive buffer element. For this reason, We have to make communications twice, the first one is to send metadata about gradient through gRPC, the second one is the real communication through MPI which send gradient data to mpi_listenandserve_op. +The detailed flow is below: +![](https://github.com/seiriosPlus/Paddle/blob/mpi_enabled/doc/fluid/design/dist_train/src/mpi_module.png) +* MPI global configurations, which store the Rank ID and the mapping in global variables, for example: +gRPC client : MPI nodes :``` 127.0.0.1:32004 : 3 ``` From 10669f1fe7fd13eaa8049fc3b37bb590978d6ac8 Mon Sep 17 00:00:00 2001 From: tangwei12 Date: Tue, 17 Apr 2018 14:11:53 +0800 Subject: [PATCH 7/7] Separate the implement to another PR later --- paddle/fluid/operators/detail/mpi_client.cpp | 29 ------- paddle/fluid/operators/detail/mpi_client.h | 56 ------------ paddle/fluid/operators/detail/mpi_server.h | 23 ----- paddle/fluid/operators/detail/mpi_utils.cpp | 90 -------------------- paddle/fluid/operators/detail/mpi_utils.h | 83 ------------------ 5 files changed, 281 deletions(-) delete mode 100644 paddle/fluid/operators/detail/mpi_client.cpp delete mode 100644 paddle/fluid/operators/detail/mpi_client.h delete mode 100644 paddle/fluid/operators/detail/mpi_server.h delete mode 100644 paddle/fluid/operators/detail/mpi_utils.cpp delete mode 100644 paddle/fluid/operators/detail/mpi_utils.h diff --git a/paddle/fluid/operators/detail/mpi_client.cpp b/paddle/fluid/operators/detail/mpi_client.cpp deleted file mode 100644 index 6890e437ef18a..0000000000000 --- a/paddle/fluid/operators/detail/mpi_client.cpp +++ /dev/null @@ -1,29 +0,0 @@ - -/* Copyright (c) 2016 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 "mpi_client.h" -#include "mpi_utils.h" - -namespace paddle { -namespace operators { -namespace detail { -bool MPIClient::AsyncSendVariable() { - char* msg = "123456787654"; - int dst = 1; - MPIIsend send = MPIIsend(dst, msg); -} - -bool MPIClient::Wait() {} - -} // namespace detail -} // namespace operators -} // namespace paddle \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_client.h b/paddle/fluid/operators/detail/mpi_client.h deleted file mode 100644 index a01e5b2d119c4..0000000000000 --- a/paddle/fluid/operators/detail/mpi_client.h +++ /dev/null @@ -1,56 +0,0 @@ - -/* Copyright (c) 2016 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 -#include -#include - -#include "paddle/fluid/framework/data_type.h" -#include "paddle/fluid/framework/lod_tensor.h" -#include "paddle/fluid/framework/scope.h" -#include "paddle/fluid/framework/selected_rows.h" - -namespace paddle { -namespace operators { -namespace detail { -class MPIClient { - public: - // bool AsyncSendVariable(const std::string& ep, - // const platform::DeviceContext& ctx, - // const framework::Scope& scope, - // const std::string& var_name, - // int64_t time_out = 600 * 1000); - - // bool AsyncGetVariable(const std::string& ep, - // const platform::DeviceContext& ctx, - // const framework::Scope& scope, - // const std::string& var_name, - // int64_t time_out = 600 * 1000); - - // void AsyncSendBatchBarrier(const std::string& ep, - // int64_t time_out = 600 * 1000); - - // void AsyncSendFetchBarrier(const std::string& ep, - // int64_t time_out = 600 * 1000); - - bool AsyncSendVariable(); - - bool Wait(); - - private: - int64_t req_count_ = 0; -}; -} // namespace detail -} // namespace operators -} // namespace paddle diff --git a/paddle/fluid/operators/detail/mpi_server.h b/paddle/fluid/operators/detail/mpi_server.h deleted file mode 100644 index dda99318afa3d..0000000000000 --- a/paddle/fluid/operators/detail/mpi_server.h +++ /dev/null @@ -1,23 +0,0 @@ - -/* Copyright (c) 2016 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 -namespace paddle { -namespace operators { -namespace detail { -class MPIServer { - public: - private: -}; -} // namespace detail -} // namespace operators -} // namespace paddle diff --git a/paddle/fluid/operators/detail/mpi_utils.cpp b/paddle/fluid/operators/detail/mpi_utils.cpp deleted file mode 100644 index d3191c156318d..0000000000000 --- a/paddle/fluid/operators/detail/mpi_utils.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// Created by tangwei12 on 2018/3/27. -// - -#include -#include - -#include -#include "mpi_utils.h" - -#define max_worker_name_length 128 -#define mpi_tag = 2008 - -namespace paddle { - namespace operators { - namespace detail { - MPIUtils::MPIUtils(const std::string &worker_name) { - InitMPI(); - - int rank = 0, size = 1; - char my_name[max_work_group_size]; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - snprintf(my_name, max_worker_name_length, worker_name.c_str()); - - std::vector worker_names(size * max_worker_name_length); - MPI_Allgather(my_name, max_worker_name_length, MPI_CHAR, &worker_names[0], - max_worker_name_length, MPI_CHAR, MPI_COMM_WORLD); - for (int i = 0; i < number_of_procs; i++) { - name_to_id_[std::string(&worker_names[i * 128])] = i; - } - } - - void MPIUtils::InitMPI() { - int flag = 0; - MPI_CHECK(MPI_Initialized(&flag)); - - if (!flag) { - int rank = 0, size = 1, len = -1; - char host_name[max_worker_name_length]; - - MPI_Init(0, 0); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - MPI_Get_processor_name(host_name, &len); - } - }; - - - MPISend::MPISend(const Meta &meta) { - done1_ = 1; - done2_ = 0; - this->meta = meta; - } - - MPISend::Send() { - MPI_Send(&meta.request, meta.count, meta.datatype, meta.dst, meta.tag, - MPI_COMM_WORLD); - done2_ = 1; - } - - bool MPISend::IsReady() { - return true; - } - - bool MPISend::IsFinished() { return done1_ && done2_; } - - MPISend::~MPISend() { MPI_Free_mem(meta); } - - - MPIRecv::MPIRecv(const Meta &meta) { - this->meta = meta; - } - - MPIRecv::Recv() {} - - bool MPIRecv::IsReady() { - return true; - } - - MPIRecv::IsFinished() {} - - MPIRecv::~MPIRecv() { - MPI_Free_mem(meta); - } - - } // namespace detail - - } // namespace operators -} // namespace paddle \ No newline at end of file diff --git a/paddle/fluid/operators/detail/mpi_utils.h b/paddle/fluid/operators/detail/mpi_utils.h deleted file mode 100644 index 05801020bf353..0000000000000 --- a/paddle/fluid/operators/detail/mpi_utils.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright (c) 2016 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 -#include -#include -#include - -namespace paddle { - namespace operators { - namespace detail { - class MPIUtils { - public: - MPIUtils(const std::string &worker_name); - - const int GetRankID(const std::string &task_id); - - private: - void InitMPI(); - - std::map name_id_map; - }; - - class Meta { - public: - int src; - int dst; - MPI_Datatype datatype; - char *request; - int count; - int tag; - int device; - }; - - class MPISend { - public: - MPISend(const Meta &meta); - - bool IsFinished(); - - bool IsReady(); - - void Send(); - - ~MPISend(); - - private: - int done1_; - int done2_; - Meta *meta; - }; - - class MPIRecv { - public: - MPIRecv(const Meta &meta); - - bool IsReady(); - - bool IsFinished(); - - void Recv(); - - ~MPIRecv(); - - private: - int done1_; - int done2_; - Meta *meta; - }; - - } // namespace detail - } // namespace operators -} // namespace paddle