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