batched changes
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index b4c18bc..bb90dd6 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -124,6 +124,8 @@
static double ServerWallTime(ServerStats s) { return s.time_elapsed(); }
static double ServerSystemTime(ServerStats s) { return s.time_system(); }
static double ServerUserTime(ServerStats s) { return s.time_user(); }
+static double ServerTotalCpuTime(ServerStats s) { return s.total_cpu_time(); }
+static double ServerIdleCpuTime(ServerStats s) { return s.idle_cpu_time(); }
static int Cores(int n) { return n; }
// Postprocess ScenarioResult and populate result summary.
@@ -147,6 +149,10 @@
sum(result->server_stats(), ServerWallTime);
auto server_user_time = 100.0 * sum(result->server_stats(), ServerUserTime) /
sum(result->server_stats(), ServerWallTime);
+ auto server_cpu_usage = 100 - 100 * average(result->server_stats(), ServerIdleCpuTime) /
+ average(result->server_stats(), ServerTotalCpuTime);
+ gpr_log(GPR_INFO, "total cpu: %.1f, idle cpu: %.1f", average(result->server_stats(), ServerTotalCpuTime),
+ average(result->server_stats(), ServerIdleCpuTime));
auto client_system_time = 100.0 * sum(result->client_stats(), SystemTime) /
sum(result->client_stats(), WallTime);
auto client_user_time = 100.0 * sum(result->client_stats(), UserTime) /
@@ -156,6 +162,7 @@
result->mutable_summary()->set_server_user_time(server_user_time);
result->mutable_summary()->set_client_system_time(client_system_time);
result->mutable_summary()->set_client_user_time(client_user_time);
+ result->mutable_summary()->set_server_cpu_usage(server_cpu_usage);
}
// Namespace for classes and functions used only in RunScenario
diff --git a/test/cpp/qps/latency_vs_load.cc b/test/cpp/qps/latency_vs_load.cc
new file mode 100644
index 0000000..acb1088
--- /dev/null
+++ b/test/cpp/qps/latency_vs_load.cc
@@ -0,0 +1,189 @@
+/*
+ *
+ * Copyright 2015-2016, 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 <memory>
+#include <set>
+
+#include <grpc++/impl/codegen/config_protobuf.h>
+
+#include <gflags/gflags.h>
+#include <grpc/support/log.h>
+
+#include "test/cpp/qps/driver.h"
+#include "test/cpp/qps/parse_json.h"
+#include "test/cpp/qps/report.h"
+#include "test/cpp/util/benchmark_config.h"
+
+DEFINE_string(scenarios_file, "",
+ "JSON file containing an array of Scenario objects");
+DEFINE_string(scenarios_json, "",
+ "JSON string containing an array of Scenario objects");
+DEFINE_bool(quit, false, "Quit the workers");
+
+DEFINE_double(initial_offered_load, 1000.0, "Set up for intial offered load");
+
+DEFINE_double(targeted_cpu_load, 99.0, "targeted cpu load");
+
+namespace grpc {
+namespace testing {
+
+static double GetCpuLoad(Scenario * scenario, double offered_load) {
+ scenario->mutable_client_config()->mutable_load_params()->mutable_poisson()->
+ set_offered_load(offered_load);
+ auto result =
+ RunScenario(scenario->client_config(), scenario->num_clients(),
+ scenario->server_config(), scenario->num_servers(),
+ scenario->warmup_seconds(), scenario->benchmark_seconds(),
+ scenario->spawn_local_worker_count());
+
+ GetReporter()->ReportQPS(*result);
+ GetReporter()->ReportQPSPerCore(*result);
+ GetReporter()->ReportLatency(*result);
+ GetReporter()->ReportTimes(*result);
+ GetReporter()->ReportCpuUsage(*result);
+
+ bool success = true;
+ for (int i = 0; success && i < result->client_success_size(); i++) {
+ success = result->client_success(i);
+ }
+
+ for (int i = 0; success && i < result->server_success_size(); i++) {
+ success = result->server_success(i);
+ }
+
+ return success ? result->summary().server_cpu_usage() : -1;
+}
+
+static double BinarySearch(Scenario * scenario, double targeted_cpu_load,
+ double low_offered_load, double high_offered_load) {
+ int low = int(low_offered_load);
+ int high = int(high_offered_load);
+ while (low <= high - 500) {
+ int mid = low + (high - low) /2;
+ double current_cpu_load = GetCpuLoad(scenario, double(mid));
+ gpr_log(GPR_INFO, "binary search: current_offered_load %d", mid);
+ if (targeted_cpu_load < current_cpu_load) {
+ high = mid -1;
+ }
+ else if (targeted_cpu_load > current_cpu_load) {
+ low = mid + 1;
+ }
+ else {
+ high = mid - 1;
+ }
+ }
+
+ return double(low);
+}
+
+static double SearchOfferedLoad(double initial_offered_load, double targeted_cpu_load,
+ Scenario * scenario) {
+ std::cerr << "RUNNING SCENARIO: " << scenario->name() << "\n";
+ double current_offered_load = initial_offered_load;
+ double current_cpu_load = GetCpuLoad(scenario, current_offered_load);
+ if (current_cpu_load > targeted_cpu_load) {
+ gpr_log(GPR_ERROR, "Initial offered load too high");
+ return -1;
+ }
+
+ do {
+ current_offered_load *= 2;
+ current_cpu_load = GetCpuLoad(scenario, current_offered_load);
+ gpr_log(GPR_INFO, "do while: current_offered_load %f", current_offered_load);
+ } while (current_cpu_load < targeted_cpu_load);
+
+ double targeted_offered_load = BinarySearch(scenario, targeted_cpu_load,
+ current_offered_load / 2,
+ current_offered_load);
+ gpr_log(GPR_INFO, "targeted_offered_load %f", targeted_offered_load);
+
+ return targeted_offered_load;
+}
+
+static bool CpuLoadDriver() {
+ grpc::string json;
+
+ bool scfile = (FLAGS_scenarios_file != "");
+ bool scjson = (FLAGS_scenarios_json != "");
+ if ((!scfile && !scjson && !FLAGS_quit) ||
+ (scfile && (scjson || FLAGS_quit)) || (scjson && FLAGS_quit)) {
+ gpr_log(GPR_ERROR,
+ "Exactly one of --scenarios_file, --scenarios_json, "
+ "or --quit must be set");
+ abort();
+ }
+
+ if (scfile) {
+ // Read the json data from disk
+ FILE *json_file = fopen(FLAGS_scenarios_file.c_str(), "r");
+ GPR_ASSERT(json_file != NULL);
+ fseek(json_file, 0, SEEK_END);
+ long len = ftell(json_file);
+ char *data = new char[len];
+ fseek(json_file, 0, SEEK_SET);
+ GPR_ASSERT(len == (long)fread(data, 1, len, json_file));
+ fclose(json_file);
+ json = grpc::string(data, data + len);
+ delete[] data;
+ } else if (scjson) {
+ json = FLAGS_scenarios_json.c_str();
+ } else if (FLAGS_quit) {
+ return RunQuit();
+ }
+
+ // Parse into an array of scenarios
+ Scenarios scenarios;
+ ParseJson(json.c_str(), "grpc.testing.Scenarios", &scenarios);
+
+ // Make sure that there is at least some valid scenario here
+ GPR_ASSERT(scenarios.scenarios_size() > 0);
+ bool success = true;
+
+ for (int i = 0; i < scenarios.scenarios_size(); i++) {
+ Scenario *scenario = scenarios.mutable_scenarios(i);
+ SearchOfferedLoad(FLAGS_initial_offered_load, FLAGS_targeted_cpu_load, scenario);
+ // GetCpuLoad(scenario, FLAGS_initial_offered_load);
+ }
+
+ return success;
+}
+
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char **argv) {
+ grpc::testing::InitBenchmark(&argc, &argv, true);
+
+ bool ok = grpc::testing::CpuLoadDriver();
+
+ return ok ? 0 : 1;
+}
diff --git a/test/cpp/qps/qps_json_driver.cc b/test/cpp/qps/qps_json_driver.cc
index 1524ebb..ec77e1e 100644
--- a/test/cpp/qps/qps_json_driver.cc
+++ b/test/cpp/qps/qps_json_driver.cc
@@ -50,6 +50,10 @@
"JSON string containing an array of Scenario objects");
DEFINE_bool(quit, false, "Quit the workers");
+DEFINE_double(initial_offered_load, 1000.0, "Set up for intial offered load");
+
+DEFINE_double(targeted_cpu_load, 99.0, "targeted cpu load");
+
namespace grpc {
namespace testing {
@@ -109,6 +113,7 @@
GetReporter()->ReportQPSPerCore(*result);
GetReporter()->ReportLatency(*result);
GetReporter()->ReportTimes(*result);
+ GetReporter()->ReportCpuUsage(*result);
for (int i = 0; success && i < result->client_success_size(); i++) {
success = result->client_success(i);
diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc
index 2ec7d86..69e4794 100644
--- a/test/cpp/qps/report.cc
+++ b/test/cpp/qps/report.cc
@@ -71,6 +71,11 @@
}
}
+void CompositeReporter::ReportCpuUsage(const ScenarioResult& result) {
+ for (size_t i = 0; i < reporters_.size(); ++i) {
+ reporters_[i]->ReportCpuUsage(result);
+ }
+}
void GprLogReporter::ReportQPS(const ScenarioResult& result) {
gpr_log(GPR_INFO, "QPS: %.1f", result.summary().qps());
}
@@ -101,6 +106,11 @@
result.summary().client_user_time());
}
+void GprLogReporter::ReportCpuUsage(const ScenarioResult& result) {
+ gpr_log(GPR_INFO, "Server CPU usage: %.2f%%",
+ result.summary().server_cpu_usage());
+}
+
void JsonReporter::ReportQPS(const ScenarioResult& result) {
grpc::string json_string =
SerializeJson(result, "type.googleapis.com/grpc.testing.ScenarioResult");
@@ -121,5 +131,9 @@
// NOP - all reporting is handled by ReportQPS.
}
+void JsonReporter::ReportCpuUsage(const ScenarioResult& result) {
+ // NOP - all reporting is handled by ReportQPS.
+}
+
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h
index 39cf498..7327d31 100644
--- a/test/cpp/qps/report.h
+++ b/test/cpp/qps/report.h
@@ -70,6 +70,9 @@
/** Reports system and user time for client and server systems. */
virtual void ReportTimes(const ScenarioResult& result) = 0;
+ /** Reports server cpu usage. */
+ virtual void ReportCpuUsage(const ScenarioResult& result) = 0;
+
private:
const string name_;
};
@@ -86,6 +89,7 @@
void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+ void ReportCpuUsage(const ScenarioResult& result) GRPC_OVERRIDE;
private:
std::vector<std::unique_ptr<Reporter> > reporters_;
@@ -101,6 +105,8 @@
void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+ void ReportCpuUsage(const ScenarioResult& result) GRPC_OVERRIDE;
+
};
/** Dumps the report to a JSON file. */
@@ -114,7 +120,8 @@
void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
-
+ void ReportCpuUsage(const ScenarioResult& result) GRPC_OVERRIDE;
+
const string report_file_;
};
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index e8bc396..c3d18e5 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -75,6 +75,8 @@
stats.set_time_elapsed(timer_result.wall);
stats.set_time_system(timer_result.system);
stats.set_time_user(timer_result.user);
+ stats.set_total_cpu_time(timer_result.total_cpu_time);
+ stats.set_idle_cpu_time(timer_result.idle_cpu_time);
return stats;
}
diff --git a/test/cpp/qps/usage_timer.cc b/test/cpp/qps/usage_timer.cc
index ff595b2..589b78f 100644
--- a/test/cpp/qps/usage_timer.cc
+++ b/test/cpp/qps/usage_timer.cc
@@ -33,10 +33,13 @@
#include "test/cpp/qps/usage_timer.h"
+#include <fstream>
+#include <string>
+#include <sstream>
+
#include <grpc/support/time.h>
#include <sys/resource.h>
#include <sys/time.h>
-
UsageTimer::UsageTimer() : start_(Sample()) {}
double UsageTimer::Now() {
@@ -48,6 +51,23 @@
return tv->tv_sec + 1e-6 * tv->tv_usec;
}
+static void get_cpu_usage(unsigned long long* total_cpu_time,
+ unsigned long long* idle_cpu_time) {
+ std::ifstream proc_stat("/proc/stat");
+ proc_stat.ignore(5);
+ std::string cpu_time_str;
+ std::string first_line;
+ std::getline(proc_stat, first_line);
+ std::stringstream first_line_s(first_line);
+ for(int i = 0; i < 10; ++i) {
+ std::getline(first_line_s, cpu_time_str, ' ');
+ *total_cpu_time += std::stoi(cpu_time_str);
+ if (i == 3) {
+ *idle_cpu_time = std::stoi(cpu_time_str);
+ }
+ }
+}
+
UsageTimer::Result UsageTimer::Sample() {
struct rusage usage;
struct timeval tv;
@@ -58,6 +78,9 @@
r.wall = time_double(&tv);
r.user = time_double(&usage.ru_utime);
r.system = time_double(&usage.ru_stime);
+ r.total_cpu_time = 0;
+ r.idle_cpu_time = 0;
+ get_cpu_usage(&r.total_cpu_time, &r.idle_cpu_time);
return r;
}
@@ -67,5 +90,8 @@
r.wall = s.wall - start_.wall;
r.user = s.user - start_.user;
r.system = s.system - start_.system;
+ r.total_cpu_time = s.total_cpu_time - start_.total_cpu_time;
+ r.idle_cpu_time = s.idle_cpu_time - start_.idle_cpu_time;
+
return r;
}
diff --git a/test/cpp/qps/usage_timer.h b/test/cpp/qps/usage_timer.h
index 8343cd6..0fc1b47 100644
--- a/test/cpp/qps/usage_timer.h
+++ b/test/cpp/qps/usage_timer.h
@@ -42,6 +42,8 @@
double wall;
double user;
double system;
+ unsigned long long total_cpu_time;
+ unsigned long long idle_cpu_time;
};
Result Mark() const;
diff --git a/test/cpp/util/benchmark_config.cc b/test/cpp/util/benchmark_config.cc
index 6fc8640..ed06f11 100644
--- a/test/cpp/util/benchmark_config.cc
+++ b/test/cpp/util/benchmark_config.cc
@@ -51,6 +51,8 @@
DEFINE_string(tag, "", "Optional tag for the test");
+
+
// 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 {}