JayBeams  0.1
Another project to have fun coding.
bm_time_delay_estimator.cpp
Go to the documentation of this file.
1 /**
2  * A microbenchmark for different instantiations of jb::time_delay_estimator.
3  */
9 
10 #include <chrono>
11 #include <iostream>
12 #include <stdexcept>
13 #include <string>
14 
15 /// Helper types and functions to benchmark jb::fftw::time_delay_estimator.
16 namespace {
17 /// Configuration parameters for bm_time_delay_estimator
18 class config : public jb::config_object {
19 public:
20  config();
22 
23  void validate() const override;
24 
27  microbenchmark;
28 };
29 
30 /// Create all the test cases.
32 } // anonymous namespace
33 
34 int main(int argc, char* argv[]) {
35  // Simply call the generic microbenchmark for a group of testcases ...
36  return jb::testing::microbenchmark_group_main<config>(
37  argc, argv, create_testcases());
38 }
39 
40 namespace {
41 // These magic numbers are motivated by observed delays between two market
42 // feeds. They assume that delay is normally around 1,250 microseconds, but
43 // can be as large as 6,000 microseconds. To reliably detect the 6,000 usecs
44 // delays we need samples that cover at least 18,000 microseconds, assuming
45 // a sampling rate of 10 microseconds that is 1,800 samples, FFTs work well
46 // with sample sizes that are powers of 2, so we use 2048 samples.
47 std::chrono::microseconds const expected_delay(1250);
48 std::chrono::microseconds const sampling_period(10);
49 int const nsamples = 2048;
50 
51 /**
52  * The fixture for this microbenchmark.
53  *
54  * Run the microbenchmark with an specific timeseries type (vector
55  * vs. aligned_vector, float vs. complex vs. double).
56  *
57  * @tparam timeseries_type the type of timeseries, typically an
58  * instance of std::vector<> or jb::fftw::aligned_vector<>.
59  */
60 template <typename timeseries_type>
61 class fixture {
62 public:
63  /// Define the tested estimator type.
65 
66  /// Constructor with default size
67  fixture()
68  : fixture(nsamples) {
69  }
70 
71  /// Constructor with given size
72  explicit fixture(int size)
73  : a(size)
74  , b(size)
75  , estimator(a, b) {
76  // Initialize the values in the timeseries. The estimator is
77  // already initialized and ready to run ...
80  a, expected_delay, sampling_period);
81  }
82 
83  /// Run a single iteration of the estimation
84  int run() {
85  // ... perform the estimation ...
86  auto e = estimator.estimate_delay(a, b);
87  // ... we could ignore the value, but just in case ...
88  if (not e.first) {
89  throw std::runtime_error("estimation failed");
90  }
91  return static_cast<int>(a.size());
92  }
93 
94 private:
95  timeseries_type a;
96  timeseries_type b;
97  tested estimator;
98 };
99 
100 /**
101  * Create a test case for the given timeseries type.
102  *
103  * Returns a (type erased) functor to run the microbenchmark for the
104  * give timeseries type.
105  *
106  * @tparam timeseries_type typically an instantiation of std::vector<>
107  * or jb::fftw::aligned_vector<>.
108  */
109 template <typename timeseries_type>
110 std::function<void(config const&)> test_case() {
111  return [](config const& cfg) {
112  // Create a microbenchmark with the right fixture, initialize from
113  // the configuration parameter ...
115  benchmark bm(cfg.microbenchmark());
116 
117  // ... run the microbenchmark ...
118  auto r = bm.run();
119  // ... output in the most commonly used format for JayBeams
120  // microbenchmarks ...
121  bm.typical_output(r);
122  };
123 }
124 
125 // Return the group of testcases
128  {"float:aligned", test_case<jb::fftw::aligned_vector<float>>()},
129  {"double:aligned", test_case<jb::fftw::aligned_vector<double>>()},
130  {"float:unaligned", test_case<std::vector<float>>()},
131  {"double:unaligned", test_case<std::vector<double>>()},
132  {"complex:float:aligned",
133  test_case<jb::fftw::aligned_vector<std::complex<float>>>()},
134  {"complex:double:aligned",
135  test_case<jb::fftw::aligned_vector<std::complex<double>>>()},
136  {"complex:float:unaligned",
137  test_case<std::vector<std::complex<float>>>()},
138  {"complex:double:unaligned",
139  test_case<std::vector<std::complex<double>>>()},
140  };
141 }
142 
143 namespace defaults {
144 #ifndef JB_FFTW_DEFAULT_fftw_bm_time_delay_estimator_test_case
145 #define JB_FFTW_DEFAULT_fftw_bm_time_delay_estimator_test_case "float:aligned"
146 #endif // JB_FFTW_DEFAULT_fftw_bm_time_delay_estimator_test_case
147 
148 std::string const test_case =
150 } // namespace defaults
151 
152 config::config()
153  : log(desc("log", "logging"), this)
154  , microbenchmark(
155  desc("microbenchmark", "microbenchmark"), this,
156  jb::testing::microbenchmark_config().test_case(defaults::test_case)) {
157 }
158 
159 void config::validate() const {
160  log().validate();
161  microbenchmark().validate();
162 }
163 
164 } // anonymous namespace
int main(int argc, char *argv[])
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.
A simple time delay estimator based on cross-correlation.
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_square_timeseries(int nsamples, timeseries &ts)
Create a simple timeseries where the values look like a square.
#define config_object_constructors(NAME)
Helper class to easily define configuration attributes.
#define JB_FFTW_DEFAULT_fftw_bm_time_delay_estimator_test_case
std::size_t nsamples(container_type const &a)
Count the elements in the last dimension of a vector-like container.
Run a micro-benchmark on a given class.
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7