[hlcpp][benchmarks] HLCPP benchmarks for "echo calls"

This adds benchmarking of making a FIDL call - in particular a call that
echos the value back to the client. This is intended to detect
regressions that make HLCPP calls slower. The sync API is used here on
the client side but a similar benchmark could be made with the async
API.

HLCPP/EchoCall/ByteVector/256/Steps.EchoCall/WallTime                                                    added             -              -                     7934 +/- 199 ns
HLCPP/EchoCall/ByteVector/4096/Steps.EchoCall/WallTime                                                   added             -              -                     12037 +/- 196 ns

Change-Id: I5c1a4a2ab9a1e7e089b7743cd5e4808470699f04
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/405315
Reviewed-by: Yifei Teng <yifeit@google.com>
Testability-Review: Yifei Teng <yifeit@google.com>
Commit-Queue: Benjamin Prosnitz <bprosnitz@google.com>
diff --git a/src/tests/benchmarks/fidl/hlcpp/echo_call_benchmark_util.h b/src/tests/benchmarks/fidl/hlcpp/echo_call_benchmark_util.h
new file mode 100644
index 0000000..d370862
--- /dev/null
+++ b/src/tests/benchmarks/fidl/hlcpp/echo_call_benchmark_util.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_TESTS_BENCHMARKS_FIDL_HLCPP_ECHO_CALL_BENCHMARK_UTIL_H_
+#define SRC_TESTS_BENCHMARKS_FIDL_HLCPP_ECHO_CALL_BENCHMARK_UTIL_H_
+
+#include <lib/async-loop/cpp/loop.h>
+#include <lib/async-loop/default.h>
+#include <lib/fidl/cpp/binding.h>
+
+#include <perftest/perftest.h>
+
+namespace hlcpp_benchmarks {
+
+template <typename ProtocolType, typename FidlType>
+class EchoServerImpl : public ProtocolType {
+  void Echo(FidlType val, typename ProtocolType::EchoCallback callback) override { callback(val); }
+};
+
+template <typename ProtocolType, typename BuilderFunc>
+bool EchoCallBenchmark(perftest::RepeatState* state, BuilderFunc builder) {
+  using FidlType = std::invoke_result_t<BuilderFunc>;
+
+  state->DeclareStep("Setup/WallTime");
+  state->DeclareStep("EchoCall/WallTime");
+  state->DeclareStep("Teardown/WallTime");
+
+  async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
+  fidl::SynchronousInterfacePtr<ProtocolType> ptr;
+
+  EchoServerImpl<ProtocolType, FidlType> server;
+  fidl::Binding<ProtocolType> server_binding(&server);
+  server_binding.Bind(ptr.NewRequest());
+
+  loop.StartThread();
+
+  while (state->KeepRunning()) {
+    FidlType in = builder();
+
+    state->NextStep();  // End: Setup. Begin: EchoCall.
+
+    FidlType out;
+    zx_status_t status = ptr->Echo(in, &out);
+
+    state->NextStep();  // End: EchoCall. Begin: Teardown
+
+    ZX_ASSERT(status == ZX_OK);
+  }
+
+  loop.Quit();
+  return true;
+}
+
+}  // namespace hlcpp_benchmarks
+
+#endif  // SRC_TESTS_BENCHMARKS_FIDL_HLCPP_ECHO_CALL_BENCHMARK_UTIL_H_
diff --git a/tools/fidl/gidl/cpp/benchmarks.go b/tools/fidl/gidl/cpp/benchmarks.go
index 2ba65f6..c8f3924 100644
--- a/tools/fidl/gidl/cpp/benchmarks.go
+++ b/tools/fidl/gidl/cpp/benchmarks.go
@@ -21,6 +21,7 @@
 #include "src/tests/benchmarks/fidl/hlcpp/builder_benchmark_util.h"
 #include "src/tests/benchmarks/fidl/hlcpp/decode_benchmark_util.h"
 #include "src/tests/benchmarks/fidl/hlcpp/encode_benchmark_util.h"
+#include "src/tests/benchmarks/fidl/hlcpp/echo_call_benchmark_util.h"
 #include "src/tests/benchmarks/fidl/hlcpp/send_event_benchmark_util.h"
 
 namespace {
@@ -44,6 +45,11 @@
 	return hlcpp_benchmarks::SendEventBenchmark<{{ .EventProtocolType }}>(state, Build{{ .Name }});
 }
 {{- end -}}
+{{ if .EnableEchoCallBenchmark }}
+bool BenchmarkEchoCall{{ .Name }}(perftest::RepeatState* state) {
+	return hlcpp_benchmarks::EchoCallBenchmark<{{ .EchoCallProtocolType }}>(state, Build{{ .Name }});
+}
+{{- end -}}
 
 void RegisterTests() {
   perftest::RegisterTest("HLCPP/Builder/{{ .Path }}/WallTime", BenchmarkBuilder{{ .Name }});
@@ -52,6 +58,9 @@
   {{ if .EnableSendEventBenchmark }}
   perftest::RegisterTest("HLCPP/SendEvent/{{ .Path }}/Steps", BenchmarkSendEvent{{ .Name }});
   {{- end -}}
+  {{ if .EnableEchoCallBenchmark }}
+  perftest::RegisterTest("HLCPP/EchoCall/{{ .Path }}/Steps", BenchmarkEchoCall{{ .Name }});
+  {{- end -}}
 }
 PERFTEST_CTOR(RegisterTests)
 
@@ -59,10 +68,10 @@
 `))
 
 type benchmarkTmplInput struct {
-	Path, Name, Type         string
-	ValueBuild, ValueVar     string
-	EventProtocolType        string
-	EnableSendEventBenchmark bool
+	Path, Name, Type                                  string
+	ValueBuild, ValueVar                              string
+	EventProtocolType, EchoCallProtocolType           string
+	EnableSendEventBenchmark, EnableEchoCallBenchmark bool
 }
 
 // Generate generates High-Level C++ benchmarks.
@@ -88,7 +97,9 @@
 			ValueBuild:               valueBuild,
 			ValueVar:                 valueVar,
 			EventProtocolType:        benchmarkTypeFromValue(gidlBenchmark.Value) + "EventProtocol",
+			EchoCallProtocolType:     benchmarkTypeFromValue(gidlBenchmark.Value) + "EchoCall",
 			EnableSendEventBenchmark: gidlBenchmark.EnableSendEventBenchmark,
+			EnableEchoCallBenchmark:  gidlBenchmark.EnableEchoCallBenchmark,
 		}); err != nil {
 			return nil, err
 		}