Split up into a new service proto, use proper service suffix, add a
reset option to the mark, create a closed loop config params (empty
message) for consistency with other tests.
diff --git a/Makefile b/Makefile
index 1f2c93a..0dd0f26 100644
--- a/Makefile
+++ b/Makefile
@@ -3601,6 +3601,21 @@
 endif
 
 ifeq ($(NO_PROTOC),true)
+$(GENDIR)/test/proto/perf_tests/perf_services.pb.cc: protoc_dep_error
+$(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc: protoc_dep_error
+else
+$(GENDIR)/test/proto/perf_tests/perf_services.pb.cc: test/proto/perf_tests/perf_services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[PROTOC]  Generating protobuf CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --cpp_out=$(GENDIR) $<
+
+$(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc: test/proto/perf_tests/perf_services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS)
+	$(E) "[GRPC]    Generating gRPC's protobuf service CC file from $<"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(PROTOC) --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $<
+endif
+
+ifeq ($(NO_PROTOC),true)
 $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc: protoc_dep_error
 $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc: protoc_dep_error
 else
@@ -5206,6 +5221,7 @@
 LIBQPS_SRC = \
     $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc \
     $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc \
+    $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc \
     $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc \
     $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc \
     test/cpp/qps/client_async.cc \
@@ -5261,16 +5277,16 @@
 -include $(LIBQPS_OBJS:.o=.dep)
 endif
 endif
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
-$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/client_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/driver.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/perf_db_client.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/qps_worker.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/report.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_async.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/server_sync.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/qps/timer.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
+$(OBJDIR)/$(CONFIG)/test/cpp/util/benchmark_config.o: $(GENDIR)/test/proto/messages.pb.cc $(GENDIR)/test/proto/messages.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.pb.cc $(GENDIR)/test/proto/perf_tests/perf_control.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.pb.cc $(GENDIR)/test/proto/perf_tests/perf_services.grpc.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.pb.cc $(GENDIR)/test/proto/perf_tests/perf_stats.grpc.pb.cc $(GENDIR)/test/cpp/qps/perf_db.pb.cc $(GENDIR)/test/cpp/qps/perf_db.grpc.pb.cc
 
 
 LIBGRPC_CSHARP_EXT_SRC = \
diff --git a/build.yaml b/build.yaml
index b8c53f2..a90cd12 100644
--- a/build.yaml
+++ b/build.yaml
@@ -751,6 +751,7 @@
   src:
   - test/proto/messages.proto
   - test/proto/perf_tests/perf_control.proto
+  - test/proto/perf_tests/perf_services.proto
   - test/proto/perf_tests/perf_stats.proto
   - test/cpp/qps/perf_db.proto
   - test/cpp/qps/client_async.cc
diff --git a/include/grpc/support/histogram.h b/include/grpc/support/histogram.h
index 2fd1084..fd56dac 100644
--- a/include/grpc/support/histogram.h
+++ b/include/grpc/support/histogram.h
@@ -50,7 +50,7 @@
 /* The following merges the second histogram into the first. It only works
    if they have the same buckets and resolution. Returns 0 on failure, 1
    on success */
-int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src);
+int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src);
 
 double gpr_histogram_percentile(gpr_histogram *histogram, double percentile);
 double gpr_histogram_mean(gpr_histogram *histogram);
diff --git a/src/core/support/histogram.c b/src/core/support/histogram.c
index 8a1a9d9..77b48af 100644
--- a/src/core/support/histogram.c
+++ b/src/core/support/histogram.c
@@ -125,7 +125,7 @@
   h->buckets[bucket_for(h, x)]++;
 }
 
