ALP User Documentation  0.8.preview
Algebraic Programming User Documentation
benchmark.hpp
Go to the documentation of this file.
1 
2 /*
3  * Copyright 2021 Huawei Technologies Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
28 #ifndef _H_GRB_BENCH_BASE
29 #define _H_GRB_BENCH_BASE
30 
31 #include <cmath> // for sqrt
32 #include <limits>
33 #include <vector> // warning: normally should not be used in ALP backends(!)
34 
35 #ifndef _GRB_NO_STDIO
36  #include <ios>
37  #include <chrono>
38  #include <iostream>
39 #endif
40 
41 #ifndef _GRB_NO_EXCEPTIONS
42  #include <stdexcept>
43 #endif
44 
45 #include <graphblas/backends.hpp>
46 #include <graphblas/ops.hpp>
47 #include <graphblas/rc.hpp>
48 #include <graphblas/utils.hpp>
49 #include <graphblas/utils/timerResults.hpp>
50 
51 #include "collectives.hpp"
52 #include "config.hpp"
53 #include "exec.hpp"
54 
55 
81 namespace grb {
82 
83  namespace internal {
84 
90  class BenchmarkerBase {
91 
92  protected:
93 
94 #ifndef _GRB_NO_STDIO
95 
103  static void printTimeSinceEpoch( const bool printHeader = true ) {
104  const auto now = std::chrono::system_clock::now();
105  const auto since = now.time_since_epoch();
106  if( printHeader ) {
107  std::cout << "Time since epoch (in ms.): ";
108  }
109  std::cout << std::chrono::duration_cast<
110  std::chrono::milliseconds
111  >( since ).count() << "\n";
112  }
113 #endif
114 
118  static void benchmark_calc_inner(
119  const size_t loop,
120  const size_t total,
121  grb::utils::TimerResults &inner_times,
122  grb::utils::TimerResults &total_times,
123  grb::utils::TimerResults &min_times,
124  grb::utils::TimerResults &max_times,
125  std::vector< grb::utils::TimerResults > &sdev_times
126  ) {
127  inner_times.normalize( total );
128  total_times.accum( inner_times );
129  min_times.min( inner_times );
130  max_times.max( inner_times );
131  sdev_times[ loop ] = inner_times;
132  }
133 
137  static void benchmark_calc_outer(
138  const size_t total,
139  grb::utils::TimerResults &total_times,
140  grb::utils::TimerResults &min_times,
141  grb::utils::TimerResults &max_times,
142  std::vector< grb::utils::TimerResults > &sdev_times,
143  const size_t pid
144  ) {
145  total_times.normalize( total );
146  grb::utils::TimerResults sdev;
147  // compute standard dev of average times, leaving sqrt calculation until
148  // the output of the values
149  sdev.set( 0 );
150  for( size_t i = 0; i < total; i++ ) {
151  double diff = sdev_times[ i ].io - total_times.io;
152  sdev.io += diff * diff;
153  diff = sdev_times[ i ].preamble - total_times.preamble;
154  sdev.preamble += diff * diff;
155  diff = sdev_times[ i ].useful - total_times.useful;
156  sdev.useful += diff * diff;
157  diff = sdev_times[ i ].postamble - total_times.postamble;
158  sdev.postamble += diff * diff;
159  }
160  // unbiased normalisation of the standard deviation
161  sdev.normalize( total - 1 );
162 
163 #ifndef _GRB_NO_STDIO
164  // output results
165  if( pid == 0 ) {
166  std::cout << "Overall timings (io, preamble, useful, postamble):\n"
167  << std::scientific;
168  std::cout << "Avg: " << total_times.io << ", " << total_times.preamble
169  << ", " << total_times.useful << ", " << total_times.postamble << "\n";
170  std::cout << "Min: " << min_times.io << ", " << min_times.preamble << ", "
171  << min_times.useful << ", " << min_times.postamble << "\n";
172  std::cout << "Max: " << max_times.io << ", " << max_times.preamble << ", "
173  << max_times.useful << ", " << max_times.postamble << "\n";
174  std::cout << "Std: " << sqrt( sdev.io ) << ", " << sqrt( sdev.preamble )
175  << ", " << sqrt( sdev.useful ) << ", " << sqrt( sdev.postamble ) << "\n";
176  #if __GNUC__ > 4
177  std::cout << std::defaultfloat;
178  #endif
179  printTimeSinceEpoch();
180  }
181 #else
182  // we ran the benchmark, but may not have a way to output it in this case
183  // this currently only is touched by the #grb::banshee backend, which
184  // provides other timing mechanisms.
185  (void) min_times;
186  (void) max_times;
187  (void) pid;
188 #endif
189  }
190 
211  template<
212  enum Backend implementation,
213  typename RunnerType
214  >
215  static RC benchmark(
216  RunnerType &runner,
217  grb::utils::TimerResults &times,
218  const size_t inner, const size_t outer,
219  const size_t pid
220  ) {
221  const double inf = std::numeric_limits< double >::infinity();
222  grb::utils::TimerResults total_times, min_times, max_times;
223  std::vector< grb::utils::TimerResults > sdev_times( outer );
224  total_times.set( 0 );
225  min_times.set( inf );
226  max_times.set( 0 );
227  grb::RC ret = grb::SUCCESS;
228 
229  // outer loop
230  for( size_t out = 0; out < outer && ret == grb::SUCCESS; ++out ) {
231  grb::utils::TimerResults inner_times;
232  inner_times.set( 0 );
233 
234  // inner loop
235  for( size_t in = 0; in < inner && ret == grb::SUCCESS; ++in ) {
236  times.set( 0 );
237 
238  runner();
239 
241  times.io, 0, grb::operators::max< double >() );
243  times.preamble, 0, grb::operators::max< double >() );
245  times.useful, 0, grb::operators::max< double >() );
247  times.postamble, 0, grb::operators::max< double >() );
248 
249  if( ret == grb::SUCCESS ) {
250  inner_times.accum( times );
251  }
252  }
253 
254  if( ret == grb::SUCCESS ) {
255  // calculate performance stats
256  benchmark_calc_inner( out, inner, inner_times, total_times, min_times,
257  max_times, sdev_times );
258  }
259 
260 #ifndef _GRB_NO_STDIO
261  // give experiment output line
262  if( pid == 0 ) {
263  if( ret == grb::SUCCESS ) {
264  std::ios_base::fmtflags prev_cout_state( std::cout.flags() );
265  std::cout << "Outer iteration #" << out << " timings "
266  << "(io, preamble, useful, postamble, time since epoch): "
267  << std::fixed
268  << inner_times.io << ", " << inner_times.preamble << ", "
269  << inner_times.useful << ", " << inner_times.postamble << ", ";
270  printTimeSinceEpoch( false );
271  std::cout.flags( prev_cout_state );
272  } else {
273  std::cerr << "Error during cross-process collection of timing results: "
274  << "\t" << grb::toString( ret ) << std::endl;
275  }
276  }
277 #endif
278 
279  // pause for next outer loop
280  if( sleep( 1 ) != 0 && ret == grb::SUCCESS ) {
281 #ifndef _GRB_NO_STDIO
282  std::cerr << "Sleep interrupted, assume benchmark is unreliable; "
283  << "exiting.\n";
284 #endif
285  abort();
286  }
287  }
288 
289  if( ret == grb::SUCCESS ) {
290  // calculate performance stats
291  benchmark_calc_outer( outer, total_times, min_times, max_times,
292  sdev_times, pid );
293  }
294 
295  return ret;
296  }
297 
320  template<
321  typename U,
322  enum Backend implementation
323  >
324  static RC benchmark(
325  AlpUntypedFunc< U > alp_program,
326  const void * data_in, const size_t in_size,
327  U &data_out,
328  const size_t inner, const size_t outer,
329  const size_t pid
330  ) {
331  auto runner = [ alp_program, data_in, in_size, &data_out ] {
332  alp_program( data_in, in_size, data_out );
333  };
334  return benchmark< implementation >( runner, data_out.times, inner, outer,
335  pid );
336  }
337 
360  template<
361  typename T, typename U,
362  enum Backend implementation
363  >
364  static RC benchmark(
365  AlpTypedFunc< T, U > alp_program,
366  const T &data_in, U &data_out,
367  const size_t inner, const size_t outer,
368  const size_t pid
369  ) {
370  auto runner = [ alp_program, &data_in, &data_out ] {
371  alp_program( data_in, data_out );
372  };
373  return benchmark< implementation >( runner, data_out.times, inner, outer,
374  pid );
375  }
376 
377 
378  public:
379 
380  BenchmarkerBase() {
381 #ifndef _GRB_NO_STDIO
382  printTimeSinceEpoch();
383 #endif
384  }
385 
386  };
387 
388  } // namespace internal
389 
398  template< enum EXEC_MODE mode, enum Backend implementation >
399  class Benchmarker {
400 
401  public :
402 
432  const size_t process_id = 0,
433  const size_t nprocs = 1,
434  const std::string hostname = "localhost",
435  const std::string port = "0"
436  ) {
437  (void) process_id;
438  (void) nprocs;
439  (void) hostname;
440  (void) port;
441 #ifndef _GRB_NO_EXCEPTIONS
442  throw std::logic_error( "Benchmarker class called with unsupported mode or "
443  "implementation" );
444 #endif
445  }
446 
485  template< typename T, typename U >
487  void ( *alp_program )( const T &, U & ),
488  const T &data_in, U &data_out,
489  const size_t inner, const size_t outer,
490  const bool broadcast = false
491  ) const {
492  (void) alp_program;
493  (void) data_in;
494  (void) data_out;
495  (void) inner;
496  (void) outer;
497  (void) broadcast;
498 
499  // stub implementation, should be overridden by specialised implementation.
500  // furthermore, it should be impossible to call this function without
501  // triggering an exception during construction of this stub class, so we
502  // just return PANIC here
503 #ifndef _GRB_NO_STDIO
504  std::cerr << "Error: base Benchmarker::exec called. An implementation-"
505  << "specific variant should have been called instead.\n";
506 #endif
507  return PANIC;
508  }
509 
548  template< typename U >
550  void ( *alp_program )( const void *, const size_t, U & ),
551  const void * data_in, const size_t in_size,
552  U &data_out,
553  const size_t inner, const size_t outer,
554  const bool broadcast = false
555  ) const {
556  (void) alp_program;
557  (void) data_in;
558  (void) in_size;
559  (void) data_out;
560  (void) inner;
561  (void) outer;
562  (void) broadcast;
563 
564  // stub implementation, should be overridden by specialised implementation.
565  // furthermore, it should be impossible to call this function without
566  // triggering an exception during construction of this stub class, so we
567  // just return PANIC here
568 #ifndef _GRB_NO_STDIO
569  std::cerr << "Error: base Benchmarker::exec called. An implementation-"
570  << "specific variant should have been called instead.\n";
571 #endif
572  return PANIC;
573  }
574 
592  static RC finalize() {
594  }
595 
596  };
597 
598 } // end namespace ``grb''
599 
600 #endif // end _H_GRB_BENCH_BASE
601 
Defines the ALP error codes.
This file contains a register of all backends that are either implemented, under implementation,...
RC
Return codes of ALP primitives.
Definition: rc.hpp:47
static RC finalize()
Releases all ALP resources.
Definition: benchmark.hpp:592
Benchmarker(const size_t process_id=0, const size_t nprocs=1, const std::string hostname="localhost", const std::string port="0")
Constructs an instance of the benchmarker class.
Definition: benchmark.hpp:431
This operator takes the maximum of the two input parameters and writes the result to the output varia...
Definition: ops.hpp:241
RC exec(void(*alp_program)(const void *, const size_t, U &), const void *data_in, const size_t in_size, U &data_out, const size_t inner, const size_t outer, const bool broadcast=false) const
Benchmarks a given ALP program.
Definition: benchmark.hpp:549
static RC reduce(IOType &inout, const size_t root=0, const Operator op=Operator())
Schedules a reduce operation of a single object of type IOType per process.
Definition: collectives.hpp:191
Backend
A collection of all backends.
Definition: backends.hpp:49
Specifies the grb::Launcher functionalities.
The ALP/GraphBLAS namespace.
Definition: graphblas.hpp:477
A class that follows the API of the grb::Launcher, but instead of launching the given ALP program onc...
Definition: benchmark.hpp:399
RC exec(void(*alp_program)(const T &, U &), const T &data_in, U &data_out, const size_t inner, const size_t outer, const bool broadcast=false) const
Benchmarks a given ALP program.
Definition: benchmark.hpp:486
Generic fatal error code.
Definition: rc.hpp:68
Defines both configuration parameters effective for all backends, as well as defines structured ways ...
Indicates the primitive has executed successfully.
Definition: rc.hpp:54
Specifies some basic collectives which may be used within a multi-process ALP program.
static RC finalize()
Releases all ALP resources.
Definition: exec.hpp:431
Provides a set of standard binary operators.
std::string toString(const RC code)