未验证 提交 d139f2ca 编写于 作者: W Wu Yi 提交者: GitHub

Merge pull request #9595 from typhoonzero/fix_test_sendrecv_portbind

Fix sendrecv port bind
...@@ -193,6 +193,7 @@ if(WITH_DISTRIBUTE) ...@@ -193,6 +193,7 @@ if(WITH_DISTRIBUTE)
set_source_files_properties(send_vars_op.cc PROPERTIES COMPILE_FLAGS ${DISTRIBUTE_COMPILE_FLAGS}) set_source_files_properties(send_vars_op.cc PROPERTIES COMPILE_FLAGS ${DISTRIBUTE_COMPILE_FLAGS})
op_library(send_barrier_op DEPS ${DISTRIBUTE_DEPS}) op_library(send_barrier_op DEPS ${DISTRIBUTE_DEPS})
set_source_files_properties(send_barrier_op.cc PROPERTIES COMPILE_FLAGS ${DISTRIBUTE_COMPILE_FLAGS}) set_source_files_properties(send_barrier_op.cc PROPERTIES COMPILE_FLAGS ${DISTRIBUTE_COMPILE_FLAGS})
set_source_files_properties(send_recv_op_test.cc PROPERTIES COMPILE_FLAGS ${DISTRIBUTE_COMPILE_FLAGS})
cc_test(test_send_recv SRCS send_recv_op_test.cc DEPS prefetch_op send_op listen_and_serv_op sum_op executor) cc_test(test_send_recv SRCS send_recv_op_test.cc DEPS prefetch_op send_op listen_and_serv_op sum_op executor)
else() else()
set(DEPS_OPS ${DEPS_OPS} send_op prefetch_op recv_op listen_and_serv_op send_vars_op send_barrier_op) set(DEPS_OPS ${DEPS_OPS} send_op prefetch_op recv_op listen_and_serv_op send_vars_op send_barrier_op)
......
...@@ -186,7 +186,8 @@ void AsyncGRPCServer::WaitClientGet(int count) { ...@@ -186,7 +186,8 @@ void AsyncGRPCServer::WaitClientGet(int count) {
void AsyncGRPCServer::RunSyncUpdate() { void AsyncGRPCServer::RunSyncUpdate() {
::grpc::ServerBuilder builder; ::grpc::ServerBuilder builder;
builder.AddListeningPort(address_, ::grpc::InsecureServerCredentials()); builder.AddListeningPort(address_, ::grpc::InsecureServerCredentials(),
&selected_port_);
builder.SetMaxSendMessageSize(std::numeric_limits<int>::max()); builder.SetMaxSendMessageSize(std::numeric_limits<int>::max());
builder.SetMaxReceiveMessageSize(std::numeric_limits<int>::max()); builder.SetMaxReceiveMessageSize(std::numeric_limits<int>::max());
builder.RegisterService(&service_); builder.RegisterService(&service_);
...@@ -196,7 +197,8 @@ void AsyncGRPCServer::RunSyncUpdate() { ...@@ -196,7 +197,8 @@ void AsyncGRPCServer::RunSyncUpdate() {
cq_prefetch_ = builder.AddCompletionQueue(); cq_prefetch_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart(); server_ = builder.BuildAndStart();
LOG(INFO) << "Server listening on " << address_ << std::endl; LOG(INFO) << "Server listening on " << address_
<< " selected port: " << selected_port_;
std::function<void()> send_register = std::function<void()> send_register =
std::bind(&AsyncGRPCServer::TryToRegisterNewSendOne, this); std::bind(&AsyncGRPCServer::TryToRegisterNewSendOne, this);
......
...@@ -63,6 +63,8 @@ class AsyncGRPCServer final { ...@@ -63,6 +63,8 @@ class AsyncGRPCServer final {
void SetExecutor(framework::Executor *executor) { executor_ = executor; } void SetExecutor(framework::Executor *executor) { executor_ = executor; }
int GetSelectedPort() { return selected_port_; }
const ReceivedMessage Get() { return this->var_recv_queue_.Pop(); } const ReceivedMessage Get() { return this->var_recv_queue_.Pop(); }
void Push(const std::string &msg_name) { void Push(const std::string &msg_name) {
...@@ -111,6 +113,7 @@ class AsyncGRPCServer final { ...@@ -111,6 +113,7 @@ class AsyncGRPCServer final {
int prefetch_blk_id_; int prefetch_blk_id_;
framework::ProgramDesc *program_; framework::ProgramDesc *program_;
framework::Executor *executor_; framework::Executor *executor_;
int selected_port_;
}; };
}; // namespace detail }; // namespace detail
......
...@@ -12,20 +12,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ...@@ -12,20 +12,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. */ limitations under the License. */
#include <stdint.h>
#include <ostream> #include <ostream>
#include <thread>
#include "paddle/fluid/framework/executor.h" #include "paddle/fluid/operators/listen_and_serv_op.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/threadpool.h"
#include "paddle/fluid/operators/detail/grpc_server.h"
namespace paddle { namespace paddle {
namespace operators { namespace operators {
constexpr char kOptimizeBlock[] = "OptimizeBlock";
void RunServer(std::shared_ptr<detail::AsyncGRPCServer> service) { void RunServer(std::shared_ptr<detail::AsyncGRPCServer> service) {
service->RunSyncUpdate(); service->RunSyncUpdate();
VLOG(4) << "RunServer thread end"; VLOG(4) << "RunServer thread end";
...@@ -66,143 +60,138 @@ static void ParallelExecuteBlocks( ...@@ -66,143 +60,138 @@ static void ParallelExecuteBlocks(
for (size_t i = 0; i < fs.size(); ++i) fs[i].wait(); for (size_t i = 0; i < fs.size(); ++i) fs[i].wait();
} }
class ListenAndServOp : public framework::OperatorBase { ListenAndServOp::ListenAndServOp(const std::string &type,
public: const framework::VariableNameMap &inputs,
ListenAndServOp(const std::string &type, const framework::VariableNameMap &outputs,
const framework::VariableNameMap &inputs, const framework::AttributeMap &attrs)
const framework::VariableNameMap &outputs, : OperatorBase(type, inputs, outputs, attrs) {}
const framework::AttributeMap &attrs)
: OperatorBase(type, inputs, outputs, attrs) {
if (!rpc_service_) {
std::string endpoint = Attr<std::string>("endpoint");
rpc_service_.reset(new detail::AsyncGRPCServer(endpoint));
server_thread_.reset(new std::thread(RunServer, rpc_service_));
}
}
void Stop() override { int ListenAndServOp::GetSelectedPort() {
rpc_service_->Push(LISTEN_TERMINATE_MESSAGE); return rpc_service_->GetSelectedPort();
server_thread_->join(); }
void ListenAndServOp::Stop() {
rpc_service_->Push(LISTEN_TERMINATE_MESSAGE);
server_thread_->join();
}
void ListenAndServOp::RunImpl(const framework::Scope &scope,
const platform::Place &dev_place) const {
platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance();
auto &dev_ctx = *pool.Get(dev_place);
framework::Scope &recv_scope = scope.NewScope();
if (!rpc_service_) {
std::string endpoint = Attr<std::string>("endpoint");
rpc_service_.reset(new detail::AsyncGRPCServer(endpoint));
} }
void RunImpl(const framework::Scope &scope, auto ins = Inputs("X");
const platform::Place &dev_place) const override { auto fan_in = Attr<int>("Fanin");
platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto *block = Attr<framework::BlockDesc *>(kOptimizeBlock);
auto &dev_ctx = *pool.Get(dev_place); auto *program = block->Program();
framework::Scope &recv_scope = scope.NewScope(); size_t num_blocks = program->Size();
PADDLE_ENFORCE_GE(num_blocks, 2,
// FIXME(Yancey1989): initialize rpc server with lazy mode. "server program should have at least 2 blocks");
rpc_service_->SetScope(&recv_scope);
rpc_service_->SetDevCtx(&dev_ctx); framework::Executor executor(dev_place);
auto ins = Inputs("X"); std::vector<int> block_list;
auto fan_in = Attr<int>("Fanin"); for (size_t blkid = 1; blkid < num_blocks; ++blkid) {
block_list.push_back(blkid);
auto *block = Attr<framework::BlockDesc *>(kOptimizeBlock); }
auto *program = block->Program(); auto prepared = executor.Prepare(*program, block_list);
size_t num_blocks = program->Size(); // Insert placeholder for block0 which holds current op itself.
PADDLE_ENFORCE_GE(num_blocks, 2, prepared.insert(prepared.begin(),
"server program should have at least 2 blocks"); std::shared_ptr<framework::ExecutorPrepareContext>(nullptr));
framework::Executor executor(dev_place); rpc_service_->SetScope(&recv_scope);
std::vector<int> block_list; rpc_service_->SetDevCtx(&dev_ctx);
for (size_t blkid = 1; blkid < num_blocks; ++blkid) // TODO(qiao) set proper fields for table lookup and update
block_list.push_back(blkid); rpc_service_->SetExecutor(&executor);
auto prepared = executor.Prepare(*program, block_list); rpc_service_->SetPrefetchBlkdId(0);
prepared.insert( rpc_service_->SetProgram(program);
prepared.begin(), // start the server listening after all member initialized.
std::shared_ptr<framework::ExecutorPrepareContext>(nullptr)); server_thread_.reset(new std::thread(RunServer, rpc_service_));
// FIXME(typhoonzero): do we need to wait until the server port is ready?
// TODO(qiao) set proper fields for table lookup and update sleep(5);
rpc_service_->SetExecutor(&executor);
rpc_service_->SetPrefetchBlkdId(0); // TODO(typhoonzero): change this to a while_op for every cluster-batch.
rpc_service_->SetProgram(program); bool exit_flag = false;
// Record received sparse variables, so that
// TODO(typhoonzero): change this to a while_op for every cluster-batch. // we could reset those after execute optimize program
bool exit_flag = false; std::vector<framework::Variable *> sparse_vars;
// Record received sparse variables, so that while (!exit_flag) {
// we could reset those after execute optimize program // Get from multiple trainers, we don't care about the order in which
std::vector<framework::Variable *> sparse_vars; // the gradients arrives, just add suffix 0~n and merge the gradient.
while (!exit_flag) { rpc_service_->SetCond(0);
// Get from multiple trainers, we don't care about the order in which size_t recv_var_cnt = 0;
// the gradients arrives, just add suffix 0~n and merge the gradient. int batch_barrier = 0;
rpc_service_->SetCond(0); while (batch_barrier != fan_in) {
size_t recv_var_cnt = 0; const detail::ReceivedMessage v = rpc_service_->Get();
int batch_barrier = 0; auto recv_var_name = v.first;
while (batch_barrier != fan_in) { if (recv_var_name == LISTEN_TERMINATE_MESSAGE) {
const detail::ReceivedMessage v = rpc_service_->Get(); LOG(INFO) << "received terminate message and exit";
auto recv_var_name = v.first; exit_flag = true;
if (recv_var_name == LISTEN_TERMINATE_MESSAGE) {
LOG(INFO) << "received terminate message and exit";
exit_flag = true;
break;
} else if (recv_var_name == BATCH_BARRIER_MESSAGE) {
VLOG(3) << "recv batch barrier message";
batch_barrier++;
continue;
} else {
VLOG(3) << "received grad: " << recv_var_name;
recv_var_cnt++;
auto var = v.second->GetVar();
if (var == nullptr) {
LOG(ERROR) << "Can not find server side var: " << recv_var_name;
PADDLE_THROW("Can not find server side var");
}
if (var->IsType<framework::SelectedRows>()) {
sparse_vars.push_back(var);
}
}
}
if (exit_flag) {
rpc_service_->SetCond(1);
rpc_service_->ShutDown();
break; break;
} } else if (recv_var_name == BATCH_BARRIER_MESSAGE) {
VLOG(3) << "recv batch barrier message";
// NOTE: if is_gpu_place, CUDA kernels are laugched by multiple threads batch_barrier++;
// and this will still work. continue;
} else {
// The optimize blocks which have the same parent ID would run parallel VLOG(3) << "received grad: " << recv_var_name;
// TODO(Yancey1989): need to use ParallelExecutor for future recv_var_cnt++;
int32_t last_parent_blkid = program->Block(1).Parent(); auto var = v.second->GetVar();
std::vector<size_t> parallel_blkids; if (var == nullptr) {
parallel_blkids.push_back(1); LOG(ERROR) << "Can not find server side var: " << recv_var_name;
double ts = detail::GetTimestamp(); PADDLE_THROW("Can not find server side var");
for (size_t blkid = 2; blkid < num_blocks; ++blkid) { }
if (program->Block(blkid).Parent() != last_parent_blkid) { if (var->IsType<framework::SelectedRows>()) {
for (size_t idx : parallel_blkids) VLOG(3) << idx; sparse_vars.push_back(var);
ParallelExecuteBlocks(parallel_blkids, &executor, prepared, program,
&recv_scope);
parallel_blkids.clear();
last_parent_blkid = program->Block(blkid).Parent();
} }
parallel_blkids.push_back(blkid);
}
ParallelExecuteBlocks(parallel_blkids, &executor, prepared, program,
&recv_scope);
VLOG(3) << "run all blocks spent " << detail::GetTimestamp() - ts
<< "(ms)";
// Reset the received sparse variables, the sum operator would not
// sum the input sparse variables which rows is empty at the next
// mini-batch.
// TODO(Yancey1989): move the reset action into an operator, we couldn't
// have any hide logic in the operator.
for (auto &var : sparse_vars) {
var->GetMutable<framework::SelectedRows>()->mutable_rows()->clear();
} }
}
if (exit_flag) {
rpc_service_->SetCond(1); rpc_service_->SetCond(1);
// NOTE: does not consider barrier request retry in here, we may use rpc_service_->ShutDown();
// global barrier id to resolve this. break;
rpc_service_->WaitClientGet(fan_in); }
sparse_vars.clear();
} // while(true)
}
protected: // NOTE: if is_gpu_place, CUDA kernels are laugched by multiple threads
std::shared_ptr<detail::AsyncGRPCServer> rpc_service_; // and this will still work.
std::shared_ptr<std::thread> server_thread_;
}; // The optimize blocks which have the same parent ID would run parallel
// TODO(Yancey1989): need to use ParallelExecutor for future
int32_t last_parent_blkid = program->Block(1).Parent();
std::vector<size_t> parallel_blkids;
parallel_blkids.push_back(1);
double ts = detail::GetTimestamp();
for (size_t blkid = 2; blkid < num_blocks; ++blkid) {
if (program->Block(blkid).Parent() != last_parent_blkid) {
ParallelExecuteBlocks(parallel_blkids, &executor, prepared, program,
&recv_scope);
parallel_blkids.clear();
last_parent_blkid = program->Block(blkid).Parent();
}
parallel_blkids.push_back(blkid);
}
ParallelExecuteBlocks(parallel_blkids, &executor, prepared, program,
&recv_scope);
VLOG(2) << "run all blocks spent " << detail::GetTimestamp() - ts << "(ms)";
// Reset the received sparse variables, the sum operator would not
// sum the input sparse variables which rows is empty at the next
// mini-batch.
// TODO(Yancey1989): move the reset action into an operator, we couldn't
// have any hide logic in the operator.
for (auto &var : sparse_vars) {
var->GetMutable<framework::SelectedRows>()->mutable_rows()->clear();
}
rpc_service_->SetCond(1);
// FIXME(typhoonzero): use another condition to sync wait clients get.
rpc_service_->WaitClientGet(fan_in);
sparse_vars.clear();
} // while(true)
}
class ListenAndServOpMaker : public framework::OpProtoAndCheckerMaker { class ListenAndServOpMaker : public framework::OpProtoAndCheckerMaker {
public: public:
......
/* 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 <stdint.h>
#include <ostream>
#include "paddle/fluid/framework/executor.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/threadpool.h"
#include "paddle/fluid/operators/detail/grpc_server.h"
namespace paddle {
namespace operators {
constexpr char kOptimizeBlock[] = "OptimizeBlock";
void RunServer(std::shared_ptr<detail::AsyncGRPCServer> service);
class ListenAndServOp : public framework::OperatorBase {
public:
ListenAndServOp(const std::string &type,
const framework::VariableNameMap &inputs,
const framework::VariableNameMap &outputs,
const framework::AttributeMap &attrs);
int GetSelectedPort();
void Stop() override;
void RunImpl(const framework::Scope &scope,
const platform::Place &dev_place) const override;
protected:
mutable std::shared_ptr<detail::AsyncGRPCServer> rpc_service_;
mutable std::shared_ptr<std::thread> server_thread_;
};
} // namespace operators
} // namespace paddle
...@@ -20,6 +20,7 @@ limitations under the License. */ ...@@ -20,6 +20,7 @@ limitations under the License. */
#include "paddle/fluid/framework/op_registry.h" #include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h" #include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h" #include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/operators/listen_and_serv_op.h"
#include "paddle/fluid/operators/math/math_function.h" #include "paddle/fluid/operators/math/math_function.h"
#include "paddle/fluid/operators/math/selected_rows_functor.h" #include "paddle/fluid/operators/math/selected_rows_functor.h"
#include "paddle/fluid/string/printf.h" #include "paddle/fluid/string/printf.h"
...@@ -34,6 +35,7 @@ namespace m = paddle::operators::math; ...@@ -34,6 +35,7 @@ namespace m = paddle::operators::math;
// global for simplicity. // global for simplicity.
std::unique_ptr<f::OperatorBase> listen_and_serv_op; std::unique_ptr<f::OperatorBase> listen_and_serv_op;
int selected_port;
void InitTensorsInScope(f::Scope &scope, p::CPUPlace &place) { void InitTensorsInScope(f::Scope &scope, p::CPUPlace &place) {
p::CPUDeviceContext ctx(place); p::CPUDeviceContext ctx(place);
...@@ -128,14 +130,16 @@ void StartServerNet(bool is_sparse) { ...@@ -128,14 +130,16 @@ void StartServerNet(bool is_sparse) {
AddOp("sum", {{"X", {"x0", "x1"}}}, {{"Out", {"Out"}}}, {}, optimize_block); AddOp("sum", {{"X", {"x0", "x1"}}}, {{"Out", {"Out"}}}, {}, optimize_block);
f::AttributeMap attrs; f::AttributeMap attrs;
attrs.insert({"endpoint", std::string("127.0.0.1:6174")}); attrs.insert({"endpoint", std::string("127.0.0.1:0")});
attrs.insert({"Fanin", 1}); attrs.insert({"Fanin", 1});
attrs.insert({"ParamList", std::vector<std::string>({"Out"})}); attrs.insert({"ParamList", std::vector<std::string>({"Out"})});
attrs.insert({"GradList", std::vector<std::string>({"x1"})}); attrs.insert({"GradList", std::vector<std::string>({"x1"})});
attrs.insert({"OptimizeBlock", optimize_block}); attrs.insert({"OptimizeBlock", optimize_block});
listen_and_serv_op = listen_and_serv_op =
f::OpRegistry::CreateOp("listen_and_serv", {{"X", {"x1"}}}, {}, attrs); f::OpRegistry::CreateOp("listen_and_serv", {{"X", {"x1"}}}, {}, attrs);
LOG(INFO) << "selected port before run " << selected_port;
listen_and_serv_op->Run(scope, place); listen_and_serv_op->Run(scope, place);
LOG(INFO) << "server exit";
} }
TEST(SendRecvOp, CPUDense) { TEST(SendRecvOp, CPUDense) {
...@@ -149,12 +153,19 @@ TEST(SendRecvOp, CPUDense) { ...@@ -149,12 +153,19 @@ TEST(SendRecvOp, CPUDense) {
scope.Var("RPC_CLIENT_VAR"); scope.Var("RPC_CLIENT_VAR");
f::AttributeMap attrs; f::AttributeMap attrs;
attrs.insert({"endpoints", std::vector<std::string>({"127.0.0.1:6174"})}); selected_port = static_cast<paddle::operators::ListenAndServOp *>(
attrs.insert({"epmap", std::vector<std::string>({"127.0.0.1:6174"})}); listen_and_serv_op.get())
->GetSelectedPort();
LOG(INFO) << "selected port " << selected_port;
std::string endpoint = paddle::string::Sprintf("127.0.0.1:%d", selected_port);
attrs.insert({"endpoints", std::vector<std::string>({endpoint})});
attrs.insert({"epmap", std::vector<std::string>({endpoint})});
auto send_op = f::OpRegistry::CreateOp( auto send_op = f::OpRegistry::CreateOp(
"send", {{"X", {"x1"}}}, "send", {{"X", {"x1"}}},
{{"Out", {"Out"}}, {"RPCClient", {"RPC_CLIENT_VAR"}}}, attrs); {{"Out", {"Out"}}, {"RPCClient", {"RPC_CLIENT_VAR"}}}, attrs);
LOG(INFO) << "before run " << endpoint;
send_op->Run(scope, place); send_op->Run(scope, place);
LOG(INFO) << "end run";
auto in_var = scope.Var("x1"); auto in_var = scope.Var("x1");
auto tensor = in_var->GetMutable<f::LoDTensor>(); auto tensor = in_var->GetMutable<f::LoDTensor>();
...@@ -167,6 +178,7 @@ TEST(SendRecvOp, CPUDense) { ...@@ -167,6 +178,7 @@ TEST(SendRecvOp, CPUDense) {
for (int64_t i = 0; i < target->numel(); ++i) { for (int64_t i = 0; i < target->numel(); ++i) {
EXPECT_EQ(expected[i] * 2, actual[i]); EXPECT_EQ(expected[i] * 2, actual[i]);
} }
LOG(INFO) << "before stop";
listen_and_serv_op->Stop(); listen_and_serv_op->Stop();
server_thread.join(); server_thread.join();
listen_and_serv_op.reset(nullptr); listen_and_serv_op.reset(nullptr);
...@@ -182,8 +194,13 @@ TEST(SendRecvOp, CPUSparse) { ...@@ -182,8 +194,13 @@ TEST(SendRecvOp, CPUSparse) {
InitSelectedRowsInScope(scope, place); InitSelectedRowsInScope(scope, place);
scope.Var("RPC_CLIENT_VAR"); scope.Var("RPC_CLIENT_VAR");
f::AttributeMap attrs; f::AttributeMap attrs;
attrs.insert({"endpoints", std::vector<std::string>({"127.0.0.1:6174"})}); selected_port = static_cast<paddle::operators::ListenAndServOp *>(
attrs.insert({"epmap", std::vector<std::string>({"127.0.0.1:6174"})}); listen_and_serv_op.get())
->GetSelectedPort();
LOG(INFO) << "selected port " << selected_port;
std::string endpoint = paddle::string::Sprintf("127.0.0.1:%d", selected_port);
attrs.insert({"endpoints", std::vector<std::string>({endpoint})});
attrs.insert({"epmap", std::vector<std::string>({endpoint})});
auto send_op = f::OpRegistry::CreateOp( auto send_op = f::OpRegistry::CreateOp(
"send", {{"X", {"x1"}}}, "send", {{"X", {"x1"}}},
{{"Out", {"Out"}}, {"RPCClient", {"RPC_CLIENT_VAR"}}}, attrs); {{"Out", {"Out"}}, {"RPCClient", {"RPC_CLIENT_VAR"}}}, attrs);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册