mirror of https://github.com/grpc/grpc.git
commit
0bc6805c6d
23 changed files with 1730 additions and 960 deletions
@ -1,252 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, Google Inc. |
|
||||||
* All rights reserved. |
|
||||||
* |
|
||||||
* Redistribution and use in source and binary forms, with or without |
|
||||||
* modification, are permitted provided that the following conditions are |
|
||||||
* met: |
|
||||||
* |
|
||||||
* * Redistributions of source code must retain the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer. |
|
||||||
* * Redistributions in binary form must reproduce the above |
|
||||||
* copyright notice, this list of conditions and the following disclaimer |
|
||||||
* in the documentation and/or other materials provided with the |
|
||||||
* distribution. |
|
||||||
* * Neither the name of Google Inc. nor the names of its |
|
||||||
* contributors may be used to endorse or promote products derived from |
|
||||||
* this software without specific prior written permission. |
|
||||||
* |
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||||
* |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <cassert> |
|
||||||
#include <memory> |
|
||||||
#include <string> |
|
||||||
#include <thread> |
|
||||||
#include <vector> |
|
||||||
#include <sstream> |
|
||||||
|
|
||||||
#include <grpc/grpc.h> |
|
||||||
#include <grpc/support/histogram.h> |
|
||||||
#include <grpc/support/log.h> |
|
||||||
#include <gflags/gflags.h> |
|
||||||
#include <grpc++/client_context.h> |
|
||||||
#include <grpc++/status.h> |
|
||||||
#include "test/core/util/grpc_profiler.h" |
|
||||||
#include "test/cpp/util/create_test_channel.h" |
|
||||||
#include "test/cpp/qps/qpstest.pb.h" |
|
||||||
|
|
||||||
DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); |
|
||||||
DEFINE_int32(server_port, 0, "Server port."); |
|
||||||
DEFINE_string(server_host, "127.0.0.1", "Server host."); |
|
||||||
DEFINE_int32(client_threads, 4, "Number of client threads."); |
|
||||||
|
|
||||||
// We have a configurable number of channels for sending RPCs.
|
|
||||||
// RPCs are sent round-robin on the available channels by the
|
|
||||||
// various threads. Interesting cases are 1 global channel or
|
|
||||||
// 1 per-thread channel, but we can support any number.
|
|
||||||
// The channels are assigned round-robin on an RPC by RPC basis
|
|
||||||
// rather than just at initialization time in order to also measure the
|
|
||||||
// impact of cache thrashing caused by channel changes. This is an issue
|
|
||||||
// if you are not in one of the above "interesting cases"
|
|
||||||
DEFINE_int32(client_channels, 4, "Number of client channels."); |
|
||||||
|
|
||||||
DEFINE_int32(num_rpcs, 1000, "Number of RPCs per thread."); |
|
||||||
DEFINE_int32(payload_size, 1, "Payload size in bytes"); |
|
||||||
|
|
||||||
// Alternatively, specify parameters for test as a workload so that multiple
|
|
||||||
// tests are initiated back-to-back. This is convenient for keeping a borg
|
|
||||||
// allocation consistent. This is a space-separated list of
|
|
||||||
// [threads channels num_rpcs payload_size ]*
|
|
||||||
DEFINE_string(workload, "", "Workload parameters"); |
|
||||||
|
|
||||||
using grpc::ChannelInterface; |
|
||||||
using grpc::CreateTestChannel; |
|
||||||
using grpc::testing::ServerStats; |
|
||||||
using grpc::testing::SimpleRequest; |
|
||||||
using grpc::testing::SimpleResponse; |
|
||||||
using grpc::testing::StatsRequest; |
|
||||||
using grpc::testing::TestService; |
|
||||||
|
|
||||||
// In some distros, gflags is in the namespace google, and in some others,
|
|
||||||
// in gflags. This hack is enabling us to find both.
|
|
||||||
namespace google { } |
|
||||||
namespace gflags { } |
|
||||||
using namespace google; |
|
||||||
using namespace gflags; |
|
||||||
|
|
||||||
static double now() { |
|
||||||
gpr_timespec tv = gpr_now(); |
|
||||||
return 1e9 * tv.tv_sec + tv.tv_nsec; |
|
||||||
} |
|
||||||
|
|
||||||
void RunTest(const int client_threads, const int client_channels, |
|
||||||
const int num_rpcs, const int payload_size) { |
|
||||||
gpr_log(GPR_INFO, |
|
||||||
"QPS test with parameters\n" |
|
||||||
"enable_ssl = %d\n" |
|
||||||
"client_channels = %d\n" |
|
||||||
"client_threads = %d\n" |
|
||||||
"num_rpcs = %d\n" |
|
||||||
"payload_size = %d\n" |
|
||||||
"server_host:server_port = %s:%d\n\n", |
|
||||||
FLAGS_enable_ssl, client_channels, client_threads, num_rpcs, |
|
||||||
payload_size, FLAGS_server_host.c_str(), FLAGS_server_port); |
|
||||||
|
|
||||||
std::ostringstream oss; |
|
||||||
oss << FLAGS_server_host << ":" << FLAGS_server_port; |
|
||||||
|
|
||||||
class ClientChannelInfo { |
|
||||||
public: |
|
||||||
explicit ClientChannelInfo(const grpc::string &server) |
|
||||||
: channel_(CreateTestChannel(server, FLAGS_enable_ssl)), |
|
||||||
stub_(TestService::NewStub(channel_)) {} |
|
||||||
ChannelInterface *get_channel() { return channel_.get(); } |
|
||||||
TestService::Stub *get_stub() { return stub_.get(); } |
|
||||||
|
|
||||||
private: |
|
||||||
std::shared_ptr<ChannelInterface> channel_; |
|
||||||
std::unique_ptr<TestService::Stub> stub_; |
|
||||||
}; |
|
||||||
|
|
||||||
std::vector<ClientChannelInfo> channels; |
|
||||||
for (int i = 0; i < client_channels; i++) { |
|
||||||
channels.push_back(ClientChannelInfo(oss.str())); |
|
||||||
} |
|
||||||
|
|
||||||
std::vector<std::thread> threads; // Will add threads when ready to execute
|
|
||||||
std::vector< ::gpr_histogram *> thread_stats(client_threads); |
|
||||||
|
|
||||||
TestService::Stub *stub_stats = channels[0].get_stub(); |
|
||||||
grpc::ClientContext context_stats_begin; |
|
||||||
StatsRequest stats_request; |
|
||||||
ServerStats server_stats_begin; |
|
||||||
stats_request.set_test_num(0); |
|
||||||
grpc::Status status_beg = stub_stats->CollectServerStats( |
|
||||||
&context_stats_begin, stats_request, &server_stats_begin); |
|
||||||
|
|
||||||
grpc_profiler_start("qps_client.prof"); |
|
||||||
|
|
||||||
for (int i = 0; i < client_threads; i++) { |
|
||||||
gpr_histogram *hist = gpr_histogram_create(0.01, 60e9); |
|
||||||
GPR_ASSERT(hist != NULL); |
|
||||||
thread_stats[i] = hist; |
|
||||||
|
|
||||||
threads.push_back( |
|
||||||
std::thread([hist, client_threads, client_channels, num_rpcs, |
|
||||||
payload_size, &channels](int channel_num) { |
|
||||||
SimpleRequest request; |
|
||||||
SimpleResponse response; |
|
||||||
request.set_response_type( |
|
||||||
grpc::testing::PayloadType::COMPRESSABLE); |
|
||||||
request.set_response_size(payload_size); |
|
||||||
|
|
||||||
for (int j = 0; j < num_rpcs; j++) { |
|
||||||
TestService::Stub *stub = |
|
||||||
channels[channel_num].get_stub(); |
|
||||||
double start = now(); |
|
||||||
grpc::ClientContext context; |
|
||||||
grpc::Status s = |
|
||||||
stub->UnaryCall(&context, request, &response); |
|
||||||
gpr_histogram_add(hist, now() - start); |
|
||||||
|
|
||||||
GPR_ASSERT((s.code() == grpc::StatusCode::OK) && |
|
||||||
(response.payload().type() == |
|
||||||
grpc::testing::PayloadType::COMPRESSABLE) && |
|
||||||
(response.payload().body().length() == |
|
||||||
static_cast<size_t>(payload_size))); |
|
||||||
|
|
||||||
// Now do runtime round-robin assignment of the next
|
|
||||||
// channel number
|
|
||||||
channel_num += client_threads; |
|
||||||
channel_num %= client_channels; |
|
||||||
} |
|
||||||
}, |
|
||||||
i % client_channels)); |
|
||||||
} |
|
||||||
|
|
||||||
gpr_histogram *hist = gpr_histogram_create(0.01, 60e9); |
|
||||||
GPR_ASSERT(hist != NULL); |
|
||||||
for (auto &t : threads) { |
|
||||||
t.join(); |
|
||||||
} |
|
||||||
|
|
||||||
grpc_profiler_stop(); |
|
||||||
|
|
||||||
for (int i = 0; i < client_threads; i++) { |
|
||||||
gpr_histogram *h = thread_stats[i]; |
|
||||||
gpr_log(GPR_INFO, "latency at thread %d (50/90/95/99/99.9): %f/%f/%f/%f/%f", |
|
||||||
i, gpr_histogram_percentile(h, 50), gpr_histogram_percentile(h, 90), |
|
||||||
gpr_histogram_percentile(h, 95), gpr_histogram_percentile(h, 99), |
|
||||||
gpr_histogram_percentile(h, 99.9)); |
|
||||||
gpr_histogram_merge(hist, h); |
|
||||||
gpr_histogram_destroy(h); |
|
||||||
} |
|
||||||
|
|
||||||
gpr_log( |
|
||||||
GPR_INFO, |
|
||||||
"latency across %d threads with %d channels and %d payload " |
|
||||||
"(50/90/95/99/99.9): %f / %f / %f / %f / %f", |
|
||||||
client_threads, client_channels, payload_size, |
|
||||||
gpr_histogram_percentile(hist, 50), gpr_histogram_percentile(hist, 90), |
|
||||||
gpr_histogram_percentile(hist, 95), gpr_histogram_percentile(hist, 99), |
|
||||||
gpr_histogram_percentile(hist, 99.9)); |
|
||||||
gpr_histogram_destroy(hist); |
|
||||||
|
|
||||||
grpc::ClientContext context_stats_end; |
|
||||||
ServerStats server_stats_end; |
|
||||||
grpc::Status status_end = stub_stats->CollectServerStats( |
|
||||||
&context_stats_end, stats_request, &server_stats_end); |
|
||||||
|
|
||||||
double elapsed = server_stats_end.time_now() - server_stats_begin.time_now(); |
|
||||||
int total_rpcs = client_threads * num_rpcs; |
|
||||||
double utime = server_stats_end.time_user() - server_stats_begin.time_user(); |
|
||||||
double stime = |
|
||||||
server_stats_end.time_system() - server_stats_begin.time_system(); |
|
||||||
gpr_log(GPR_INFO, |
|
||||||
"Elapsed time: %.3f\n" |
|
||||||
"RPC Count: %d\n" |
|
||||||
"QPS: %.3f\n" |
|
||||||
"System time: %.3f\n" |
|
||||||
"User time: %.3f\n" |
|
||||||
"Resource usage: %.1f%%\n", |
|
||||||
elapsed, total_rpcs, total_rpcs / elapsed, stime, utime, |
|
||||||
(stime + utime) / elapsed * 100.0); |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char **argv) { |
|
||||||
grpc_init(); |
|
||||||
ParseCommandLineFlags(&argc, &argv, true); |
|
||||||
|
|
||||||
GPR_ASSERT(FLAGS_server_port); |
|
||||||
|
|
||||||
if (FLAGS_workload.length() == 0) { |
|
||||||
RunTest(FLAGS_client_threads, FLAGS_client_channels, FLAGS_num_rpcs, |
|
||||||
FLAGS_payload_size); |
|
||||||
} else { |
|
||||||
std::istringstream workload(FLAGS_workload); |
|
||||||
int client_threads, client_channels, num_rpcs, payload_size; |
|
||||||
workload >> client_threads; |
|
||||||
while (!workload.eof()) { |
|
||||||
workload >> client_channels >> num_rpcs >> payload_size; |
|
||||||
RunTest(client_threads, client_channels, num_rpcs, payload_size); |
|
||||||
workload >> client_threads; |
|
||||||
} |
|
||||||
gpr_log(GPR_INFO, "Done with specified workload."); |
|
||||||
} |
|
||||||
|
|
||||||
grpc_shutdown(); |
|
||||||
return 0; |
|
||||||
} |
|
@ -0,0 +1,173 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_CLIENT_H |
||||||
|
#define TEST_QPS_CLIENT_H |
||||||
|
|
||||||
|
#include "test/cpp/qps/histogram.h" |
||||||
|
#include "test/cpp/qps/timer.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
|
||||||
|
#include <condition_variable> |
||||||
|
#include <mutex> |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
class Client { |
||||||
|
public: |
||||||
|
explicit Client(const ClientConfig& config) : timer_(new Timer) { |
||||||
|
for (int i = 0; i < config.client_channels(); i++) { |
||||||
|
channels_.push_back(ClientChannelInfo( |
||||||
|
config.server_targets(i % config.server_targets_size()), config)); |
||||||
|
} |
||||||
|
request_.set_response_type(grpc::testing::PayloadType::COMPRESSABLE); |
||||||
|
request_.set_response_size(config.payload_size()); |
||||||
|
} |
||||||
|
virtual ~Client() {} |
||||||
|
|
||||||
|
ClientStats Mark() { |
||||||
|
Histogram latencies; |
||||||
|
std::vector<Histogram> to_merge(threads_.size()); |
||||||
|
for (size_t i = 0; i < threads_.size(); i++) { |
||||||
|
threads_[i]->BeginSwap(&to_merge[i]); |
||||||
|
} |
||||||
|
std::unique_ptr<Timer> timer(new Timer); |
||||||
|
timer_.swap(timer); |
||||||
|
for (size_t i = 0; i < threads_.size(); i++) { |
||||||
|
threads_[i]->EndSwap(); |
||||||
|
latencies.Merge(&to_merge[i]); |
||||||
|
} |
||||||
|
|
||||||
|
auto timer_result = timer->Mark(); |
||||||
|
|
||||||
|
ClientStats stats; |
||||||
|
latencies.FillProto(stats.mutable_latencies()); |
||||||
|
stats.set_time_elapsed(timer_result.wall); |
||||||
|
stats.set_time_system(timer_result.system); |
||||||
|
stats.set_time_user(timer_result.user); |
||||||
|
return stats; |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
SimpleRequest request_; |
||||||
|
|
||||||
|
class ClientChannelInfo { |
||||||
|
public: |
||||||
|
ClientChannelInfo(const grpc::string& target, const ClientConfig& config) |
||||||
|
: channel_(CreateTestChannel(target, config.enable_ssl())), |
||||||
|
stub_(TestService::NewStub(channel_)) {} |
||||||
|
ChannelInterface* get_channel() { return channel_.get(); } |
||||||
|
TestService::Stub* get_stub() { return stub_.get(); } |
||||||
|
|
||||||
|
private: |
||||||
|
std::shared_ptr<ChannelInterface> channel_; |
||||||
|
std::unique_ptr<TestService::Stub> stub_; |
||||||
|
}; |
||||||
|
std::vector<ClientChannelInfo> channels_; |
||||||
|
|
||||||
|
void StartThreads(size_t num_threads) { |
||||||
|
for (size_t i = 0; i < num_threads; i++) { |
||||||
|
threads_.emplace_back(new Thread(this, i)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void EndThreads() { threads_.clear(); } |
||||||
|
|
||||||
|
virtual void ThreadFunc(Histogram* histogram, size_t thread_idx) = 0; |
||||||
|
|
||||||
|
private: |
||||||
|
class Thread { |
||||||
|
public: |
||||||
|
Thread(Client* client, size_t idx) |
||||||
|
: done_(false), |
||||||
|
new_(nullptr), |
||||||
|
impl_([this, idx, client]() { |
||||||
|
for (;;) { |
||||||
|
// run the loop body
|
||||||
|
client->ThreadFunc(&histogram_, idx); |
||||||
|
// lock, see if we're done
|
||||||
|
std::lock_guard<std::mutex> g(mu_); |
||||||
|
if (done_) return; |
||||||
|
// also check if we're marking, and swap out the histogram if so
|
||||||
|
if (new_) { |
||||||
|
new_->Swap(&histogram_); |
||||||
|
new_ = nullptr; |
||||||
|
cv_.notify_one(); |
||||||
|
} |
||||||
|
} |
||||||
|
}) {} |
||||||
|
|
||||||
|
~Thread() { |
||||||
|
{ |
||||||
|
std::lock_guard<std::mutex> g(mu_); |
||||||
|
done_ = true; |
||||||
|
} |
||||||
|
impl_.join(); |
||||||
|
} |
||||||
|
|
||||||
|
void BeginSwap(Histogram* n) { |
||||||
|
std::lock_guard<std::mutex> g(mu_); |
||||||
|
new_ = n; |
||||||
|
} |
||||||
|
|
||||||
|
void EndSwap() { |
||||||
|
std::unique_lock<std::mutex> g(mu_); |
||||||
|
cv_.wait(g, [this]() { return new_ == nullptr; }); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
Thread(const Thread&); |
||||||
|
Thread& operator=(const Thread&); |
||||||
|
|
||||||
|
TestService::Stub* stub_; |
||||||
|
ClientConfig config_; |
||||||
|
std::mutex mu_; |
||||||
|
std::condition_variable cv_; |
||||||
|
bool done_; |
||||||
|
Histogram* new_; |
||||||
|
Histogram histogram_; |
||||||
|
std::thread impl_; |
||||||
|
}; |
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Thread>> threads_; |
||||||
|
std::unique_ptr<Timer> timer_; |
||||||
|
}; |
||||||
|
|
||||||
|
std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& args); |
||||||
|
std::unique_ptr<Client> CreateAsyncClient(const ClientConfig& args); |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,93 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <memory> |
||||||
|
#include <mutex> |
||||||
|
#include <string> |
||||||
|
#include <thread> |
||||||
|
#include <vector> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include <sys/signal.h> |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/histogram.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/host_port.h> |
||||||
|
#include <gflags/gflags.h> |
||||||
|
#include <grpc++/client_context.h> |
||||||
|
#include <grpc++/status.h> |
||||||
|
#include <grpc++/server.h> |
||||||
|
#include <grpc++/server_builder.h> |
||||||
|
#include "test/core/util/grpc_profiler.h" |
||||||
|
#include "test/cpp/util/create_test_channel.h" |
||||||
|
#include "test/cpp/qps/client.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
#include "test/cpp/qps/histogram.h" |
||||||
|
#include "test/cpp/qps/timer.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
class SynchronousClient GRPC_FINAL : public Client { |
||||||
|
public: |
||||||
|
SynchronousClient(const ClientConfig& config) : Client(config) { |
||||||
|
size_t num_threads = |
||||||
|
config.outstanding_rpcs_per_channel() * config.client_channels(); |
||||||
|
responses_.resize(num_threads); |
||||||
|
StartThreads(num_threads); |
||||||
|
} |
||||||
|
|
||||||
|
~SynchronousClient() { EndThreads(); } |
||||||
|
|
||||||
|
void ThreadFunc(Histogram* histogram, size_t thread_idx) { |
||||||
|
auto* stub = channels_[thread_idx % channels_.size()].get_stub(); |
||||||
|
double start = Timer::Now(); |
||||||
|
grpc::ClientContext context; |
||||||
|
grpc::Status s = |
||||||
|
stub->UnaryCall(&context, request_, &responses_[thread_idx]); |
||||||
|
histogram->Add((Timer::Now() - start) * 1e9); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<SimpleResponse> responses_; |
||||||
|
}; |
||||||
|
|
||||||
|
std::unique_ptr<Client> CreateSynchronousClient(const ClientConfig& config) { |
||||||
|
return std::unique_ptr<Client>(new SynchronousClient(config)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
@ -0,0 +1,210 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "test/cpp/qps/driver.h" |
||||||
|
#include "src/core/support/env.h" |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/host_port.h> |
||||||
|
#include <grpc++/channel_arguments.h> |
||||||
|
#include <grpc++/client_context.h> |
||||||
|
#include <grpc++/create_channel.h> |
||||||
|
#include <grpc++/stream.h> |
||||||
|
#include <list> |
||||||
|
#include <thread> |
||||||
|
#include <vector> |
||||||
|
#include "test/cpp/qps/histogram.h" |
||||||
|
|
||||||
|
using std::list; |
||||||
|
using std::thread; |
||||||
|
using std::unique_ptr; |
||||||
|
using std::vector; |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
static vector<string> get_hosts(const string& name) { |
||||||
|
char* env = gpr_getenv(name.c_str()); |
||||||
|
if (!env) return vector<string>(); |
||||||
|
|
||||||
|
vector<string> out; |
||||||
|
char* p = env; |
||||||
|
for (;;) { |
||||||
|
char* comma = strchr(p, ','); |
||||||
|
if (comma) { |
||||||
|
out.emplace_back(p, comma); |
||||||
|
p = comma + 1; |
||||||
|
} else { |
||||||
|
out.emplace_back(p); |
||||||
|
gpr_free(env); |
||||||
|
return out; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ScenarioResult RunScenario(const ClientConfig& initial_client_config, |
||||||
|
size_t num_clients, |
||||||
|
const ServerConfig& server_config, |
||||||
|
size_t num_servers) { |
||||||
|
// ClientContext allocator (all are destroyed at scope exit)
|
||||||
|
list<ClientContext> contexts; |
||||||
|
auto alloc_context = [&contexts]() { |
||||||
|
contexts.emplace_back(); |
||||||
|
return &contexts.back(); |
||||||
|
}; |
||||||
|
|
||||||
|
// Get client, server lists
|
||||||
|
auto workers = get_hosts("QPS_WORKERS"); |
||||||
|
ClientConfig client_config = initial_client_config; |
||||||
|
|
||||||
|
// TODO(ctiller): support running multiple configurations, and binpack
|
||||||
|
// client/server pairs
|
||||||
|
// to available workers
|
||||||
|
GPR_ASSERT(workers.size() >= num_clients + num_servers); |
||||||
|
|
||||||
|
// Trim to just what we need
|
||||||
|
workers.resize(num_clients + num_servers); |
||||||
|
|
||||||
|
// Start servers
|
||||||
|
struct ServerData { |
||||||
|
unique_ptr<Worker::Stub> stub; |
||||||
|
unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream; |
||||||
|
}; |
||||||
|
vector<ServerData> servers; |
||||||
|
for (size_t i = 0; i < num_servers; i++) { |
||||||
|
ServerData sd; |
||||||
|
sd.stub = std::move(Worker::NewStub( |
||||||
|
CreateChannelDeprecated(workers[i], ChannelArguments()))); |
||||||
|
ServerArgs args; |
||||||
|
*args.mutable_setup() = server_config; |
||||||
|
sd.stream = std::move(sd.stub->RunServer(alloc_context())); |
||||||
|
GPR_ASSERT(sd.stream->Write(args)); |
||||||
|
ServerStatus init_status; |
||||||
|
GPR_ASSERT(sd.stream->Read(&init_status)); |
||||||
|
char* host; |
||||||
|
char* driver_port; |
||||||
|
char* cli_target; |
||||||
|
gpr_split_host_port(workers[i].c_str(), &host, &driver_port); |
||||||
|
gpr_join_host_port(&cli_target, host, init_status.port()); |
||||||
|
client_config.add_server_targets(cli_target); |
||||||
|
gpr_free(host); |
||||||
|
gpr_free(driver_port); |
||||||
|
gpr_free(cli_target); |
||||||
|
|
||||||
|
servers.push_back(std::move(sd)); |
||||||
|
} |
||||||
|
|
||||||
|
// Start clients
|
||||||
|
struct ClientData { |
||||||
|
unique_ptr<Worker::Stub> stub; |
||||||
|
unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream; |
||||||
|
}; |
||||||
|
vector<ClientData> clients; |
||||||
|
for (size_t i = 0; i < num_clients; i++) { |
||||||
|
ClientData cd; |
||||||
|
cd.stub = std::move(Worker::NewStub( |
||||||
|
CreateChannelDeprecated(workers[i + num_servers], ChannelArguments()))); |
||||||
|
ClientArgs args; |
||||||
|
*args.mutable_setup() = client_config; |
||||||
|
cd.stream = std::move(cd.stub->RunTest(alloc_context())); |
||||||
|
GPR_ASSERT(cd.stream->Write(args)); |
||||||
|
ClientStatus init_status; |
||||||
|
GPR_ASSERT(cd.stream->Read(&init_status)); |
||||||
|
|
||||||
|
clients.push_back(std::move(cd)); |
||||||
|
} |
||||||
|
|
||||||
|
// Let everything warmup
|
||||||
|
gpr_log(GPR_INFO, "Warming up"); |
||||||
|
gpr_timespec start = gpr_now(); |
||||||
|
gpr_sleep_until(gpr_time_add(start, gpr_time_from_seconds(5))); |
||||||
|
|
||||||
|
// Start a run
|
||||||
|
gpr_log(GPR_INFO, "Starting"); |
||||||
|
ServerArgs server_mark; |
||||||
|
server_mark.mutable_mark(); |
||||||
|
ClientArgs client_mark; |
||||||
|
client_mark.mutable_mark(); |
||||||
|
for (auto& server : servers) { |
||||||
|
GPR_ASSERT(server.stream->Write(server_mark)); |
||||||
|
} |
||||||
|
for (auto& client : clients) { |
||||||
|
GPR_ASSERT(client.stream->Write(client_mark)); |
||||||
|
} |
||||||
|
ServerStatus server_status; |
||||||
|
ClientStatus client_status; |
||||||
|
for (auto& server : servers) { |
||||||
|
GPR_ASSERT(server.stream->Read(&server_status)); |
||||||
|
} |
||||||
|
for (auto& client : clients) { |
||||||
|
GPR_ASSERT(client.stream->Read(&client_status)); |
||||||
|
} |
||||||
|
|
||||||
|
// Wait some time
|
||||||
|
gpr_log(GPR_INFO, "Running"); |
||||||
|
gpr_sleep_until(gpr_time_add(start, gpr_time_from_seconds(15))); |
||||||
|
|
||||||
|
// Finish a run
|
||||||
|
ScenarioResult result; |
||||||
|
gpr_log(GPR_INFO, "Finishing"); |
||||||
|
for (auto& server : servers) { |
||||||
|
GPR_ASSERT(server.stream->Write(server_mark)); |
||||||
|
} |
||||||
|
for (auto& client : clients) { |
||||||
|
GPR_ASSERT(client.stream->Write(client_mark)); |
||||||
|
} |
||||||
|
for (auto& server : servers) { |
||||||
|
GPR_ASSERT(server.stream->Read(&server_status)); |
||||||
|
const auto& stats = server_status.stats(); |
||||||
|
result.server_resources.push_back(ResourceUsage{ |
||||||
|
stats.time_elapsed(), stats.time_user(), stats.time_system()}); |
||||||
|
} |
||||||
|
for (auto& client : clients) { |
||||||
|
GPR_ASSERT(client.stream->Read(&client_status)); |
||||||
|
const auto& stats = client_status.stats(); |
||||||
|
result.latencies.MergeProto(stats.latencies()); |
||||||
|
result.client_resources.push_back(ResourceUsage{ |
||||||
|
stats.time_elapsed(), stats.time_user(), stats.time_system()}); |
||||||
|
} |
||||||
|
|
||||||
|
for (auto& client : clients) { |
||||||
|
GPR_ASSERT(client.stream->WritesDone()); |
||||||
|
GPR_ASSERT(client.stream->Finish().IsOk()); |
||||||
|
} |
||||||
|
for (auto& server : servers) { |
||||||
|
GPR_ASSERT(server.stream->WritesDone()); |
||||||
|
GPR_ASSERT(server.stream->Finish().IsOk()); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
@ -0,0 +1,61 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_DRIVER_H |
||||||
|
#define TEST_QPS_DRIVER_H |
||||||
|
|
||||||
|
#include "test/cpp/qps/histogram.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
struct ResourceUsage { |
||||||
|
double wall_time; |
||||||
|
double user_time; |
||||||
|
double system_time; |
||||||
|
}; |
||||||
|
|
||||||
|
struct ScenarioResult { |
||||||
|
Histogram latencies; |
||||||
|
std::vector<ResourceUsage> client_resources; |
||||||
|
std::vector<ResourceUsage> server_resources; |
||||||
|
}; |
||||||
|
|
||||||
|
ScenarioResult RunScenario(const grpc::testing::ClientConfig& client_config, |
||||||
|
size_t num_clients, |
||||||
|
const grpc::testing::ServerConfig& server_config, |
||||||
|
size_t num_servers); |
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,85 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_HISTOGRAM_H |
||||||
|
#define TEST_QPS_HISTOGRAM_H |
||||||
|
|
||||||
|
#include <grpc/support/histogram.h> |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
class Histogram { |
||||||
|
public: |
||||||
|
Histogram() : impl_(gpr_histogram_create(0.01, 60e9)) {} |
||||||
|
~Histogram() { |
||||||
|
if (impl_) gpr_histogram_destroy(impl_); |
||||||
|
} |
||||||
|
Histogram(Histogram&& other) : impl_(other.impl_) { other.impl_ = nullptr; } |
||||||
|
|
||||||
|
void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); } |
||||||
|
void Add(double value) { gpr_histogram_add(impl_, value); } |
||||||
|
double Percentile(double pctile) { |
||||||
|
return gpr_histogram_percentile(impl_, pctile); |
||||||
|
} |
||||||
|
double Count() { return gpr_histogram_count(impl_); } |
||||||
|
void Swap(Histogram* other) { std::swap(impl_, other->impl_); } |
||||||
|
void FillProto(HistogramData* p) { |
||||||
|
size_t n; |
||||||
|
const auto* data = gpr_histogram_get_contents(impl_, &n); |
||||||
|
for (size_t i = 0; i < n; i++) { |
||||||
|
p->add_bucket(data[i]); |
||||||
|
} |
||||||
|
p->set_min_seen(gpr_histogram_minimum(impl_)); |
||||||
|
p->set_max_seen(gpr_histogram_maximum(impl_)); |
||||||
|
p->set_sum(gpr_histogram_sum(impl_)); |
||||||
|
p->set_sum_of_squares(gpr_histogram_sum_of_squares(impl_)); |
||||||
|
p->set_count(gpr_histogram_count(impl_)); |
||||||
|
} |
||||||
|
void MergeProto(const HistogramData& p) { |
||||||
|
gpr_histogram_merge_contents(impl_, &*p.bucket().begin(), p.bucket_size(), |
||||||
|
p.min_seen(), p.max_seen(), p.sum(), |
||||||
|
p.sum_of_squares(), p.count()); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
Histogram(const Histogram&); |
||||||
|
Histogram& operator=(const Histogram&); |
||||||
|
|
||||||
|
gpr_histogram* impl_; |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* TEST_QPS_HISTOGRAM_H */ |
@ -0,0 +1,132 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <gflags/gflags.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "test/cpp/qps/driver.h" |
||||||
|
#include "test/cpp/qps/stats.h" |
||||||
|
|
||||||
|
DEFINE_int32(num_clients, 1, "Number of client binaries"); |
||||||
|
DEFINE_int32(num_servers, 1, "Number of server binaries"); |
||||||
|
|
||||||
|
// Common config
|
||||||
|
DEFINE_bool(enable_ssl, false, "Use SSL"); |
||||||
|
|
||||||
|
// Server config
|
||||||
|
DEFINE_int32(server_threads, 1, "Number of server threads"); |
||||||
|
DEFINE_string(server_type, "SYNCHRONOUS_SERVER", "Server type"); |
||||||
|
|
||||||
|
// Client config
|
||||||
|
DEFINE_int32(outstanding_rpcs_per_channel, 1, |
||||||
|
"Number of outstanding rpcs per channel"); |
||||||
|
DEFINE_int32(client_channels, 1, "Number of client channels"); |
||||||
|
DEFINE_int32(payload_size, 1, "Payload size"); |
||||||
|
DEFINE_string(client_type, "SYNCHRONOUS_CLIENT", "Client type"); |
||||||
|
DEFINE_int32(async_client_threads, 1, "Async client threads"); |
||||||
|
|
||||||
|
using grpc::testing::ClientConfig; |
||||||
|
using grpc::testing::ServerConfig; |
||||||
|
using grpc::testing::ClientType; |
||||||
|
using grpc::testing::ServerType; |
||||||
|
using grpc::testing::ResourceUsage; |
||||||
|
using grpc::testing::sum; |
||||||
|
|
||||||
|
// In some distros, gflags is in the namespace google, and in some others,
|
||||||
|
// in gflags. This hack is enabling us to find both.
|
||||||
|
namespace google {} |
||||||
|
namespace gflags {} |
||||||
|
using namespace google; |
||||||
|
using namespace gflags; |
||||||
|
|
||||||
|
int main(int argc, char **argv) { |
||||||
|
grpc_init(); |
||||||
|
ParseCommandLineFlags(&argc, &argv, true); |
||||||
|
|
||||||
|
ClientType client_type; |
||||||
|
ServerType server_type; |
||||||
|
GPR_ASSERT(ClientType_Parse(FLAGS_client_type, &client_type)); |
||||||
|
GPR_ASSERT(ServerType_Parse(FLAGS_server_type, &server_type)); |
||||||
|
|
||||||
|
ClientConfig client_config; |
||||||
|
client_config.set_client_type(client_type); |
||||||
|
client_config.set_enable_ssl(FLAGS_enable_ssl); |
||||||
|
client_config.set_outstanding_rpcs_per_channel( |
||||||
|
FLAGS_outstanding_rpcs_per_channel); |
||||||
|
client_config.set_client_channels(FLAGS_client_channels); |
||||||
|
client_config.set_payload_size(FLAGS_payload_size); |
||||||
|
client_config.set_async_client_threads(FLAGS_async_client_threads); |
||||||
|
|
||||||
|
ServerConfig server_config; |
||||||
|
server_config.set_server_type(server_type); |
||||||
|
server_config.set_threads(FLAGS_server_threads); |
||||||
|
server_config.set_enable_ssl(FLAGS_enable_ssl); |
||||||
|
|
||||||
|
auto result = RunScenario(client_config, FLAGS_num_clients, server_config, |
||||||
|
FLAGS_num_servers); |
||||||
|
|
||||||
|
gpr_log(GPR_INFO, "QPS: %.1f", |
||||||
|
result.latencies.Count() / |
||||||
|
average(result.client_resources, |
||||||
|
[](ResourceUsage u) { return u.wall_time; })); |
||||||
|
|
||||||
|
gpr_log(GPR_INFO, "Latencies (50/95/99/99.9%%-ile): %.1f/%.1f/%.1f/%.1f us", |
||||||
|
result.latencies.Percentile(50) / 1000, |
||||||
|
result.latencies.Percentile(95) / 1000, |
||||||
|
result.latencies.Percentile(99) / 1000, |
||||||
|
result.latencies.Percentile(99.9) / 1000); |
||||||
|
|
||||||
|
gpr_log(GPR_INFO, "Server system time: %.2f%%", |
||||||
|
100.0 * sum(result.server_resources, |
||||||
|
[](ResourceUsage u) { return u.system_time; }) / |
||||||
|
sum(result.server_resources, |
||||||
|
[](ResourceUsage u) { return u.wall_time; })); |
||||||
|
gpr_log(GPR_INFO, "Server user time: %.2f%%", |
||||||
|
100.0 * sum(result.server_resources, |
||||||
|
[](ResourceUsage u) { return u.user_time; }) / |
||||||
|
sum(result.server_resources, |
||||||
|
[](ResourceUsage u) { return u.wall_time; })); |
||||||
|
gpr_log(GPR_INFO, "Client system time: %.2f%%", |
||||||
|
100.0 * sum(result.client_resources, |
||||||
|
[](ResourceUsage u) { return u.system_time; }) / |
||||||
|
sum(result.client_resources, |
||||||
|
[](ResourceUsage u) { return u.wall_time; })); |
||||||
|
gpr_log(GPR_INFO, "Client user time: %.2f%%", |
||||||
|
100.0 * sum(result.client_resources, |
||||||
|
[](ResourceUsage u) { return u.user_time; }) / |
||||||
|
sum(result.client_resources, |
||||||
|
[](ResourceUsage u) { return u.wall_time; })); |
||||||
|
|
||||||
|
grpc_shutdown(); |
||||||
|
return 0; |
||||||
|
} |
@ -1,171 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, Google Inc. |
|
||||||
* All rights reserved. |
|
||||||
* |
|
||||||
* Redistribution and use in source and binary forms, with or without |
|
||||||
* modification, are permitted provided that the following conditions are |
|
||||||
* met: |
|
||||||
* |
|
||||||
* * Redistributions of source code must retain the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer. |
|
||||||
* * Redistributions in binary form must reproduce the above |
|
||||||
* copyright notice, this list of conditions and the following disclaimer |
|
||||||
* in the documentation and/or other materials provided with the |
|
||||||
* distribution. |
|
||||||
* * Neither the name of Google Inc. nor the names of its |
|
||||||
* contributors may be used to endorse or promote products derived from |
|
||||||
* this software without specific prior written permission. |
|
||||||
* |
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
||||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||||
* |
|
||||||
*/ |
|
||||||
|
|
||||||
#include <sys/time.h> |
|
||||||
#include <sys/resource.h> |
|
||||||
#include <sys/signal.h> |
|
||||||
#include <thread> |
|
||||||
|
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <gflags/gflags.h> |
|
||||||
#include <grpc/support/alloc.h> |
|
||||||
#include <grpc/support/host_port.h> |
|
||||||
#include <grpc++/config.h> |
|
||||||
#include <grpc++/server.h> |
|
||||||
#include <grpc++/server_builder.h> |
|
||||||
#include <grpc++/server_context.h> |
|
||||||
#include <grpc++/status.h> |
|
||||||
#include "src/cpp/server/thread_pool.h" |
|
||||||
#include "test/core/util/grpc_profiler.h" |
|
||||||
#include "test/cpp/qps/qpstest.pb.h" |
|
||||||
|
|
||||||
#include <grpc/grpc.h> |
|
||||||
#include <grpc/support/log.h> |
|
||||||
|
|
||||||
DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls."); |
|
||||||
DEFINE_int32(port, 0, "Server port."); |
|
||||||
DEFINE_int32(server_threads, 4, "Number of server threads."); |
|
||||||
|
|
||||||
using grpc::Server; |
|
||||||
using grpc::ServerBuilder; |
|
||||||
using grpc::ServerContext; |
|
||||||
using grpc::ThreadPool; |
|
||||||
using grpc::testing::Payload; |
|
||||||
using grpc::testing::PayloadType; |
|
||||||
using grpc::testing::ServerStats; |
|
||||||
using grpc::testing::SimpleRequest; |
|
||||||
using grpc::testing::SimpleResponse; |
|
||||||
using grpc::testing::StatsRequest; |
|
||||||
using grpc::testing::TestService; |
|
||||||
using grpc::Status; |
|
||||||
|
|
||||||
// In some distros, gflags is in the namespace google, and in some others,
|
|
||||||
// in gflags. This hack is enabling us to find both.
|
|
||||||
namespace google { } |
|
||||||
namespace gflags { } |
|
||||||
using namespace google; |
|
||||||
using namespace gflags; |
|
||||||
|
|
||||||
static bool got_sigint = false; |
|
||||||
|
|
||||||
static void sigint_handler(int x) { got_sigint = 1; } |
|
||||||
|
|
||||||
static double time_double(struct timeval* tv) { |
|
||||||
return tv->tv_sec + 1e-6 * tv->tv_usec; |
|
||||||
} |
|
||||||
|
|
||||||
static bool SetPayload(PayloadType type, int size, Payload* payload) { |
|
||||||
PayloadType response_type = type; |
|
||||||
// TODO(yangg): Support UNCOMPRESSABLE payload.
|
|
||||||
if (type != PayloadType::COMPRESSABLE) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
payload->set_type(response_type); |
|
||||||
std::unique_ptr<char[]> body(new char[size]()); |
|
||||||
payload->set_body(body.get(), size); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
namespace { |
|
||||||
|
|
||||||
class TestServiceImpl GRPC_FINAL : public TestService::Service { |
|
||||||
public: |
|
||||||
Status CollectServerStats(ServerContext* context, const StatsRequest*, |
|
||||||
ServerStats* response) { |
|
||||||
struct rusage usage; |
|
||||||
struct timeval tv; |
|
||||||
gettimeofday(&tv, NULL); |
|
||||||
getrusage(RUSAGE_SELF, &usage); |
|
||||||
response->set_time_now(time_double(&tv)); |
|
||||||
response->set_time_user(time_double(&usage.ru_utime)); |
|
||||||
response->set_time_system(time_double(&usage.ru_stime)); |
|
||||||
return Status::OK; |
|
||||||
} |
|
||||||
Status UnaryCall(ServerContext* context, const SimpleRequest* request, |
|
||||||
SimpleResponse* response) { |
|
||||||
if (request->has_response_size() && request->response_size() > 0) { |
|
||||||
if (!SetPayload(request->response_type(), request->response_size(), |
|
||||||
response->mutable_payload())) { |
|
||||||
return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); |
|
||||||
} |
|
||||||
} |
|
||||||
return Status::OK; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static void RunServer() { |
|
||||||
char* server_address = NULL; |
|
||||||
gpr_join_host_port(&server_address, "::", FLAGS_port); |
|
||||||
|
|
||||||
TestServiceImpl service; |
|
||||||
|
|
||||||
SimpleRequest request; |
|
||||||
SimpleResponse response; |
|
||||||
|
|
||||||
ServerBuilder builder; |
|
||||||
builder.AddPort(server_address); |
|
||||||
builder.RegisterService(&service); |
|
||||||
|
|
||||||
std::unique_ptr<ThreadPool> pool(new ThreadPool(FLAGS_server_threads)); |
|
||||||
builder.SetThreadPool(pool.get()); |
|
||||||
|
|
||||||
std::unique_ptr<Server> server(builder.BuildAndStart()); |
|
||||||
gpr_log(GPR_INFO, "Server listening on %s\n", server_address); |
|
||||||
|
|
||||||
grpc_profiler_start("qps_server.prof"); |
|
||||||
|
|
||||||
while (!got_sigint) { |
|
||||||
sleep(5); |
|
||||||
} |
|
||||||
|
|
||||||
grpc_profiler_stop(); |
|
||||||
|
|
||||||
gpr_free(server_address); |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char** argv) { |
|
||||||
grpc_init(); |
|
||||||
ParseCommandLineFlags(&argc, &argv, true); |
|
||||||
|
|
||||||
signal(SIGINT, sigint_handler); |
|
||||||
|
|
||||||
GPR_ASSERT(FLAGS_port != 0); |
|
||||||
GPR_ASSERT(!FLAGS_enable_ssl); |
|
||||||
RunServer(); |
|
||||||
|
|
||||||
grpc_shutdown(); |
|
||||||
return 0; |
|
||||||
} |
|
@ -0,0 +1,84 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_SERVER_H |
||||||
|
#define TEST_QPS_SERVER_H |
||||||
|
|
||||||
|
#include "test/cpp/qps/timer.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
class Server { |
||||||
|
public: |
||||||
|
Server() : timer_(new Timer) {} |
||||||
|
virtual ~Server() {} |
||||||
|
|
||||||
|
ServerStats Mark() { |
||||||
|
std::unique_ptr<Timer> timer(new Timer); |
||||||
|
timer.swap(timer_); |
||||||
|
|
||||||
|
auto timer_result = timer->Mark(); |
||||||
|
|
||||||
|
ServerStats stats; |
||||||
|
stats.set_time_elapsed(timer_result.wall); |
||||||
|
stats.set_time_system(timer_result.system); |
||||||
|
stats.set_time_user(timer_result.user); |
||||||
|
return stats; |
||||||
|
} |
||||||
|
|
||||||
|
static bool SetPayload(PayloadType type, int size, Payload* payload) { |
||||||
|
PayloadType response_type = type; |
||||||
|
// TODO(yangg): Support UNCOMPRESSABLE payload.
|
||||||
|
if (type != PayloadType::COMPRESSABLE) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
payload->set_type(response_type); |
||||||
|
std::unique_ptr<char[]> body(new char[size]()); |
||||||
|
payload->set_body(body.get(), size); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
std::unique_ptr<Timer> timer_; |
||||||
|
}; |
||||||
|
|
||||||
|
std::unique_ptr<Server> CreateSynchronousServer(const ServerConfig& config, |
||||||
|
int port); |
||||||
|
std::unique_ptr<Server> CreateAsyncServer(const ServerConfig& config, int port); |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,107 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sys/signal.h> |
||||||
|
#include <thread> |
||||||
|
|
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include <gflags/gflags.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/host_port.h> |
||||||
|
#include <grpc++/config.h> |
||||||
|
#include <grpc++/server.h> |
||||||
|
#include <grpc++/server_builder.h> |
||||||
|
#include <grpc++/server_context.h> |
||||||
|
#include <grpc++/status.h> |
||||||
|
#include <grpc++/stream.h> |
||||||
|
#include "src/cpp/server/thread_pool.h" |
||||||
|
#include "test/core/util/grpc_profiler.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
#include "test/cpp/qps/server.h" |
||||||
|
#include "test/cpp/qps/timer.h" |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
class TestServiceImpl GRPC_FINAL : public TestService::Service { |
||||||
|
public: |
||||||
|
Status UnaryCall(ServerContext* context, const SimpleRequest* request, |
||||||
|
SimpleResponse* response) GRPC_OVERRIDE { |
||||||
|
if (request->has_response_size() && request->response_size() > 0) { |
||||||
|
if (!Server::SetPayload(request->response_type(), |
||||||
|
request->response_size(), |
||||||
|
response->mutable_payload())) { |
||||||
|
return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); |
||||||
|
} |
||||||
|
} |
||||||
|
return Status::OK; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
class SynchronousServer GRPC_FINAL : public grpc::testing::Server { |
||||||
|
public: |
||||||
|
SynchronousServer(const ServerConfig& config, int port) |
||||||
|
: thread_pool_(config.threads()), impl_(MakeImpl(port)) {} |
||||||
|
|
||||||
|
private: |
||||||
|
std::unique_ptr<grpc::Server> MakeImpl(int port) { |
||||||
|
ServerBuilder builder; |
||||||
|
|
||||||
|
char* server_address = NULL; |
||||||
|
gpr_join_host_port(&server_address, "::", port); |
||||||
|
builder.AddPort(server_address); |
||||||
|
gpr_free(server_address); |
||||||
|
|
||||||
|
builder.RegisterService(&service_); |
||||||
|
|
||||||
|
builder.SetThreadPool(&thread_pool_); |
||||||
|
|
||||||
|
return builder.BuildAndStart(); |
||||||
|
} |
||||||
|
|
||||||
|
TestServiceImpl service_; |
||||||
|
ThreadPool thread_pool_; |
||||||
|
std::unique_ptr<grpc::Server> impl_; |
||||||
|
}; |
||||||
|
|
||||||
|
std::unique_ptr<grpc::testing::Server> CreateSynchronousServer( |
||||||
|
const ServerConfig& config, int port) { |
||||||
|
return std::unique_ptr<Server>(new SynchronousServer(config, port)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
@ -0,0 +1,28 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
# performs a single qps run with one client and one server |
||||||
|
|
||||||
|
set -ex |
||||||
|
|
||||||
|
cd $(dirname $0)/../../.. |
||||||
|
|
||||||
|
killall qps_worker || true |
||||||
|
|
||||||
|
config=opt |
||||||
|
|
||||||
|
NUMCPUS=`python2.7 -c 'import multiprocessing; print multiprocessing.cpu_count()'` |
||||||
|
|
||||||
|
make CONFIG=$config qps_worker qps_driver -j$NUMCPUS |
||||||
|
|
||||||
|
bins/$config/qps_worker -driver_port 10000 -server_port 10001 & |
||||||
|
PID1=$! |
||||||
|
bins/$config/qps_worker -driver_port 10010 -server_port 10011 & |
||||||
|
PID2=$! |
||||||
|
|
||||||
|
export QPS_WORKERS="localhost:10000,localhost:10010" |
||||||
|
|
||||||
|
bins/$config/qps_driver $* |
||||||
|
|
||||||
|
kill -2 $PID1 $PID2 |
||||||
|
wait |
||||||
|
|
@ -0,0 +1,60 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_STATS_UTILS_H |
||||||
|
#define TEST_QPS_STATS_UTILS_H |
||||||
|
|
||||||
|
#include "test/cpp/qps/histogram.h" |
||||||
|
#include <string> |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
template <class T, class F> |
||||||
|
double sum(const T& container, F functor) { |
||||||
|
double r = 0; |
||||||
|
for (auto v : container) { |
||||||
|
r += functor(v); |
||||||
|
} |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
template <class T, class F> |
||||||
|
double average(const T& container, F functor) { |
||||||
|
return sum(container, functor) / container.size(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,71 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "test/cpp/qps/timer.h" |
||||||
|
|
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/resource.h> |
||||||
|
#include <grpc/support/time.h> |
||||||
|
|
||||||
|
Timer::Timer() : start_(Sample()) {} |
||||||
|
|
||||||
|
double Timer::Now() { |
||||||
|
auto ts = gpr_now(); |
||||||
|
return ts.tv_sec + 1e-9 * ts.tv_nsec; |
||||||
|
} |
||||||
|
|
||||||
|
static double time_double(struct timeval* tv) { |
||||||
|
return tv->tv_sec + 1e-6 * tv->tv_usec; |
||||||
|
} |
||||||
|
|
||||||
|
Timer::Result Timer::Sample() { |
||||||
|
struct rusage usage; |
||||||
|
struct timeval tv; |
||||||
|
gettimeofday(&tv, nullptr); |
||||||
|
getrusage(RUSAGE_SELF, &usage); |
||||||
|
|
||||||
|
Result r; |
||||||
|
r.wall = time_double(&tv); |
||||||
|
r.user = time_double(&usage.ru_utime); |
||||||
|
r.system = time_double(&usage.ru_stime); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
Timer::Result Timer::Mark() { |
||||||
|
Result s = Sample(); |
||||||
|
Result r; |
||||||
|
r.wall = s.wall - start_.wall; |
||||||
|
r.user = s.user - start_.user; |
||||||
|
r.system = s.system - start_.system; |
||||||
|
return r; |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef TEST_QPS_TIMER_H |
||||||
|
#define TEST_QPS_TIMER_H |
||||||
|
|
||||||
|
class Timer { |
||||||
|
public: |
||||||
|
Timer(); |
||||||
|
|
||||||
|
struct Result { |
||||||
|
double wall; |
||||||
|
double user; |
||||||
|
double system; |
||||||
|
}; |
||||||
|
|
||||||
|
Result Mark(); |
||||||
|
|
||||||
|
static double Now(); |
||||||
|
|
||||||
|
private: |
||||||
|
static Result Sample(); |
||||||
|
|
||||||
|
const Result start_; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // TEST_QPS_TIMER_H
|
@ -0,0 +1,235 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <memory> |
||||||
|
#include <mutex> |
||||||
|
#include <string> |
||||||
|
#include <thread> |
||||||
|
#include <vector> |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include <sys/signal.h> |
||||||
|
|
||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/histogram.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/host_port.h> |
||||||
|
#include <gflags/gflags.h> |
||||||
|
#include <grpc++/client_context.h> |
||||||
|
#include <grpc++/status.h> |
||||||
|
#include <grpc++/server.h> |
||||||
|
#include <grpc++/server_builder.h> |
||||||
|
#include <grpc++/stream.h> |
||||||
|
#include "test/core/util/grpc_profiler.h" |
||||||
|
#include "test/cpp/util/create_test_channel.h" |
||||||
|
#include "test/cpp/qps/qpstest.pb.h" |
||||||
|
#include "test/cpp/qps/client.h" |
||||||
|
#include "test/cpp/qps/server.h" |
||||||
|
|
||||||
|
DEFINE_int32(driver_port, 0, "Driver server port."); |
||||||
|
DEFINE_int32(server_port, 0, "Spawned server port."); |
||||||
|
|
||||||
|
// In some distros, gflags is in the namespace google, and in some others,
|
||||||
|
// in gflags. This hack is enabling us to find both.
|
||||||
|
namespace google {} |
||||||
|
namespace gflags {} |
||||||
|
using namespace google; |
||||||
|
using namespace gflags; |
||||||
|
|
||||||
|
static bool got_sigint = false; |
||||||
|
|
||||||
|
namespace grpc { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
std::unique_ptr<Client> CreateClient(const ClientConfig& config) { |
||||||
|
switch (config.client_type()) { |
||||||
|
case ClientType::SYNCHRONOUS_CLIENT: |
||||||
|
return CreateSynchronousClient(config); |
||||||
|
case ClientType::ASYNC_CLIENT: |
||||||
|
return CreateAsyncClient(config); |
||||||
|
} |
||||||
|
abort(); |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<Server> CreateServer(const ServerConfig& config) { |
||||||
|
switch (config.server_type()) { |
||||||
|
case ServerType::SYNCHRONOUS_SERVER: |
||||||
|
return CreateSynchronousServer(config, FLAGS_server_port); |
||||||
|
case ServerType::ASYNC_SERVER: |
||||||
|
return CreateAsyncServer(config, FLAGS_server_port); |
||||||
|
} |
||||||
|
abort(); |
||||||
|
} |
||||||
|
|
||||||
|
class WorkerImpl GRPC_FINAL : public Worker::Service { |
||||||
|
public: |
||||||
|
WorkerImpl() : acquired_(false) {} |
||||||
|
|
||||||
|
Status RunTest(ServerContext* ctx, |
||||||
|
ServerReaderWriter<ClientStatus, ClientArgs>* stream) |
||||||
|
GRPC_OVERRIDE { |
||||||
|
InstanceGuard g(this); |
||||||
|
if (!g.Acquired()) { |
||||||
|
return Status(RESOURCE_EXHAUSTED); |
||||||
|
} |
||||||
|
|
||||||
|
ClientArgs args; |
||||||
|
if (!stream->Read(&args)) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
if (!args.has_setup()) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
auto client = CreateClient(args.setup()); |
||||||
|
if (!client) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
ClientStatus status; |
||||||
|
if (!stream->Write(status)) { |
||||||
|
return Status(UNKNOWN); |
||||||
|
} |
||||||
|
while (stream->Read(&args)) { |
||||||
|
if (!args.has_mark()) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
*status.mutable_stats() = client->Mark(); |
||||||
|
stream->Write(status); |
||||||
|
} |
||||||
|
|
||||||
|
return Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
Status RunServer(ServerContext* ctx, |
||||||
|
ServerReaderWriter<ServerStatus, ServerArgs>* stream) |
||||||
|
GRPC_OVERRIDE { |
||||||
|
InstanceGuard g(this); |
||||||
|
if (!g.Acquired()) { |
||||||
|
return Status(RESOURCE_EXHAUSTED); |
||||||
|
} |
||||||
|
|
||||||
|
ServerArgs args; |
||||||
|
if (!stream->Read(&args)) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
if (!args.has_setup()) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
auto server = CreateServer(args.setup()); |
||||||
|
if (!server) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
ServerStatus status; |
||||||
|
status.set_port(FLAGS_server_port); |
||||||
|
if (!stream->Write(status)) { |
||||||
|
return Status(UNKNOWN); |
||||||
|
} |
||||||
|
while (stream->Read(&args)) { |
||||||
|
if (!args.has_mark()) { |
||||||
|
return Status(INVALID_ARGUMENT); |
||||||
|
} |
||||||
|
*status.mutable_stats() = server->Mark(); |
||||||
|
stream->Write(status); |
||||||
|
} |
||||||
|
|
||||||
|
return Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// Protect against multiple clients using this worker at once.
|
||||||
|
class InstanceGuard { |
||||||
|
public: |
||||||
|
InstanceGuard(WorkerImpl* impl) |
||||||
|
: impl_(impl), acquired_(impl->TryAcquireInstance()) {} |
||||||
|
~InstanceGuard() { |
||||||
|
if (acquired_) { |
||||||
|
impl_->ReleaseInstance(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Acquired() const { return acquired_; } |
||||||
|
|
||||||
|
private: |
||||||
|
WorkerImpl* const impl_; |
||||||
|
const bool acquired_; |
||||||
|
}; |
||||||
|
|
||||||
|
bool TryAcquireInstance() { |
||||||
|
std::lock_guard<std::mutex> g(mu_); |
||||||
|
if (acquired_) return false; |
||||||
|
acquired_ = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void ReleaseInstance() { |
||||||
|
std::lock_guard<std::mutex> g(mu_); |
||||||
|
GPR_ASSERT(acquired_); |
||||||
|
acquired_ = false; |
||||||
|
} |
||||||
|
|
||||||
|
std::mutex mu_; |
||||||
|
bool acquired_; |
||||||
|
}; |
||||||
|
|
||||||
|
static void RunServer() { |
||||||
|
char* server_address = NULL; |
||||||
|
gpr_join_host_port(&server_address, "::", FLAGS_driver_port); |
||||||
|
|
||||||
|
WorkerImpl service; |
||||||
|
|
||||||
|
ServerBuilder builder; |
||||||
|
builder.AddPort(server_address); |
||||||
|
builder.RegisterService(&service); |
||||||
|
|
||||||
|
gpr_free(server_address); |
||||||
|
|
||||||
|
auto server = builder.BuildAndStart(); |
||||||
|
|
||||||
|
while (!got_sigint) { |
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(5)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace grpc
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
grpc_init(); |
||||||
|
ParseCommandLineFlags(&argc, &argv, true); |
||||||
|
|
||||||
|
grpc::testing::RunServer(); |
||||||
|
|
||||||
|
grpc_shutdown(); |
||||||
|
return 0; |
||||||
|
} |
Loading…
Reference in new issue