1 // Copyright 2017 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_BENCHMARKS_BENCHMARK_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_BENCHMARKS_BENCHMARK_H
17 
18 #include "google/cloud/bigtable/benchmarks/embedded_server.h"
19 #include "google/cloud/bigtable/benchmarks/setup.h"
20 #include "google/cloud/bigtable/table.h"
21 #include "google/cloud/internal/random.h"
22 #include "google/cloud/status_or.h"
23 #include <chrono>
24 #include <deque>
25 #include <thread>
26 
27 namespace google {
28 namespace cloud {
29 namespace bigtable {
30 namespace benchmarks {
31 /// The result of a single operation.
32 struct OperationResult {
33   google::cloud::Status status;
34   std::chrono::microseconds latency;
35 };
36 
37 struct BenchmarkResult {
38   std::chrono::milliseconds elapsed;
39   std::deque<OperationResult> operations;
40   long row_count;  // NOLINT(google-runtime-int)
41 };
42 
43 /**
44  * Common code used by the Cloud Bigtable C++ Client benchmarks.
45  */
46 class Benchmark {
47  public:
48   explicit Benchmark(BenchmarkSetup setup);
49   ~Benchmark();
50 
51   Benchmark(Benchmark const&) = delete;
52   Benchmark& operator=(Benchmark const&) = delete;
53 
54   /// Create a table for the benchmark, return the table_id.
55   std::string CreateTable();
56 
57   /// Delete the table used in the benchmark.
58   void DeleteTable();
59 
60   /// Populate the table with initial data.
61   google::cloud::StatusOr<BenchmarkResult> PopulateTable();
62 
63   /// Return a `bigtable::DataClient` configured for this benchmark.
64   std::shared_ptr<bigtable::DataClient> MakeDataClient();
65 
66   /// Create a random key.
67   std::string MakeRandomKey(google::cloud::internal::DefaultPRNG& gen) const;
68 
69   /// Return the key for row @p id.
70   std::string MakeKey(long id) const;  // NOLINT(google-runtime-int)
71 
72   /// Measure the time to compute an operation.
73   template <typename Operation>
TimeOperation(Operation && op)74   static OperationResult TimeOperation(Operation&& op) {
75     auto start = std::chrono::steady_clock::now();
76     auto status = op();
77     using std::chrono::duration_cast;
78     auto elapsed = duration_cast<std::chrono::microseconds>(
79         std::chrono::steady_clock::now() - start);
80     return OperationResult{status, elapsed};
81   }
82 
83   /// Print the result of a throughput test in human readable form.
84   static void PrintThroughputResult(std::ostream& os,
85                                     std::string const& test_name,
86                                     std::string const& phase,
87                                     BenchmarkResult const& result);
88 
89   /// Print the result of a latency test in human readable form.
90   static void PrintLatencyResult(std::ostream& os, std::string const& test_name,
91                                  std::string const& operation,
92                                  BenchmarkResult& result);
93 
94   /// Return the header for CSV results.
95   static std::string ResultsCsvHeader();
96 
97   /// Print the result of a benchmark as a CSV line.
98   void PrintResultCsv(std::ostream& os, std::string const& test_name,
99                       std::string const& op_name,
100                       std::string const& measurement,
101                       BenchmarkResult& result) const;
102 
103   //@{
104   /**
105    * @name Embedded server counter accessors.
106    *
107    * Return 0 if there is no embedded server, or the value from the
108    * corresponding embedded server counter.  This class is tested largely by
109    * observing how many calls it makes on the embedded server.  Because the
110    * embedded server has no memory, that is the only observable effect when
111    * unit testing the class.
112    */
113   int create_table_count() const;
114   int delete_table_count() const;
115   int mutate_row_count() const;
116   int mutate_rows_count() const;
117   int read_rows_count() const;
118   //@}
119 
120  private:
121   /// Populate the table rows in the range [@p begin, @p end)
122   google::cloud::StatusOr<BenchmarkResult> PopulateTableShard(
123       bigtable::Table& table, long begin,  // NOLINT(google-runtime-int)
124       long end);                           // NOLINT(google-runtime-int)
125 
126   /**
127    * Return how much space to reserve for digits if the table has @p table_size
128    * elements.
129    */
130   int KeyWidth() const;
131 
132  private:
133   BenchmarkSetup setup_;
134   int key_width_;
135   bigtable::ClientOptions client_options_;
136   std::unique_ptr<EmbeddedServer> server_;
137   std::thread server_thread_;
138 };
139 
140 /// Helper class to pretty print durations.
141 struct FormatDuration {
142   template <typename Rep, typename Period>
FormatDurationFormatDuration143   explicit FormatDuration(std::chrono::duration<Rep, Period> d)
144       : ns(std::chrono::duration_cast<std::chrono::nanoseconds>(d)) {}
145   std::chrono::nanoseconds ns;
146 };
147 
148 /**
149  * Pretty print an elapsed time.
150  *
151  * The benchmarks need to report time in human readable terms.  This operator
152  * streams a FormatDuration in hours, minutes, seconds and sub-seconds.  Any
153  * component that is zero gets ommitted, e.g. 1 hour exactly is printed as 1h.
154  *
155  * If the time is less than 1 second then the format uses millisecond or
156  * microsecond resolution, as appropriate.
157  *
158  * @param os the destination stream.
159  * @param duration the duration value.
160  * @return the stream after printing.
161  */
162 std::ostream& operator<<(std::ostream& os, FormatDuration duration);
163 
164 }  // namespace benchmarks
165 }  // namespace bigtable
166 }  // namespace cloud
167 }  // namespace google
168 
169 #endif  // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_BIGTABLE_BENCHMARKS_BENCHMARK_H
170