Visual Servoing Platform  version 3.4.1 under development (2021-10-17)
perfColVectorOperations.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Benchmark column vector operations.
33  *
34  *****************************************************************************/
35 
36 #include <visp3/core/vpConfig.h>
37 
38 #ifdef VISP_HAVE_CATCH2
39 #define CATCH_CONFIG_ENABLE_BENCHMARKING
40 #define CATCH_CONFIG_RUNNER
41 #include <catch.hpp>
42 
43 #include <visp3/core/vpColVector.h>
44 
45 namespace
46 {
47 static bool g_runBenchmark = false;
48 static const std::vector<unsigned int> g_sizes = {23, 127, 233, 419, 1153, 2749};
49 
50 double getRandomValues(double min, double max)
51 {
52  return (max - min) * ((double)rand() / (double)RAND_MAX) + min;
53 }
54 
55 vpColVector generateRandomVector(unsigned int rows, double min=-100, double max=100)
56 {
57  vpColVector v(rows);
58 
59  for (unsigned int i = 0; i < v.getRows(); i++) {
60  v[i] = getRandomValues(min, max);
61  }
62 
63  return v;
64 }
65 
66 double stddev(const std::vector<double>& vec)
67 {
68  double sum = std::accumulate(vec.begin(), vec.end(), 0.0);
69  double mean = sum / vec.size();
70 
71  std::vector<double> diff(vec.size());
72  std::transform(vec.begin(), vec.end(), diff.begin(), [mean](double x) { return x - mean; });
73  double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
74  return std::sqrt(sq_sum / vec.size());
75 }
76 
77 double computeRegularSum(const vpColVector &v)
78 {
79  double sum = 0.0;
80 
81  for (unsigned int i = 0; i < v.getRows(); i++) {
82  sum += v[i];
83  }
84 
85  return sum;
86 }
87 
88 double computeRegularSumSquare(const vpColVector &v)
89 {
90  double sum_square = 0.0;
91 
92  for (unsigned int i = 0; i < v.getRows(); i++) {
93  sum_square += v[i] * v[i];
94  }
95 
96  return sum_square;
97 }
98 
99 double computeRegularStdev(const vpColVector &v)
100 {
101  double mean_value = computeRegularSum(v) / v.getRows();
102  double sum_squared_diff = 0.0;
103 
104  for (unsigned int i = 0; i < v.size(); i++) {
105  sum_squared_diff += (v[i] - mean_value) * (v[i] - mean_value);
106  }
107 
108  double divisor = (double)v.size();
109 
110  return std::sqrt(sum_squared_diff / divisor);
111 }
112 
113 std::vector<double> computeHadamard(const std::vector<double>& v1, const std::vector<double>& v2)
114 {
115  std::vector<double> result;
116  std::transform(v1.begin(), v1.end(), v2.begin(),
117  std::back_inserter(result), std::multiplies<double>());
118  return result;
119 }
120 
121 bool almostEqual(const vpColVector& v1, const vpColVector& v2, double tol=1e-9)
122 {
123  if (v1.getRows() != v2.getRows()) {
124  return false;
125  }
126 
127  for (unsigned int i = 0; i < v1.getRows(); i++) {
128  if (!vpMath::equal(v1[i], v2[i], tol)) {
129  return false;
130  }
131  }
132 
133  return true;
134 }
135 } // namespace
136 
137 TEST_CASE("Benchmark vpColVector::sum()", "[benchmark]") {
138  // Sanity checks
139  {
140  const double val = 11;
141  vpColVector v(1, val);
142  CHECK(v.sum() == Approx(val).epsilon(std::numeric_limits<double>::epsilon()));
143  }
144  {
145  const unsigned int size = 11;
146  std::vector<double> vec(size);
147  vpColVector v(size);
148  for (size_t i = 0; i < 11; i++) {
149  vec[i] = 2.*i;
150  v[static_cast<unsigned int>(i)] = vec[i];
151  }
152  CHECK(v.sum() == Approx(std::accumulate(vec.begin(), vec.end(), 0.0)).epsilon(std::numeric_limits<double>::epsilon()));
153  }
154 
155  if (g_runBenchmark) {
156  for (auto size : g_sizes) {
157  vpColVector v = generateRandomVector(size);
158  std::vector<double> vec = v.toStdVector();
159 
160  std::ostringstream oss;
161  oss << "Benchmark vpColVector::sum() with size: " << size << " (ViSP)";
162  double vp_sum = 0;
163  BENCHMARK(oss.str().c_str()) {
164  vp_sum = v.sum();
165  return vp_sum;
166  };
167 
168  oss.str("");
169  oss << "Benchmark std::accumulate() with size: " << size << " (C++)";
170  double std_sum = 0;
171  BENCHMARK(oss.str().c_str()) {
172  std_sum = std::accumulate(vec.begin(), vec.end(), 0.0);
173  return std_sum;
174  };
175  CHECK(vp_sum == Approx(std_sum));
176 
177  oss.str("");
178  oss << "Benchmark naive sum() with size: " << size;
179  double naive_sum = 0;
180  BENCHMARK(oss.str().c_str()) {
181  naive_sum = computeRegularSum(v);
182  return naive_sum;
183  };
184  CHECK(naive_sum == Approx(std_sum));
185  }
186  }
187 }
188 
189 TEST_CASE("Benchmark vpColVector::sumSquare()", "[benchmark]") {
190  // Sanity checks
191  {
192  const double val = 11;
193  vpColVector v(1, val);
194  CHECK(v.sumSquare() == Approx(val*val).epsilon(std::numeric_limits<double>::epsilon()));
195  }
196  {
197  const unsigned int size = 11;
198  std::vector<double> vec(size);
199  vpColVector v(size);
200  for (size_t i = 0; i < 11; i++) {
201  vec[i] = 2.*i;
202  v[static_cast<unsigned int>(i)] = vec[i];
203  }
204  CHECK(v.sumSquare() == Approx(std::inner_product(vec.begin(), vec.end(), vec.begin(), 0.0)).epsilon(std::numeric_limits<double>::epsilon()));
205  }
206 
207  if (g_runBenchmark) {
208  for (auto size : g_sizes) {
209  vpColVector v = generateRandomVector(size);
210  std::vector<double> vec = v.toStdVector();
211 
212  std::ostringstream oss;
213  oss << "Benchmark vpColVector::sumSquare() with size: " << size << " (ViSP)";
214  double vp_sq_sum = 0;
215  BENCHMARK(oss.str().c_str()) {
216  vp_sq_sum = v.sumSquare();
217  return vp_sq_sum;
218  };
219 
220  oss.str("");
221  oss << "Benchmark std::inner_product with size: " << size << " (C++)";
222  double std_sq_sum = 0;
223  BENCHMARK(oss.str().c_str()) {
224  std_sq_sum = std::inner_product(vec.begin(), vec.end(), vec.begin(), 0.0);
225  return std_sq_sum;
226  };
227  CHECK(vp_sq_sum == Approx(std_sq_sum));
228 
229  oss.str("");
230  oss << "Benchmark naive sumSquare() with size: " << size;
231  double naive_sq_sum = 0;
232  BENCHMARK(oss.str().c_str()) {
233  naive_sq_sum = computeRegularSumSquare(v);
234  return naive_sq_sum;
235  };
236  CHECK(naive_sq_sum == Approx(std_sq_sum));
237  }
238  }
239 }
240 
241 TEST_CASE("Benchmark vpColVector::stdev()", "[benchmark]") {
242  // Sanity checks
243  {
244  vpColVector v(2);
245  v[0] = 11;
246  v[1] = 16;
247  std::vector<double> vec = v.toStdVector();
248  CHECK(vpColVector::stdev(v) == Approx(stddev(vec)).epsilon(std::numeric_limits<double>::epsilon()));
249  }
250  {
251  const unsigned int size = 11;
252  std::vector<double> vec(size);
253  vpColVector v(size);
254  for (size_t i = 0; i < 11; i++) {
255  vec[i] = 2.*i;
256  v[static_cast<unsigned int>(i)] = vec[i];
257  }
258  CHECK(vpColVector::stdev(v) == Approx(stddev(vec)).epsilon(std::numeric_limits<double>::epsilon()));
259  }
260 
261  if (g_runBenchmark) {
262  for (auto size : g_sizes) {
263  vpColVector v = generateRandomVector(size);
264  std::vector<double> vec = v.toStdVector();
265 
266  std::ostringstream oss;
267  oss << "Benchmark vpColVector::stdev() with size: " << size << " (ViSP)";
268  double vp_stddev = 0;
269  BENCHMARK(oss.str().c_str()) {
270  vp_stddev = vpColVector::stdev(v);
271  return vp_stddev;
272  };
273 
274  oss.str("");
275  oss << "Benchmark C++ stddev impl with size: " << size << " (C++)";
276  double std_stddev = 0;
277  BENCHMARK(oss.str().c_str()) {
278  std_stddev = stddev(vec);
279  return std_stddev;
280  };
281  CHECK(vp_stddev == Approx(std_stddev));
282 
283  oss.str("");
284  oss << "Benchmark naive stdev() with size: " << size;
285  double naive_stddev = 0;
286  BENCHMARK(oss.str().c_str()) {
287  naive_stddev = computeRegularStdev(v);
288  return naive_stddev;
289  };
290  CHECK(naive_stddev == Approx(std_stddev));
291  }
292  }
293 }
294 
295 TEST_CASE("Benchmark vpColVector::hadamard()", "[benchmark]") {
296  // Sanity checks
297  {
298  vpColVector v1(2), v2(2);
299  v1[0] = 11;
300  v1[1] = 16;
301  v2[0] = 8;
302  v2[1] = 23;
303  vpColVector res1 = v1.hadamard(v2);
304  vpColVector res2(computeHadamard(v1.toStdVector(), v2.toStdVector()));
305  CHECK(almostEqual(res1, res2));
306  }
307  {
308  const unsigned int size = 11;
309  std::vector<double> vec1(size), vec2(size);
310  for (size_t i = 0; i < 11; i++) {
311  vec1[i] = 2.*i;
312  vec2[i] = 3.*i + 5.;
313  }
314  vpColVector v1(vec1), v2(vec2);
315  vpColVector res1 = v1.hadamard(v2);
316  vpColVector res2(computeHadamard(v1.toStdVector(), v2.toStdVector()));
317  CHECK(almostEqual(res1, res2));
318  }
319 
320  if (g_runBenchmark) {
321  for (auto size : g_sizes) {
322  vpColVector v1 = generateRandomVector(size);
323  vpColVector v2 = generateRandomVector(size);
324  std::vector<double> vec1 = v1.toStdVector();
325  std::vector<double> vec2 = v2.toStdVector();
326 
327  std::ostringstream oss;
328  oss << "Benchmark vpColVector::hadamard() with size: " << size << " (ViSP)";
329  vpColVector vp_res;
330  BENCHMARK(oss.str().c_str()) {
331  vp_res = v1.hadamard(v2);
332  return vp_res;
333  };
334 
335  oss.str("");
336  oss << "Benchmark C++ element wise multiplication with size: " << size << " (C++)";
337  std::vector<double> std_res;
338  BENCHMARK(oss.str().c_str()) {
339  std_res = computeHadamard(vec1, vec2);
340  return std_res;
341  };
342  CHECK(almostEqual(vp_res, vpColVector(std_res)));
343  }
344  }
345 }
346 
347 int main(int argc, char *argv[])
348 {
349  // Set random seed explicitly to avoid confusion
350  // See: https://en.cppreference.com/w/cpp/numeric/random/srand
351  // If rand() is used before any calls to srand(), rand() behaves as if it was seeded with srand(1).
352  srand(1);
353 
354  Catch::Session session; // There must be exactly one instance
355 
356  // Build a new parser on top of Catch's
357  using namespace Catch::clara;
358  auto cli = session.cli() // Get Catch's composite command line parser
359  | Opt(g_runBenchmark) // bind variable to a new option, with a hint string
360  ["--benchmark"] // the option names it will respond to
361  ("run benchmark?"); // description string for the help output
362 
363  // Now pass the new composite back to Catch so it uses that
364  session.cli(cli);
365 
366  // Let Catch (using Clara) parse the command line
367  session.applyCommandLine(argc, argv);
368 
369  int numFailed = session.run();
370 
371  // numFailed is clamped to 255 as some unices only use the lower 8 bits.
372  // This clamping has already been applied, so just return it here
373  // You can also do any post run clean-up here
374  return numFailed;
375 }
376 #else
377 #include <iostream>
378 
379 int main()
380 {
381  return 0;
382 }
383 #endif
static bool equal(double x, double y, double s=0.001)
Definition: vpMath.h:293
unsigned int getRows() const
Definition: vpArray2D.h:289
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:291
double sum() const
vpColVector hadamard(const vpColVector &v) const
std::vector< double > toStdVector()
Implementation of column vector and the associated operations.
Definition: vpColVector.h:130
double sumSquare() const
static double stdev(const vpColVector &v, bool useBesselCorrection=false)