-int gpr_histogram_merge(gpr_histogram *dst, gpr_histogram *src) {
+int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src) {
   if ((dst->num_buckets != src->num_buckets) ||
       (dst->multiplier != src->multiplier)) {
     /* Fail because these histograms don't match */
diff --git a/test/cpp/qps/async_streaming_ping_pong_test.cc b/test/cpp/qps/async_streaming_ping_pong_test.cc
index 2d3ebfd..e3a614e 100644
--- a/test/cpp/qps/async_streaming_ping_pong_test.cc
+++ b/test/cpp/qps/async_streaming_ping_pong_test.cc
@@ -58,6 +58,7 @@
   client_config.set_payload_size(1);
   client_config.set_async_client_threads(1);
   client_config.set_rpc_type(STREAMING);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/async_unary_ping_pong_test.cc b/test/cpp/qps/async_unary_ping_pong_test.cc
index a5f91c5..caed835 100644
--- a/test/cpp/qps/async_unary_ping_pong_test.cc
+++ b/test/cpp/qps/async_unary_ping_pong_test.cc
@@ -58,6 +58,7 @@
   client_config.set_payload_size(1);
   client_config.set_async_client_threads(1);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 7086ae8..110249b 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -40,8 +40,8 @@
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
-#include "test/proto/perf_tests/perf_control.grpc.pb.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 namespace grpc {
 
@@ -80,22 +80,31 @@
   }
   virtual ~Client() {}
 
-  ClientStats Mark() {
+  ClientStats Mark(bool reset) {
     Histogram latencies;
-    // avoid std::vector for old compilers that expect a copy constructor
-    Histogram* to_merge = new Histogram[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]);
-    }
-    delete[] to_merge;
+    Timer::Result timer_result;
 
-    auto timer_result = timer->Mark();
+    // avoid std::vector for old compilers that expect a copy constructor
+    if (reset) {
+      Histogram* to_merge = new Histogram[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]);
+      }
+      delete[] to_merge;
+      timer_result = timer->Mark();
+    } else {
+      // merge snapshots of each thread histogram
+      for (size_t i = 0; i < threads_.size(); i++) {
+	threads_[i]->MergeStatsInto(&latencies);
+      }
+      timer_result = timer_->Mark();
+    }
 
     ClientStats stats;
     latencies.FillProto(stats.mutable_latencies());
@@ -123,14 +132,14 @@
       // constructor followed by an initializer function to make
       // old compilers happy with using this in std::vector
       channel_ = CreateTestChannel(target, config.use_tls());
-      stub_ = TestService::NewStub(channel_);
+      stub_ = BenchmarkService::NewStub(channel_);
     }
     Channel* get_channel() { return channel_.get(); }
-    TestService::Stub* get_stub() { return stub_.get(); }
+    BenchmarkService::Stub* get_stub() { return stub_.get(); }
 
    private:
     std::shared_ptr<Channel> channel_;
-    std::unique_ptr<TestService::Stub> stub_;
+    std::unique_ptr<BenchmarkService::Stub> stub_;
   };
   std::vector<ClientChannelInfo> channels_;
 
@@ -162,7 +171,10 @@
     } else if (load.has_pareto()) {
       random_dist.reset(new ParetoDist(load.pareto().interarrival_base() * num_threads,
 				       load.pareto().alpha()));
-    } else { // No load parameters, so must be closed-loop
+    } else if (load.has_closed()) {
+      // Closed-loop doesn't use random dist at all
+    } else { // invalid load type
+      GPR_ASSERT(false);
     }
 
     // Set closed_loop_ based on whether or not random_dist is set
@@ -198,7 +210,7 @@
    public:
     Thread(Client* client, size_t idx)
         : done_(false),
-          new_(nullptr),
+          new_stats_(nullptr),
           client_(client),
           idx_(idx),
           impl_(&Thread::ThreadFunc, this) {}
@@ -213,16 +225,21 @@
 
     void BeginSwap(Histogram* n) {
       std::lock_guard<std::mutex> g(mu_);
-      new_ = n;
+      new_stats_ = n;
     }
 
     void EndSwap() {
       std::unique_lock<std::mutex> g(mu_);
-      while (new_ != nullptr) {
+      while (new_stats_ != nullptr) {
         cv_.wait(g);
       };
     }
 
+    void MergeStatsInto(Histogram* hist) {
+      std::unique_lock<std::mutex> g(mu_);
+      hist->Merge(histogram_);
+    }
+
    private:
     Thread(const Thread&);
     Thread& operator=(const Thread&);
@@ -240,21 +257,21 @@
         if (done_) {
           return;
         }
-        // check if we're marking, swap out the histogram if so
-        if (new_) {
-          new_->Swap(&histogram_);
-          new_ = nullptr;
+        // check if we're resetting stats, swap out the histogram if so
+        if (new_stats_) {
+          new_stats_->Swap(&histogram_);
+          new_stats_ = nullptr;
           cv_.notify_one();
         }
       }
     }
 
