JayBeams  0.1
Another project to have fun coding.
bm_time_delay_estimator_many.cpp
Go to the documentation of this file.
1 /**
2  * This benchmark evaluates the performance of time_delay_estimator_many.
3  *
4  * It validates the assumption that FFTW performs better with an array of
5  * timeseries vs. being called multiple times with a one timeseries
6  * container each time.
7  */
15 
16 #include <chrono>
17 #include <iostream>
18 #include <stdexcept>
19 #include <string>
20 #include <vector>
21 
22 /// Helper types and functions to benchmark jb::fftw::time_delay_estimator.
23 namespace {
24 /**
25  * Configuration parameters for bm_time_delay_estimator_many.
26  *
27  * Add timeseries and log parameters to the base
28  * jb::testing::microbenchmark_config.
29  */
30 class config : public jb::config_object {
31 public:
32  config();
34 
35  void validate() const override;
36 
39  microbenchmark;
41 };
42 
43 /// Create all the test cases.
45 } // anonymous namespace
46 
47 int main(int argc, char* argv[]) {
48  // Simply call the generic microbenchmark for a group of testcases ...
49  return jb::testing::microbenchmark_group_main<config>(
50  argc, argv, create_testcases());
51 }
52 
53 namespace {
54 // These magic numbers are motivated by observed delays between two market
55 // feeds. They assume that delay is normally around 1,250 microseconds, but
56 // can be as large as 6,000 microseconds. To reliably detect the 6,000 usecs
57 // delays we need samples that cover at least 18,000 microseconds, assuming
58 // a sampling rate of 10 microseconds that is 1,800 samples, FFTs work well
59 // with sample sizes that are powers of 2, so we use 4096 samples.
60 std::chrono::microseconds const expected_delay(1250);
61 std::chrono::microseconds const sampling_period(10);
62 int const nsamples = 4096;
63 
64 /**
65  * Fixture for container like implementation (vector).
66  *
67  * Implements a call to bm_time_delay_estimator with n_timeseries iterations.
68  * Since
69  * estimate_delay might modify the values of the time series passed as
70  * arguments, a vector
71  * of n_timeseries time series is constructed with redundant data to pass one on
72  * each call.
73  *
74  * @tparam timeseries_type container (e.g. std::vector<>) containing one time
75  * series
76  */
77 template <typename timeseries_type>
78 class fixture {
79 public:
80  explicit fixture(int n_timeseries)
81  : fixture(nsamples, n_timeseries) {
82  }
83  fixture(int size, int n_timeseries)
84  : n_ts_(n_timeseries)
85  , va_(n_ts_, timeseries_type(size))
86  , vb_(n_ts_, timeseries_type(size))
87  , estimator_(va_[0], vb_[0])
88  , confidence_(va_[0])
89  , tde_(va_[0])
90  , sum2_(va_[0]) {
91  timeseries_type a(size), b(size);
92  // creates one time series...
95  a, expected_delay, sampling_period);
96  sum2_ = jb::testing::sum_square(a);
97  for (int i = 0; i != n_ts_; ++i) {
98  va_[i] = a;
99  vb_[i] = b;
100  }
101  }
102 
103  int run() {
104  for (int i = 0; i != n_ts_; ++i) {
105  estimator_.estimate_delay(confidence_, tde_, va_[i], vb_[i], sum2_);
106  }
107  return static_cast<int>(n_ts_ * va_.size());
108  }
109 
110  using vector_timeseries_type = typename std::vector<timeseries_type>;
112  using confidence_type = typename tested_type::confidence_type;
113  using estimated_delay_type = typename tested_type::estimated_delay_type;
114  using sum2_type = typename tested_type::sum2_type;
115 
116 private:
117  // Time series number. Number of times the estimate_delay has to be called
118  int n_ts_;
119  // contains n_ts_ copies of triangle shaped time series
120  vector_timeseries_type va_;
121  // contains n_ts_ copies of the same triangle shaped time series shifted by
122  // expected_delay
123  vector_timeseries_type vb_;
124  // the tested type, time delay estimator (TDE)
125  tested_type estimator_;
126  // ignored. Argumnet to call the TDE
127  confidence_type confidence_;
128  // ignored. Argument to call the TDE
129  estimated_delay_type tde_;
130  // the sum of the square of the triangle shaped time series (anyone is valid)
131  sum2_type sum2_;
132 };
133 
134 /**
135  * Fixture for boost multi array, only 2 dimensions allowed.
136  *
137  * @tparam T value type stores on the multi array
138  * @tparam A Allocator type to allocate value types
139  */
140 template <typename T, typename A>
141 class fixture<boost::multi_array<T, 2, A>> {
142 public:
143  explicit fixture(int n_timeseries)
144  : fixture(nsamples, n_timeseries) {
145  }
146  fixture(int size, int n_timeseries)
147  : a_(boost::extents[n_timeseries][size])
148  , b_(boost::extents[n_timeseries][size])
149  , estimator_(a_, b_)
150  , confidence_(a_)
151  , tde_(a_)
152  , sum2_(a_) {
153  // creates a family of n_timeseries time series of size samples each
156  a_, expected_delay, sampling_period);
157  sum2_ = jb::testing::sum_square(a_);
158  }
159 
160  int run() {
161  estimator_.estimate_delay(confidence_, tde_, a_, b_, sum2_);
162  return static_cast<int>(a_.shape()[0] * a_.shape()[1]);
163  }
164 
165  using timeseries_type = boost::multi_array<T, 2, A>;
167  using confidence_type = typename tested_type::confidence_type;
168  using estimated_delay_type = typename tested_type::estimated_delay_type;
169  using sum2_type = typename tested_type::sum2_type;
170 
171 private:
172  timeseries_type a_;
173  timeseries_type b_;
174  tested_type estimator_;
175  confidence_type confidence_;
176  estimated_delay_type tde_;
177  sum2_type sum2_;
178 };
179 
180 /**
181  * Create a test case for the given timeseries type.
182  *
183  * Returns a (type erased) functor to run the microbenchmark for the
184  * give timeseries type.
185  *
186  * @tparam vector_type typically an instantiation of
187  * boost::multi_array<> or something similar.
188  */
189 template <typename vector_type>
190 std::function<void(config const&)> test_case() {
191  return [](config const& cfg) {
193  benchmark bm(cfg.microbenchmark());
194  auto r = bm.run(cfg.n_timeseries());
195  bm.typical_output(r);
196  };
197 }
198 
199 // Return the group of testcases
202  {"float:aligned:many",
203  test_case<jb::fftw::aligned_multi_array<float, 2>>()},
204  {"float:aligned:single", test_case<jb::fftw::aligned_vector<float>>()},
205  {"double:aligned:many",
206  test_case<jb::fftw::aligned_multi_array<double, 2>>()},
207  {"double:aligned:single", test_case<jb::fftw::aligned_vector<double>>()},
208  {"float:unaligned:many", test_case<boost::multi_array<float, 2>>()},
209  {"float:unaligned:single", test_case<std::vector<float>>()},
210  {"double:unaligned:many", test_case<boost::multi_array<double, 2>>()},
211  {"double:unaligned:single", test_case<std::vector<double>>()},
212  {"complex:float:aligned:many",
213  test_case<jb::fftw::aligned_multi_array<std::complex<float>, 2>>()},
214  {"complex:float:aligned:single",
215  test_case<jb::fftw::aligned_vector<std::complex<float>>>()},
216  {"complex:double:aligned:many",
217  test_case<jb::fftw::aligned_multi_array<std::complex<double>, 2>>()},
218  {"complex:double:aligned:single",
219  test_case<jb::fftw::aligned_vector<std::complex<double>>>()},
220  {"complex:float:unaligned:many",
221  test_case<boost::multi_array<std::complex<float>, 2>>()},
222  {"complex:float:unaligned:single",
223  test_case<std::vector<std::complex<float>>>()},
224  {"complex:double:unaligned:many",
225  test_case<boost::multi_array<std::complex<double>, 2>>()},
226  {"complex:double:unaligned:single",
227  test_case<std::vector<std::complex<double>>>()},
228  };
229 }
230 
231 namespace defaults {
232 
233 #ifndef JB_FFTW_DEFAULT_bm_time_delay_estimator_many_test_case
234 #define JB_FFTW_DEFAULT_bm_time_delay_estimator_many_test_case \
235  "float:aligned:many"
236 #endif // JB_FFTW_DEFAULT_bm_time_delay_estimator_many_test_case
237 
238 #ifndef JB_FFTW_DEFAULT_bm_time_delay_estimator_many_n_timeseries
239 #define JB_FFTW_DEFAULT_bm_time_delay_estimator_many_n_timeseries 1
240 #endif // JB_FFTW_DEFAULT_bm_time_delay_estimator_many_n_timeseries
241 
242 std::string const test_case =
244 int constexpr n_timeseries =
246 
247 } // namespace defaults
248 
249 config::config()
250  : log(desc("log", "logging"), this)
251  , microbenchmark(
252  desc("microbenchmark", "microbenchmark"), this,
253  jb::testing::microbenchmark_config().test_case(defaults::test_case))
254  , n_timeseries(
255  desc("ntimeseries")
256  .help(
257  "Number of timeseries as argument to compute TDE. "
258  "If microbenchmark.test_case is *:single, the fixture "
259  "executes this many calls "
260  " to compute TDE passing a container with one time series as "
261  "argument every time. "
262  "If it is *:many, the fixture uses a 2-dimension array "
263  "containing this "
264  "many time series as argument to a one time compute TDE."),
265  this, defaults::n_timeseries) {
266 }
267 
268 void config::validate() const {
269  log().validate();
270  microbenchmark().validate();
271  if (n_timeseries() < 1) {
272  std::ostringstream os;
273  os << "n_timeseries must be > 0 (" << n_timeseries() << ")";
274  throw jb::usage(os.str(), 1);
275  }
276 }
277 
278 } // anonymous namespace
Define defaults for program parameters.
timeseries_t delay_timeseries_periodic(timeseries_t const &ts, duration_t delay, duration_t sampling_period)
Delay a timeseries using a periodic extension for early values.
Base class for all configuration objects.
virtual void validate() const
Validate the settings.
auto sum_square(container_t const &ts)
Compute the sum square of a family of timeseries.
Definition: sum_square.hpp:17
results run(Args &&... args)
Run the microbenchmaark.
std::map< std::string, std::function< void(config const &cfg)> > microbenchmark_group
Define a representation for a group of microbenchmarks.
void create_triangle_timeseries(int nsamples, timeseries &ts)
Create a simple timeseries where the values look like a triangle.
A time delay estimator based on cross-correlation.
#define config_object_constructors(NAME)
Helper class to easily define configuration attributes.
A simple class to communicate the result of parsing the options.
Definition: usage.hpp:11
#define JB_FFTW_DEFAULT_bm_time_delay_estimator_many_test_case
std::size_t nsamples(container_type const &a)
Count the elements in the last dimension of a vector-like container.
#define JB_FFTW_DEFAULT_bm_time_delay_estimator_many_n_timeseries
Run a micro-benchmark on a given class.
int main(int argc, char *argv[])
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7