Support multiple threads per cq sharing, add tests

pull/9904/head
Vijay Pai 8 years ago
parent 275bc932d2
commit 4b07aab513
  1. 6
      src/proto/grpc/testing/control.proto
  2. 47
      test/cpp/qps/client_async.cc
  3. 18
      test/cpp/qps/server_async.cc
  4. 1252
      tools/run_tests/generated/tests.json
  5. 64
      tools/run_tests/performance/scenario_config.py

@ -117,6 +117,9 @@ message ClientConfig {
repeated ChannelArg channel_args = 16; repeated ChannelArg channel_args = 16;
// Number of threads that share each completion queue
int32 threads_per_cq = 17;
// Number of messages on a stream before it gets finished/restarted // Number of messages on a stream before it gets finished/restarted
int32 messages_per_stream = 18; int32 messages_per_stream = 18;
} }
@ -157,6 +160,9 @@ message ServerConfig {
// If we use an OTHER_SERVER client_type, this string gives more detail // If we use an OTHER_SERVER client_type, this string gives more detail
string other_server_api = 11; string other_server_api = 11;
// Number of threads that share each completion queue
int32 threads_per_cq = 12;
// c++-only options (for now) -------------------------------- // c++-only options (for now) --------------------------------
// Buffer pool size (no buffer pool specified if unset) // Buffer pool size (no buffer pool specified if unset)

@ -70,6 +70,11 @@ class ClientRpcContext {
} }
virtual void Start(CompletionQueue* cq, const ClientConfig& config) = 0; virtual void Start(CompletionQueue* cq, const ClientConfig& config) = 0;
void lock() { mu_.lock(); }
void unlock() { mu_.unlock(); }
private:
std::mutex mu_;
}; };
template <class RequestType, class ResponseType> template <class RequestType, class ResponseType>
@ -121,6 +126,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
void StartNewClone(CompletionQueue* cq) override { void StartNewClone(CompletionQueue* cq) override {
auto* clone = new ClientRpcContextUnaryImpl(stub_, req_, next_issue_, auto* clone = new ClientRpcContextUnaryImpl(stub_, req_, next_issue_,
start_req_, callback_); start_req_, callback_);
std::lock_guard<ClientRpcContext> lclone(*clone);
clone->StartInternal(cq); clone->StartInternal(cq);
} }
@ -178,8 +184,14 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
num_async_threads_(NumThreads(config)) { num_async_threads_(NumThreads(config)) {
SetupLoadTest(config, num_async_threads_); SetupLoadTest(config, num_async_threads_);
for (int i = 0; i < num_async_threads_; i++) { int tpc = std::max(1, config.threads_per_cq()); // 1 if unspecified
int num_cqs = (num_async_threads_ + tpc - 1) / tpc; // ceiling operator
for (int i = 0; i < num_cqs; i++) {
cli_cqs_.emplace_back(new CompletionQueue); cli_cqs_.emplace_back(new CompletionQueue);
}
for (int i = 0; i < num_async_threads_; i++) {
cq_.emplace_back(i % cli_cqs_.size());
next_issuers_.emplace_back(NextIssuer(i)); next_issuers_.emplace_back(NextIssuer(i));
shutdown_state_.emplace_back(new PerThreadShutdownState()); shutdown_state_.emplace_back(new PerThreadShutdownState());
} }
@ -246,20 +258,36 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
void* got_tag; void* got_tag;
bool ok; bool ok;
if (cli_cqs_[thread_idx]->Next(&got_tag, &ok)) { if (cli_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
// Got a regular event, so process it // Got a regular event, so process it
ClientRpcContext* ctx = ClientRpcContext::detag(got_tag); ClientRpcContext* ctx = ClientRpcContext::detag(got_tag);
// Proceed while holding a lock to make sure that // Proceed while holding a lock to make sure that
// this thread isn't supposed to shut down // this thread isn't supposed to shut down
std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex); std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex);
if (shutdown_state_[thread_idx]->shutdown) { if (shutdown_state_[thread_idx]->shutdown) {
// We want to delete the context. However, it is possible that
// another thread that just initiated an action on this
// context still has its lock even though the action on the
// context has completed. To delay for that, just grab the
// lock for serialization. Take a new scope.
{ std::lock_guard<ClientRpcContext> lctx(*ctx); }
delete ctx; delete ctx;
return true; return true;
} else if (!ctx->RunNextState(ok, entry)) { }
// The RPC and callback are done, so clone the ctx bool del = false;
// and kickstart the new one
ctx->StartNewClone(cli_cqs_[thread_idx].get()); // Create a new scope for a lock_guard'ed region
// delete the old version {
std::lock_guard<ClientRpcContext> lctx(*ctx);
if (!ctx->RunNextState(ok, entry)) {
// The RPC and callback are done, so clone the ctx
// and kickstart the new one
ctx->StartNewClone(cli_cqs_[cq_[thread_idx]].get());
// set the old version to delete
del = true;
}
}
if (del) {
delete ctx; delete ctx;
} }
return true; return true;
@ -270,6 +298,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
} }
std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_; std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
std::vector<int> cq_;
std::vector<std::function<gpr_timespec()>> next_issuers_; std::vector<std::function<gpr_timespec()>> next_issuers_;
std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_; std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_;
}; };
@ -392,6 +421,7 @@ class ClientRpcContextStreamingPingPongImpl : public ClientRpcContext {
void StartNewClone(CompletionQueue* cq) override { void StartNewClone(CompletionQueue* cq) override {
auto* clone = new ClientRpcContextStreamingPingPongImpl( auto* clone = new ClientRpcContextStreamingPingPongImpl(
stub_, req_, next_issue_, start_req_, callback_); stub_, req_, next_issue_, start_req_, callback_);
std::lock_guard<ClientRpcContext> lclone(*clone);
clone->StartInternal(cq, messages_per_stream_); clone->StartInternal(cq, messages_per_stream_);
} }
@ -530,6 +560,7 @@ class ClientRpcContextStreamingFromClientImpl : public ClientRpcContext {
void StartNewClone(CompletionQueue* cq) override { void StartNewClone(CompletionQueue* cq) override {
auto* clone = new ClientRpcContextStreamingFromClientImpl( auto* clone = new ClientRpcContextStreamingFromClientImpl(
stub_, req_, next_issue_, start_req_, callback_); stub_, req_, next_issue_, start_req_, callback_);
std::lock_guard<ClientRpcContext> lclone(*clone);
clone->StartInternal(cq); clone->StartInternal(cq);
} }
@ -647,6 +678,7 @@ class ClientRpcContextStreamingFromServerImpl : public ClientRpcContext {
void StartNewClone(CompletionQueue* cq) override { void StartNewClone(CompletionQueue* cq) override {
auto* clone = new ClientRpcContextStreamingFromServerImpl( auto* clone = new ClientRpcContextStreamingFromServerImpl(
stub_, req_, next_issue_, start_req_, callback_); stub_, req_, next_issue_, start_req_, callback_);
std::lock_guard<ClientRpcContext> lclone(*clone);
clone->StartInternal(cq); clone->StartInternal(cq);
} }
@ -789,6 +821,7 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
void StartNewClone(CompletionQueue* cq) override { void StartNewClone(CompletionQueue* cq) override {
auto* clone = new ClientRpcContextGenericStreamingImpl( auto* clone = new ClientRpcContextGenericStreamingImpl(
stub_, req_, next_issue_, start_req_, callback_); stub_, req_, next_issue_, start_req_, callback_);
std::lock_guard<ClientRpcContext> lclone(*clone);
clone->StartInternal(cq, messages_per_stream_); clone->StartInternal(cq, messages_per_stream_);
} }

@ -31,6 +31,7 @@
* *
*/ */
#include <algorithm>
#include <forward_list> #include <forward_list>
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -104,9 +105,14 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
gpr_log(GPR_INFO, "Sizing async server to %d threads", num_threads); gpr_log(GPR_INFO, "Sizing async server to %d threads", num_threads);
} }
for (int i = 0; i < num_threads; i++) { int tpc = std::max(1, config.threads_per_cq()); // 1 if unspecified
int num_cqs = (num_threads + tpc - 1) / tpc; // ceiling operator
for (int i = 0; i < num_cqs; i++) {
srv_cqs_.emplace_back(builder.AddCompletionQueue()); srv_cqs_.emplace_back(builder.AddCompletionQueue());
} }
for (int i = 0; i < num_threads; i++) {
cq_.emplace_back(i % srv_cqs_.size());
}
if (config.resource_quota_size() > 0) { if (config.resource_quota_size() > 0) {
builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest") builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest")
@ -120,7 +126,7 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
std::placeholders::_2); std::placeholders::_2);
for (int i = 0; i < 5000; i++) { for (int i = 0; i < 5000; i++) {
for (int j = 0; j < num_threads; j++) { for (int j = 0; j < num_cqs; j++) {
if (request_unary_function) { if (request_unary_function) {
auto request_unary = std::bind( auto request_unary = std::bind(
request_unary_function, &async_service_, std::placeholders::_1, request_unary_function, &async_service_, std::placeholders::_1,
@ -205,7 +211,7 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
// Wait until work is available or we are shutting down // Wait until work is available or we are shutting down
bool ok; bool ok;
void *got_tag; void *got_tag;
while (srv_cqs_[thread_idx]->Next(&got_tag, &ok)) { while (srv_cqs_[cq_[thread_idx]]->Next(&got_tag, &ok)) {
ServerRpcContext *ctx = detag(got_tag); ServerRpcContext *ctx = detag(got_tag);
// The tag is a pointer to an RPC context to invoke // The tag is a pointer to an RPC context to invoke
// Proceed while holding a lock to make sure that // Proceed while holding a lock to make sure that
@ -214,6 +220,7 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
if (shutdown_state_[thread_idx]->shutdown) { if (shutdown_state_[thread_idx]->shutdown) {
return; return;
} }
std::lock_guard<ServerRpcContext> l2(*ctx);
const bool still_going = ctx->RunNextState(ok); const bool still_going = ctx->RunNextState(ok);
// if this RPC context is done, refresh it // if this RPC context is done, refresh it
if (!still_going) { if (!still_going) {
@ -226,9 +233,13 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
class ServerRpcContext { class ServerRpcContext {
public: public:
ServerRpcContext() {} ServerRpcContext() {}
void lock() { mu_.lock(); }
void unlock() { mu_.unlock(); }
virtual ~ServerRpcContext(){}; virtual ~ServerRpcContext(){};
virtual bool RunNextState(bool) = 0; // next state, return false if done virtual bool RunNextState(bool) = 0; // next state, return false if done
virtual void Reset() = 0; // start this back at a clean state virtual void Reset() = 0; // start this back at a clean state
private:
std::mutex mu_;
}; };
static void *tag(ServerRpcContext *func) { static void *tag(ServerRpcContext *func) {
return reinterpret_cast<void *>(func); return reinterpret_cast<void *>(func);
@ -518,6 +529,7 @@ class AsyncQpsServerTest final : public grpc::testing::Server {
std::vector<std::thread> threads_; std::vector<std::thread> threads_;
std::unique_ptr<grpc::Server> server_; std::unique_ptr<grpc::Server> server_;
std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_; std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
std::vector<int> cq_;
ServiceType async_service_; ServiceType async_service_;
std::vector<std::unique_ptr<ServerRpcContext>> contexts_; std::vector<std::unique_ptr<ServerRpcContext>> contexts_;

File diff suppressed because it is too large Load Diff

@ -108,6 +108,8 @@ def _ping_pong_scenario(name, rpc_type,
client_language=None, client_language=None,
server_language=None, server_language=None,
async_server_threads=0, async_server_threads=0,
server_threads_per_cq=0,
client_threads_per_cq=0,
warmup_seconds=WARMUP_SECONDS, warmup_seconds=WARMUP_SECONDS,
categories=DEFAULT_CATEGORIES, categories=DEFAULT_CATEGORIES,
channels=None, channels=None,
@ -127,6 +129,7 @@ def _ping_pong_scenario(name, rpc_type,
'outstanding_rpcs_per_channel': 1, 'outstanding_rpcs_per_channel': 1,
'client_channels': 1, 'client_channels': 1,
'async_client_threads': 1, 'async_client_threads': 1,
'threads_per_cq': client_threads_per_cq,
'rpc_type': rpc_type, 'rpc_type': rpc_type,
'load_params': { 'load_params': {
'closed_loop': {} 'closed_loop': {}
@ -137,6 +140,7 @@ def _ping_pong_scenario(name, rpc_type,
'server_type': server_type, 'server_type': server_type,
'security_params': _get_secargs(secure), 'security_params': _get_secargs(secure),
'async_server_threads': async_server_threads, 'async_server_threads': async_server_threads,
'threads_per_cq': server_threads_per_cq,
}, },
'warmup_seconds': warmup_seconds, 'warmup_seconds': warmup_seconds,
'benchmark_seconds': BENCHMARK_SECONDS 'benchmark_seconds': BENCHMARK_SECONDS
@ -280,6 +284,66 @@ class CXXLanguage:
secure=secure, secure=secure,
categories=smoketest_categories+[SCALABLE]) categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_unconstrained_1cq_%s' % secstr,
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_GENERIC_SERVER',
unconstrained_client='async', use_generic_payload=True,
secure=secure,
client_threads_per_cq=1000000, server_threads_per_cq=1000000,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_unconstrained_2waysharedcq_%s' % secstr,
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_GENERIC_SERVER',
unconstrained_client='async', use_generic_payload=True,
secure=secure,
client_threads_per_cq=2, server_threads_per_cq=2,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_protobuf_async_streaming_qps_unconstrained_1cq_%s' % secstr,
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_SERVER',
unconstrained_client='async',
secure=secure,
client_threads_per_cq=1000000, server_threads_per_cq=1000000,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_protobuf_async_streaming_qps_unconstrained_2waysharedcq_%s' % secstr,
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_SERVER',
unconstrained_client='async',
secure=secure,
client_threads_per_cq=2, server_threads_per_cq=2,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_protobuf_async_unary_qps_unconstrained_1cq_%s' % secstr,
rpc_type='UNARY',
client_type='ASYNC_CLIENT',
server_type='ASYNC_SERVER',
unconstrained_client='async',
secure=secure,
client_threads_per_cq=1000000, server_threads_per_cq=1000000,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario(
'cpp_protobuf_async_unary_qps_unconstrained_2waysharedcq_%s' % secstr,
rpc_type='UNARY',
client_type='ASYNC_CLIENT',
server_type='ASYNC_SERVER',
unconstrained_client='async',
secure=secure,
client_threads_per_cq=2, server_threads_per_cq=2,
categories=smoketest_categories+[SCALABLE])
yield _ping_pong_scenario( yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_one_server_core_%s' % secstr, 'cpp_generic_async_streaming_qps_one_server_core_%s' % secstr,
rpc_type='STREAMING', rpc_type='STREAMING',

Loading…
Cancel
Save