-    TestService::Stub* stub_;
+    BenchmarkService::Stub* stub_;
     ClientConfig config_;
     std::mutex mu_;
     std::condition_variable cv_;
     bool done_;
-    Histogram* new_;
+    Histogram* new_stats_;
     Histogram histogram_;
     Client* client_;
     size_t idx_;
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index cef17fa..41db615 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -48,10 +48,10 @@
 #include <gflags/gflags.h>
 #include <grpc++/client_context.h>
 
-#include "test/proto/perf_tests/perf_control.grpc.pb.h"
 #include "test/cpp/qps/timer.h"
 #include "test/cpp/qps/client.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
@@ -88,10 +88,10 @@
 class ClientRpcContextUnaryImpl : public ClientRpcContext {
  public:
   ClientRpcContextUnaryImpl(
-      int channel_id, TestService::Stub* stub, const RequestType& req,
+      int channel_id, BenchmarkService::Stub* stub, const RequestType& req,
       std::function<
           std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
-              TestService::Stub*, grpc::ClientContext*, const RequestType&,
+              BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
               CompletionQueue*)> start_req,
       std::function<void(grpc::Status, ResponseType*)> on_done)
       : ClientRpcContext(channel_id),
@@ -131,13 +131,13 @@
     return true;  // we're done, this'll be ignored
   }
   grpc::ClientContext context_;
-  TestService::Stub* stub_;
+  BenchmarkService::Stub* stub_;
   RequestType req_;
   ResponseType response_;
   bool (ClientRpcContextUnaryImpl::*next_state_)(bool);
   std::function<void(grpc::Status, ResponseType*)> callback_;
   std::function<std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
-      TestService::Stub*, grpc::ClientContext*, const RequestType&,
+      BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
       CompletionQueue*)> start_req_;
   grpc::Status status_;
   double start_;
