Visual Servoing Platform version 3.5.0
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
45namespace
46{
47static bool g_runBenchmark = false;
48static const std::vector<unsigned int> g_sizes = {23, 127, 233, 419, 1153, 2749};
49
50double getRandomValues(double min, double max)
51{
52 return (max - min) * ((double)rand() / (double)RAND_MAX) + min;
53}
54
55vpColVector 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
66double 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
77double 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
88double 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
99double 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
113std::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
121bool 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
137TEST_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
189TEST_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
241TEST_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
295TEST_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
347int 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
379int main()
380{
381 return 0;
382}
383#endif
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:291
unsigned int getRows() const
Definition: vpArray2D.h:289
Implementation of column vector and the associated operations.
Definition: vpColVector.h:131
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 s=0.001)
Definition: vpMath.h:295