@@ -151,7 +151,7 @@
  public:
   explicit AsyncClient(
       const ClientConfig& config,
-      std::function<ClientRpcContext*(int, TestService::Stub*,
+      std::function<ClientRpcContext*(int, BenchmarkService::Stub*,
                                       const SimpleRequest&)> setup_ctx)
       : Client(config),
         channel_lock_(new std::mutex[config.client_channels()]),
@@ -354,11 +354,11 @@
  private:
   static void CheckDone(grpc::Status s, SimpleResponse* response) {}
   static std::unique_ptr<grpc::ClientAsyncResponseReader<SimpleResponse>>
-  StartReq(TestService::Stub* stub, grpc::ClientContext* ctx,
+  StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
            const SimpleRequest& request, CompletionQueue* cq) {
     return stub->AsyncUnaryCall(ctx, request, cq);
   };
-  static ClientRpcContext* SetupCtx(int channel_id, TestService::Stub* stub,
+  static ClientRpcContext* SetupCtx(int channel_id, BenchmarkService::Stub* stub,
                                     const SimpleRequest& req) {
     return new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
         channel_id, stub, req, AsyncUnaryClient::StartReq,
@@ -370,9 +370,9 @@
 class ClientRpcContextStreamingImpl : public ClientRpcContext {
  public:
   ClientRpcContextStreamingImpl(
-      int channel_id, TestService::Stub* stub, const RequestType& req,
+      int channel_id, BenchmarkService::Stub* stub, const RequestType& req,
       std::function<std::unique_ptr<grpc::ClientAsyncReaderWriter<
-          RequestType, ResponseType>>(TestService::Stub*, grpc::ClientContext*,
+          RequestType, ResponseType>>(BenchmarkService::Stub*, grpc::ClientContext*,
                                       CompletionQueue*, void*)> start_req,
       std::function<void(grpc::Status, ResponseType*)> on_done)
       : ClientRpcContext(channel_id),
@@ -420,14 +420,14 @@
     return StartWrite(ok);
   }
   grpc::ClientContext context_;
-  TestService::Stub* stub_;
+  BenchmarkService::Stub* stub_;
   RequestType req_;
   ResponseType response_;
   bool (ClientRpcContextStreamingImpl::*next_state_)(bool, Histogram*);
   std::function<void(grpc::Status, ResponseType*)> callback_;
   std::function<
       std::unique_ptr<grpc::ClientAsyncReaderWriter<RequestType, ResponseType>>(
-          TestService::Stub*, grpc::ClientContext*, CompletionQueue*, void*)>
+          BenchmarkService::Stub*, grpc::ClientContext*, CompletionQueue*, void*)>
       start_req_;
   grpc::Status status_;
   double start_;
@@ -451,12 +451,12 @@
   static void CheckDone(grpc::Status s, SimpleResponse* response) {}
   static std::unique_ptr<
       grpc::ClientAsyncReaderWriter<SimpleRequest, SimpleResponse>>
-  StartReq(TestService::Stub* stub, grpc::ClientContext* ctx,
+  StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
            CompletionQueue* cq, void* tag) {
     auto stream = stub->AsyncStreamingCall(ctx, cq, tag);
     return stream;
   };
-  static ClientRpcContext* SetupCtx(int channel_id, TestService::Stub* stub,
+  static ClientRpcContext* SetupCtx(int channel_id, BenchmarkService::Stub* stub,
                                     const SimpleRequest& req) {
     return new ClientRpcContextStreamingImpl<SimpleRequest, SimpleResponse>(
         channel_id, stub, req, AsyncStreamingClient::StartReq,
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index b9d857c..44d525b 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -54,10 +54,10 @@
 
 #include "test/cpp/util/create_test_channel.h"
 #include "test/cpp/qps/client.h"
-#include "test/proto/perf_tests/perf_control.grpc.pb.h"
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/interarrival.h"
 #include "test/cpp/qps/timer.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 #include "src/core/profiling/timers.h"
 
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 500cc51..12b9fee 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -48,6 +48,7 @@
 #include "test/cpp/qps/driver.h"
 #include "test/cpp/qps/histogram.h"
 #include "test/cpp/qps/qps_worker.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 using std::list;
 using std::thread;
@@ -91,12 +92,12 @@
 }
 
 struct ServerData {
-  unique_ptr<Worker::Stub> stub;
+  unique_ptr<WorkerService::Stub> stub;
   unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
 };
 
 struct ClientData {
-  unique_ptr<Worker::Stub> stub;
+  unique_ptr<WorkerService::Stub> stub;
   unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream;
 };
 }  // namespace runsc
@@ -162,7 +163,7 @@
   auto* servers = new ServerData[num_servers];
   for (size_t i = 0; i < num_servers; i++) {
     servers[i].stub =
-        Worker::NewStub(CreateChannel(workers[i], InsecureCredentials()));
+        WorkerService::NewStub(CreateChannel(workers[i], InsecureCredentials()));
     ServerArgs args;
     result_server_config = server_config;
     result_server_config.set_host(workers[i]);
@@ -189,7 +190,7 @@
   // where class contained in std::vector must have a copy constructor
   auto* clients = new ClientData[num_clients];
   for (size_t i = 0; i < num_clients; i++) {
-    clients[i].stub = Worker::NewStub(
+    clients[i].stub = WorkerService::NewStub(
         CreateChannel(workers[i + num_servers], InsecureCredentials()));
     ClientArgs args;
     result_client_config = client_config;
@@ -211,9 +212,9 @@
   // Start a run
   gpr_log(GPR_INFO, "Starting");
   ServerArgs server_mark;
-  server_mark.mutable_mark();
+  server_mark.mutable_mark()->set_reset(true);
   ClientArgs client_mark;
-  client_mark.mutable_mark();
+  client_mark.mutable_mark()->set_reset(true);
   for (auto server = &servers[0]; server != &servers[num_servers]; server++) {
     GPR_ASSERT(server->stream->Write(server_mark));
   }
diff --git a/test/cpp/qps/histogram.h b/test/cpp/qps/histogram.h
index 8ea9cb6..f7cb308 100644
--- a/test/cpp/qps/histogram.h
+++ b/test/cpp/qps/histogram.h
@@ -48,7 +48,7 @@
   }
   Histogram(Histogram&& other) : impl_(other.impl_) { other.impl_ = nullptr; }
 
-  void Merge(Histogram* h) { gpr_histogram_merge(impl_, h->impl_); }
+  void Merge(const Histogram& h) { gpr_histogram_merge(impl_, h.impl_); }
   void Add(double value) { gpr_histogram_add(impl_, value); }
   double Percentile(double pctile) const {
     return gpr_histogram_percentile(impl_, pctile);
diff --git a/test/cpp/qps/qps_driver.cc b/test/cpp/qps/qps_driver.cc
index 3d352b4..658cf87 100644
--- a/test/cpp/qps/qps_driver.cc
+++ b/test/cpp/qps/qps_driver.cc
@@ -72,8 +72,6 @@
 DEFINE_double(pareto_base, -1.0, "Pareto base interarrival time (us)");
 DEFINE_double(pareto_alpha, -1.0, "Pareto alpha value");
 
-DEFINE_string(load_type, "CLOSED_LOOP", "Load type");
-
 using grpc::testing::ClientConfig;
 using grpc::testing::ServerConfig;
 using grpc::testing::ClientType;
@@ -119,8 +117,8 @@
     pareto->set_interarrival_base(FLAGS_pareto_base / 1e6);
     pareto->set_alpha(FLAGS_pareto_alpha);
   } else {
-    // Default is closed loop
-    // No need to set up any other load parameters here
+    client_config.mutable_load_params()->mutable_closed();
+    // No further load parameters to set up for closed loop
   }
 
   ServerConfig server_config;
diff --git a/test/cpp/qps/qps_test.cc b/test/cpp/qps/qps_test.cc
index c3dbc25..82850e5 100644
--- a/test/cpp/qps/qps_test.cc
+++ b/test/cpp/qps/qps_test.cc
@@ -58,6 +58,7 @@
   client_config.set_payload_size(1);
   client_config.set_async_client_threads(8);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/qps_test_with_poll.cc b/test/cpp/qps/qps_test_with_poll.cc
index a7a11a6..153cbd7 100644
--- a/test/cpp/qps/qps_test_with_poll.cc
+++ b/test/cpp/qps/qps_test_with_poll.cc
@@ -62,6 +62,7 @@
   client_config.set_payload_size(1);
   client_config.set_async_client_threads(8);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index f16740b..76b4c9b 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -52,10 +52,10 @@
 #include <grpc++/security/server_credentials.h>
 
 #include "test/core/util/grpc_profiler.h"
-#include "test/proto/perf_tests/perf_control.pb.h"
 #include "test/cpp/qps/client.h"
 #include "test/cpp/qps/server.h"
 #include "test/cpp/util/create_test_channel.h"
+#include "test/proto/perf_tests/perf_services.pb.h"
 
 namespace grpc {
 namespace testing {
@@ -89,9 +89,9 @@
   abort();
 }
 
-class WorkerImpl GRPC_FINAL : public Worker::Service {
+class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
  public:
-  explicit WorkerImpl(int server_port)
+  explicit WorkerServiceImpl(int server_port)
       : server_port_(server_port), acquired_(false) {}
 
   Status RunClient(ServerContext* ctx,
@@ -126,7 +126,7 @@
   // Protect against multiple clients using this worker at once.
   class InstanceGuard {
    public:
-    InstanceGuard(WorkerImpl* impl)
+    InstanceGuard(WorkerServiceImpl* impl)
         : impl_(impl), acquired_(impl->TryAcquireInstance()) {}
     ~InstanceGuard() {
       if (acquired_) {
@@ -137,7 +137,7 @@
     bool Acquired() const { return acquired_; }
 
    private:
-    WorkerImpl* const impl_;
+    WorkerServiceImpl* const impl_;
     const bool acquired_;
   };
 
@@ -175,7 +175,7 @@
       if (!args.has_mark()) {
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
-      *status.mutable_stats() = client->Mark();
+      *status.mutable_stats() = client->Mark(args.mark().reset());
       stream->Write(status);
     }
 
@@ -204,7 +204,7 @@
       if (!args.has_mark()) {
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
-      *status.mutable_stats() = server->Mark();
+      *status.mutable_stats() = server->Mark(args.mark().reset());
       stream->Write(status);
     }
 
@@ -218,7 +218,7 @@
 };
 
 QpsWorker::QpsWorker(int driver_port, int server_port) {
-  impl_.reset(new WorkerImpl(server_port));
+  impl_.reset(new WorkerServiceImpl(server_port));
 
   char* server_address = NULL;
   gpr_join_host_port(&server_address, "::", driver_port);
diff --git a/test/cpp/qps/qps_worker.h b/test/cpp/qps/qps_worker.h
index 8615889..d5a7d7d 100644
--- a/test/cpp/qps/qps_worker.h
+++ b/test/cpp/qps/qps_worker.h
@@ -42,7 +42,7 @@
 
 namespace testing {
 
-class WorkerImpl;
+class WorkerServiceImpl;
 
 class QpsWorker {
  public:
@@ -50,7 +50,7 @@
   ~QpsWorker();
 
  private:
-  std::unique_ptr<WorkerImpl> impl_;
+  std::unique_ptr<WorkerServiceImpl> impl_;
   std::unique_ptr<Server> server_;
 };
 
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index ba8394b..c99ef97 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -35,6 +35,7 @@
 #define TEST_QPS_SERVER_H
 
 #include "test/cpp/qps/timer.h"
+#include "test/proto/messages.grpc.pb.h"
 #include "test/proto/perf_tests/perf_control.grpc.pb.h"
 
 namespace grpc {
@@ -45,11 +46,15 @@
   Server() : timer_(new Timer) {}
   virtual ~Server() {}
 
-  ServerStats Mark() {
-    std::unique_ptr<Timer> timer(new Timer);
-    timer.swap(timer_);
-
-    auto timer_result = timer->Mark();
+  ServerStats Mark(bool reset) {
+    Timer::Result timer_result;
+    if (reset) {
+      std::unique_ptr<Timer> timer(new Timer);
+      timer.swap(timer_);
+      timer_result = timer->Mark();
+    } else {
+      timer_result = timer_->Mark();
+    }
 
     ServerStats stats;
     stats.set_time_elapsed(timer_result.wall);
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index db52186..2aee729 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -49,8 +49,8 @@
 #include <grpc++/security/server_credentials.h>
 #include <gtest/gtest.h>
 
-#include "test/proto/perf_tests/perf_control.grpc.pb.h"
 #include "test/cpp/qps/server.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
@@ -76,10 +76,10 @@
     for (int i = 0; i < 10000 / config.threads(); i++) {
       for (int j = 0; j < config.threads(); j++) {
         auto request_unary = std::bind(
-            &TestService::AsyncService::RequestUnaryCall, &async_service_, _1,
+            &BenchmarkService::AsyncService::RequestUnaryCall, &async_service_, _1,
             _2, _3, srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
         auto request_streaming = std::bind(
-            &TestService::AsyncService::RequestStreamingCall, &async_service_,
+            &BenchmarkService::AsyncService::RequestStreamingCall, &async_service_,
             _1, _2, srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
         contexts_.push_front(
             new ServerRpcContextUnaryImpl<SimpleRequest, SimpleResponse>(
@@ -309,7 +309,7 @@
   std::vector<std::thread> threads_;
   std::unique_ptr<grpc::Server> server_;
   std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
-  TestService::AsyncService async_service_;
+  BenchmarkService::AsyncService async_service_;
   std::forward_list<ServerRpcContext *> contexts_;
 
   class PerThreadShutdownState {
diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc
index 2b83548..20da139 100644
--- a/test/cpp/qps/server_sync.cc
+++ b/test/cpp/qps/server_sync.cc
@@ -43,14 +43,14 @@
 #include <grpc++/server_context.h>
 #include <grpc++/security/server_credentials.h>
 
-#include "test/proto/perf_tests/perf_control.grpc.pb.h"
 #include "test/cpp/qps/server.h"
 #include "test/cpp/qps/timer.h"
+#include "test/proto/perf_tests/perf_services.grpc.pb.h"
 
 namespace grpc {
 namespace testing {
 
-class TestServiceImpl GRPC_FINAL : public TestService::Service {
+class BenchmarkServiceImpl GRPC_FINAL : public BenchmarkService::Service {
  public:
   Status UnaryCall(ServerContext* context, const SimpleRequest* request,
                    SimpleResponse* response) GRPC_OVERRIDE {
@@ -101,7 +101,7 @@
     return builder.BuildAndStart();
   }
 
-  TestServiceImpl service_;
+  BenchmarkServiceImpl service_;
   std::unique_ptr<grpc::Server> impl_;
 };
 
diff --git a/test/cpp/qps/sync_streaming_ping_pong_test.cc b/test/cpp/qps/sync_streaming_ping_pong_test.cc
index 6dddb9e..ce10a87 100644
--- a/test/cpp/qps/sync_streaming_ping_pong_test.cc
+++ b/test/cpp/qps/sync_streaming_ping_pong_test.cc
@@ -57,6 +57,7 @@
   client_config.set_client_channels(1);
   client_config.set_payload_size(1);
   client_config.set_rpc_type(STREAMING);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(SYNCHRONOUS_SERVER);
diff --git a/test/cpp/qps/sync_unary_ping_pong_test.cc b/test/cpp/qps/sync_unary_ping_pong_test.cc
index 774e70e..c20e2c5 100644
--- a/test/cpp/qps/sync_unary_ping_pong_test.cc
+++ b/test/cpp/qps/sync_unary_ping_pong_test.cc
@@ -57,6 +57,7 @@
   client_config.set_client_channels(1);
   client_config.set_payload_size(1);
   client_config.set_rpc_type(UNARY);
+  client_config.mutable_load_params()->mutable_closed();
 
   ServerConfig server_config;
   server_config.set_server_type(SYNCHRONOUS_SERVER);
diff --git a/test/cpp/qps/timer.cc b/test/cpp/qps/timer.cc
index 8edb838..3ec7f49 100644
--- a/test/cpp/qps/timer.cc
+++ b/test/cpp/qps/timer.cc
@@ -61,7 +61,7 @@
   return r;
 }
 
-Timer::Result Timer::Mark() {
+Timer::Result Timer::Mark() const {
   Result s = Sample();
   Result r;
   r.wall = s.wall - start_.wall;
diff --git a/test/cpp/qps/timer.h b/test/cpp/qps/timer.h
index 30dbd7e..d1aee1a 100644
--- a/test/cpp/qps/timer.h
+++ b/test/cpp/qps/timer.h
@@ -44,7 +44,7 @@
     double system;
   };
 
-  Result Mark();
+  Result Mark() const;
 
   static double Now();
 
diff --git a/test/proto/perf_tests/perf_control.proto b/test/proto/perf_tests/perf_control.proto
index 9c3e822..bfdbfac 100644
--- a/test/proto/perf_tests/perf_control.proto
+++ b/test/proto/perf_tests/perf_control.proto
@@ -31,7 +31,6 @@
 // of unary/streaming requests/responses.
 syntax = "proto3";
 
-import "test/proto/messages.proto";
 import "test/proto/perf_tests/perf_stats.proto";
 
 package grpc.testing;
@@ -69,14 +68,16 @@
   double alpha = 2;
 }
 
+message ClosedLoopParams {
+}
+
 message LoadParams {
   oneof load {
     PoissonParams poisson = 1;
     UniformParams uniform = 2;
     DeterministicParams determ = 3;
     ParetoParams pareto = 4;
-    // No need to separately specify Closed-Loop as that
-    // will just be the absence of any of the above
+    ClosedLoopParams closed = 5;
   };
 }
 
@@ -100,6 +101,7 @@
 
 // Request current stats
 message Mark {
+  bool reset = 1;
 }
 
 message ClientArgs {
@@ -127,21 +129,3 @@
   ServerStats stats = 1;
   int32 port = 2;
 }
-
-service TestService {
-  // One request followed by one response.
-  // The server returns the client payload as-is.
-  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
-
-  // One request followed by one response.
-  // The server returns the client payload as-is.
-  rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
-}
-
-service Worker {
-  // Start server with specified workload
-  rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
-
-  // Start client with specified workload
-  rpc RunClient(stream ClientArgs) returns (stream ClientStatus);
-}
diff --git a/test/proto/perf_tests/perf_services.proto b/test/proto/perf_tests/perf_services.proto
new file mode 100644
index 0000000..f9497e2
--- /dev/null
+++ b/test/proto/perf_tests/perf_services.proto
@@ -0,0 +1,56 @@
+// 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.
+
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto3";
+
+import "test/proto/messages.proto";
+import "test/proto/perf_tests/perf_stats.proto";
+import "test/proto/perf_tests/perf_control.proto";
+
+package grpc.testing;
+
+service BenchmarkService {
+  // One request followed by one response.
+  // The server returns the client payload as-is.
+  rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+  // One request followed by one response.
+  // The server returns the client payload as-is.
+  rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
+}
+
+service WorkerService {
+  // Start server with specified workload
+  rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
+
+  // Start client with specified workload
+  rpc RunClient(stream ClientArgs) returns (stream ClientStatus);
+}
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 60abb62..a3902cf 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -13660,6 +13660,8 @@
       "test/proto/messages.pb.h", 
       "test/proto/perf_tests/perf_control.grpc.pb.h", 
       "test/proto/perf_tests/perf_control.pb.h", 
+      "test/proto/perf_tests/perf_services.grpc.pb.h", 
+      "test/proto/perf_tests/perf_services.pb.h", 
       "test/proto/perf_tests/perf_stats.grpc.pb.h", 
       "test/proto/perf_tests/perf_stats.pb.h"
     ], 
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj b/vsprojects/vcxproj/qps/qps.vcxproj
index e1987db..152ba7a 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj
+++ b/vsprojects/vcxproj/qps/qps.vcxproj
@@ -163,6 +163,14 @@
     </ClCompile>
     <ClInclude Include="..\..\..\test\proto\perf_tests\perf_control.grpc.pb.h">
     </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\perf_tests\perf_services.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\perf_tests\perf_services.pb.h">
+    </ClInclude>
+    <ClCompile Include="..\..\..\test\proto\perf_tests\perf_services.grpc.pb.cc">
+    </ClCompile>
+    <ClInclude Include="..\..\..\test\proto\perf_tests\perf_services.grpc.pb.h">
+    </ClInclude>
     <ClCompile Include="..\..\..\test\proto\perf_tests\perf_stats.pb.cc">
     </ClCompile>
     <ClInclude Include="..\..\..\test\proto\perf_tests\perf_stats.pb.h">
diff --git a/vsprojects/vcxproj/qps/qps.vcxproj.filters b/vsprojects/vcxproj/qps/qps.vcxproj.filters
index f679ad4..8bbd5c3 100644
--- a/vsprojects/vcxproj/qps/qps.vcxproj.filters
+++ b/vsprojects/vcxproj/qps/qps.vcxproj.filters
@@ -7,6 +7,9 @@
     <ClCompile Include="..\..\..\test\proto\perf_tests\perf_control.proto">
       <Filter>test\proto\perf_tests</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\test\proto\perf_tests\perf_services.proto">
+      <Filter>test\proto\perf_tests</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\..\test\proto\perf_tests\perf_stats.proto">
       <Filter>test\proto\perf_tests</Filter>
     </ClCompile>