Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/ts/src/ts_perf.cpp
16337 views
1
#include "precomp.hpp"
2
3
#include <map>
4
#include <iostream>
5
#include <fstream>
6
7
#if defined _WIN32
8
#ifndef NOMINMAX
9
#define NOMINMAX
10
#endif
11
#include <windows.h>
12
#endif
13
14
#ifdef HAVE_CUDA
15
#include "opencv2/core/cuda.hpp"
16
#endif
17
18
#ifdef __ANDROID__
19
# include <sys/time.h>
20
#endif
21
22
using namespace cvtest;
23
using namespace perf;
24
25
int64 TestBase::timeLimitDefault = 0;
26
unsigned int TestBase::iterationsLimitDefault = (unsigned int)(-1);
27
int64 TestBase::_timeadjustment = 0;
28
29
// Item [0] will be considered the default implementation.
30
static std::vector<std::string> available_impls;
31
32
static std::string param_impl;
33
34
static enum PERF_STRATEGY strategyForce = PERF_STRATEGY_DEFAULT;
35
static enum PERF_STRATEGY strategyModule = PERF_STRATEGY_SIMPLE;
36
37
static double param_max_outliers;
38
static double param_max_deviation;
39
static unsigned int param_min_samples;
40
static unsigned int param_force_samples;
41
static double param_time_limit;
42
static bool param_write_sanity;
43
static bool param_verify_sanity;
44
#ifdef CV_COLLECT_IMPL_DATA
45
static bool param_collect_impl;
46
#endif
47
#ifdef ENABLE_INSTRUMENTATION
48
static int param_instrument;
49
#endif
50
51
namespace cvtest {
52
extern bool test_ipp_check;
53
}
54
55
#ifdef HAVE_CUDA
56
static int param_cuda_device;
57
#endif
58
59
#ifdef __ANDROID__
60
static int param_affinity_mask;
61
static bool log_power_checkpoints;
62
63
#include <sys/syscall.h>
64
#include <pthread.h>
65
#include <cerrno>
66
static void setCurrentThreadAffinityMask(int mask)
67
{
68
pid_t pid=gettid();
69
int syscallres=syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
70
if (syscallres)
71
{
72
int err=errno;
73
CV_UNUSED(err);
74
LOGE("Error in the syscall setaffinity: mask=%d=0x%x err=%d=0x%x", mask, mask, err, err);
75
}
76
}
77
#endif
78
79
static double perf_stability_criteria = 0.03; // 3%
80
81
namespace {
82
83
class PerfEnvironment: public ::testing::Environment
84
{
85
public:
86
void TearDown()
87
{
88
cv::setNumThreads(-1);
89
}
90
};
91
92
} // namespace
93
94
static void randu(cv::Mat& m)
95
{
96
const int bigValue = 0x00000FFF;
97
if (m.depth() < CV_32F)
98
{
99
int minmax[] = {0, 256};
100
cv::Mat mr = cv::Mat(m.rows, (int)(m.cols * m.elemSize()), CV_8U, m.ptr(), m.step[0]);
101
cv::randu(mr, cv::Mat(1, 1, CV_32S, minmax), cv::Mat(1, 1, CV_32S, minmax + 1));
102
}
103
else if (m.depth() == CV_32F)
104
{
105
//float minmax[] = {-FLT_MAX, FLT_MAX};
106
float minmax[] = {-bigValue, bigValue};
107
cv::Mat mr = m.reshape(1);
108
cv::randu(mr, cv::Mat(1, 1, CV_32F, minmax), cv::Mat(1, 1, CV_32F, minmax + 1));
109
}
110
else
111
{
112
//double minmax[] = {-DBL_MAX, DBL_MAX};
113
double minmax[] = {-bigValue, bigValue};
114
cv::Mat mr = m.reshape(1);
115
cv::randu(mr, cv::Mat(1, 1, CV_64F, minmax), cv::Mat(1, 1, CV_64F, minmax + 1));
116
}
117
}
118
119
/*****************************************************************************************\
120
* inner exception class for early termination
121
\*****************************************************************************************/
122
123
class PerfEarlyExitException: public cv::Exception {};
124
125
/*****************************************************************************************\
126
* ::perf::Regression
127
\*****************************************************************************************/
128
129
Regression& Regression::instance()
130
{
131
static Regression single;
132
return single;
133
}
134
135
Regression& Regression::add(TestBase* test, const std::string& name, cv::InputArray array, double eps, ERROR_TYPE err)
136
{
137
if(test) test->setVerified();
138
return instance()(name, array, eps, err);
139
}
140
141
Regression& Regression::addMoments(TestBase* test, const std::string& name, const cv::Moments& array, double eps, ERROR_TYPE err)
142
{
143
int len = (int)sizeof(cv::Moments) / sizeof(double);
144
cv::Mat m(1, len, CV_64F, (void*)&array);
145
146
return Regression::add(test, name, m, eps, err);
147
}
148
149
Regression& Regression::addKeypoints(TestBase* test, const std::string& name, const std::vector<cv::KeyPoint>& array, double eps, ERROR_TYPE err)
150
{
151
int len = (int)array.size();
152
cv::Mat pt (len, 1, CV_32FC2, len ? (void*)&array[0].pt : 0, sizeof(cv::KeyPoint));
153
cv::Mat size (len, 1, CV_32FC1, len ? (void*)&array[0].size : 0, sizeof(cv::KeyPoint));
154
cv::Mat angle (len, 1, CV_32FC1, len ? (void*)&array[0].angle : 0, sizeof(cv::KeyPoint));
155
cv::Mat response(len, 1, CV_32FC1, len ? (void*)&array[0].response : 0, sizeof(cv::KeyPoint));
156
cv::Mat octave (len, 1, CV_32SC1, len ? (void*)&array[0].octave : 0, sizeof(cv::KeyPoint));
157
cv::Mat class_id(len, 1, CV_32SC1, len ? (void*)&array[0].class_id : 0, sizeof(cv::KeyPoint));
158
159
return Regression::add(test, name + "-pt", pt, eps, ERROR_ABSOLUTE)
160
(name + "-size", size, eps, ERROR_ABSOLUTE)
161
(name + "-angle", angle, eps, ERROR_ABSOLUTE)
162
(name + "-response", response, eps, err)
163
(name + "-octave", octave, eps, ERROR_ABSOLUTE)
164
(name + "-class_id", class_id, eps, ERROR_ABSOLUTE);
165
}
166
167
Regression& Regression::addMatches(TestBase* test, const std::string& name, const std::vector<cv::DMatch>& array, double eps, ERROR_TYPE err)
168
{
169
int len = (int)array.size();
170
cv::Mat queryIdx(len, 1, CV_32SC1, len ? (void*)&array[0].queryIdx : 0, sizeof(cv::DMatch));
171
cv::Mat trainIdx(len, 1, CV_32SC1, len ? (void*)&array[0].trainIdx : 0, sizeof(cv::DMatch));
172
cv::Mat imgIdx (len, 1, CV_32SC1, len ? (void*)&array[0].imgIdx : 0, sizeof(cv::DMatch));
173
cv::Mat distance(len, 1, CV_32FC1, len ? (void*)&array[0].distance : 0, sizeof(cv::DMatch));
174
175
return Regression::add(test, name + "-queryIdx", queryIdx, DBL_EPSILON, ERROR_ABSOLUTE)
176
(name + "-trainIdx", trainIdx, DBL_EPSILON, ERROR_ABSOLUTE)
177
(name + "-imgIdx", imgIdx, DBL_EPSILON, ERROR_ABSOLUTE)
178
(name + "-distance", distance, eps, err);
179
}
180
181
void Regression::Init(const std::string& testSuitName, const std::string& ext)
182
{
183
instance().init(testSuitName, ext);
184
}
185
186
void Regression::init(const std::string& testSuitName, const std::string& ext)
187
{
188
if (!storageInPath.empty())
189
{
190
LOGE("Subsequent initialization of Regression utility is not allowed.");
191
return;
192
}
193
194
#ifndef WINRT
195
const char *data_path_dir = getenv("OPENCV_TEST_DATA_PATH");
196
#else
197
const char *data_path_dir = OPENCV_TEST_DATA_PATH;
198
#endif
199
200
cvtest::addDataSearchSubDirectory("");
201
cvtest::addDataSearchSubDirectory(testSuitName);
202
203
const char *path_separator = "/";
204
205
if (data_path_dir)
206
{
207
int len = (int)strlen(data_path_dir)-1;
208
if (len < 0) len = 0;
209
std::string path_base = (data_path_dir[0] == 0 ? std::string(".") : std::string(data_path_dir))
210
+ (data_path_dir[len] == '/' || data_path_dir[len] == '\\' ? "" : path_separator)
211
+ "perf"
212
+ path_separator;
213
214
storageInPath = path_base + testSuitName + ext;
215
storageOutPath = path_base + testSuitName;
216
}
217
else
218
{
219
storageInPath = testSuitName + ext;
220
storageOutPath = testSuitName;
221
}
222
223
suiteName = testSuitName;
224
225
try
226
{
227
if (storageIn.open(storageInPath, cv::FileStorage::READ))
228
{
229
rootIn = storageIn.root();
230
if (storageInPath.length() > 3 && storageInPath.substr(storageInPath.length()-3) == ".gz")
231
storageOutPath += "_new";
232
storageOutPath += ext;
233
}
234
}
235
catch(cv::Exception&)
236
{
237
LOGE("Failed to open sanity data for reading: %s", storageInPath.c_str());
238
}
239
240
if(!storageIn.isOpened())
241
storageOutPath = storageInPath;
242
}
243
244
Regression::Regression() : regRNG(cv::getTickCount())//this rng should be really random
245
{
246
}
247
248
Regression::~Regression()
249
{
250
if (storageIn.isOpened())
251
storageIn.release();
252
if (storageOut.isOpened())
253
{
254
if (!currentTestNodeName.empty())
255
storageOut << "}";
256
storageOut.release();
257
}
258
}
259
260
cv::FileStorage& Regression::write()
261
{
262
if (!storageOut.isOpened() && !storageOutPath.empty())
263
{
264
int mode = (storageIn.isOpened() && storageInPath == storageOutPath)
265
? cv::FileStorage::APPEND : cv::FileStorage::WRITE;
266
storageOut.open(storageOutPath, mode);
267
if (!storageOut.isOpened())
268
{
269
LOGE("Could not open \"%s\" file for writing", storageOutPath.c_str());
270
storageOutPath.clear();
271
}
272
else if (mode == cv::FileStorage::WRITE && !rootIn.empty())
273
{
274
//TODO: write content of rootIn node into the storageOut
275
}
276
}
277
return storageOut;
278
}
279
280
std::string Regression::getCurrentTestNodeName()
281
{
282
const ::testing::TestInfo* const test_info =
283
::testing::UnitTest::GetInstance()->current_test_info();
284
285
if (test_info == 0)
286
return "undefined";
287
288
std::string nodename = std::string(test_info->test_case_name()) + "--" + test_info->name();
289
size_t idx = nodename.find_first_of('/');
290
if (idx != std::string::npos)
291
nodename.erase(idx);
292
293
const char* type_param = test_info->type_param();
294
if (type_param != 0)
295
(nodename += "--") += type_param;
296
297
const char* value_param = test_info->value_param();
298
if (value_param != 0)
299
(nodename += "--") += value_param;
300
301
for(size_t i = 0; i < nodename.length(); ++i)
302
if (!isalnum(nodename[i]) && '_' != nodename[i])
303
nodename[i] = '-';
304
305
return nodename;
306
}
307
308
bool Regression::isVector(cv::InputArray a)
309
{
310
return a.kind() == cv::_InputArray::STD_VECTOR_MAT || a.kind() == cv::_InputArray::STD_VECTOR_VECTOR ||
311
a.kind() == cv::_InputArray::STD_VECTOR_UMAT;
312
}
313
314
double Regression::getElem(cv::Mat& m, int y, int x, int cn)
315
{
316
switch (m.depth())
317
{
318
case CV_8U: return *(m.ptr<unsigned char>(y, x) + cn);
319
case CV_8S: return *(m.ptr<signed char>(y, x) + cn);
320
case CV_16U: return *(m.ptr<unsigned short>(y, x) + cn);
321
case CV_16S: return *(m.ptr<signed short>(y, x) + cn);
322
case CV_32S: return *(m.ptr<signed int>(y, x) + cn);
323
case CV_32F: return *(m.ptr<float>(y, x) + cn);
324
case CV_64F: return *(m.ptr<double>(y, x) + cn);
325
default: return 0;
326
}
327
}
328
329
void Regression::write(cv::Mat m)
330
{
331
if (!m.empty() && m.dims < 2) return;
332
333
double min, max;
334
cv::minMaxIdx(m, &min, &max);
335
write() << "min" << min << "max" << max;
336
337
write() << "last" << "{" << "x" << m.size.p[1] - 1 << "y" << m.size.p[0] - 1
338
<< "val" << getElem(m, m.size.p[0] - 1, m.size.p[1] - 1, m.channels() - 1) << "}";
339
340
int x, y, cn;
341
x = regRNG.uniform(0, m.size.p[1]);
342
y = regRNG.uniform(0, m.size.p[0]);
343
cn = regRNG.uniform(0, m.channels());
344
write() << "rng1" << "{" << "x" << x << "y" << y;
345
if(cn > 0) write() << "cn" << cn;
346
write() << "val" << getElem(m, y, x, cn) << "}";
347
348
x = regRNG.uniform(0, m.size.p[1]);
349
y = regRNG.uniform(0, m.size.p[0]);
350
cn = regRNG.uniform(0, m.channels());
351
write() << "rng2" << "{" << "x" << x << "y" << y;
352
if (cn > 0) write() << "cn" << cn;
353
write() << "val" << getElem(m, y, x, cn) << "}";
354
}
355
356
void Regression::verify(cv::FileNode node, cv::Mat actual, double eps, std::string argname, ERROR_TYPE err)
357
{
358
if (!actual.empty() && actual.dims < 2) return;
359
360
double expect_min = (double)node["min"];
361
double expect_max = (double)node["max"];
362
363
if (err == ERROR_RELATIVE)
364
eps *= std::max(std::abs(expect_min), std::abs(expect_max));
365
366
double actual_min, actual_max;
367
cv::minMaxIdx(actual, &actual_min, &actual_max);
368
369
ASSERT_NEAR(expect_min, actual_min, eps)
370
<< argname << " has unexpected minimal value" << std::endl;
371
ASSERT_NEAR(expect_max, actual_max, eps)
372
<< argname << " has unexpected maximal value" << std::endl;
373
374
cv::FileNode last = node["last"];
375
double actual_last = getElem(actual, actual.size.p[0] - 1, actual.size.p[1] - 1, actual.channels() - 1);
376
int expect_cols = (int)last["x"] + 1;
377
int expect_rows = (int)last["y"] + 1;
378
ASSERT_EQ(expect_cols, actual.size.p[1])
379
<< argname << " has unexpected number of columns" << std::endl;
380
ASSERT_EQ(expect_rows, actual.size.p[0])
381
<< argname << " has unexpected number of rows" << std::endl;
382
383
double expect_last = (double)last["val"];
384
ASSERT_NEAR(expect_last, actual_last, eps)
385
<< argname << " has unexpected value of the last element" << std::endl;
386
387
cv::FileNode rng1 = node["rng1"];
388
int x1 = rng1["x"];
389
int y1 = rng1["y"];
390
int cn1 = rng1["cn"];
391
392
double expect_rng1 = (double)rng1["val"];
393
// it is safe to use x1 and y1 without checks here because we have already
394
// verified that mat size is the same as recorded
395
double actual_rng1 = getElem(actual, y1, x1, cn1);
396
397
ASSERT_NEAR(expect_rng1, actual_rng1, eps)
398
<< argname << " has unexpected value of the ["<< x1 << ":" << y1 << ":" << cn1 <<"] element" << std::endl;
399
400
cv::FileNode rng2 = node["rng2"];
401
int x2 = rng2["x"];
402
int y2 = rng2["y"];
403
int cn2 = rng2["cn"];
404
405
double expect_rng2 = (double)rng2["val"];
406
double actual_rng2 = getElem(actual, y2, x2, cn2);
407
408
ASSERT_NEAR(expect_rng2, actual_rng2, eps)
409
<< argname << " has unexpected value of the ["<< x2 << ":" << y2 << ":" << cn2 <<"] element" << std::endl;
410
}
411
412
void Regression::write(cv::InputArray array)
413
{
414
write() << "kind" << array.kind();
415
write() << "type" << array.type();
416
if (isVector(array))
417
{
418
int total = (int)array.total();
419
int idx = regRNG.uniform(0, total);
420
write() << "len" << total;
421
write() << "idx" << idx;
422
423
cv::Mat m = array.getMat(idx);
424
425
if (m.total() * m.channels() < 26) //5x5 or smaller
426
write() << "val" << m;
427
else
428
write(m);
429
}
430
else
431
{
432
if (array.total() * array.channels() < 26) //5x5 or smaller
433
write() << "val" << array.getMat();
434
else
435
write(array.getMat());
436
}
437
}
438
439
static int countViolations(const cv::Mat& expected, const cv::Mat& actual, const cv::Mat& diff, double eps, double* max_violation = 0, double* max_allowed = 0)
440
{
441
cv::Mat diff64f;
442
diff.reshape(1).convertTo(diff64f, CV_64F);
443
444
cv::Mat expected_abs = cv::abs(expected.reshape(1));
445
cv::Mat actual_abs = cv::abs(actual.reshape(1));
446
cv::Mat maximum, mask;
447
cv::max(expected_abs, actual_abs, maximum);
448
cv::multiply(maximum, cv::Vec<double, 1>(eps), maximum, CV_64F);
449
cv::compare(diff64f, maximum, mask, cv::CMP_GT);
450
451
int v = cv::countNonZero(mask);
452
453
if (v > 0 && max_violation != 0 && max_allowed != 0)
454
{
455
int loc[10] = {0};
456
cv::minMaxIdx(maximum, 0, max_allowed, 0, loc, mask);
457
*max_violation = diff64f.at<double>(loc[0], loc[1]);
458
}
459
460
return v;
461
}
462
463
void Regression::verify(cv::FileNode node, cv::InputArray array, double eps, ERROR_TYPE err)
464
{
465
int expected_kind = (int)node["kind"];
466
int expected_type = (int)node["type"];
467
ASSERT_EQ(expected_kind, array.kind()) << " Argument \"" << node.name() << "\" has unexpected kind";
468
ASSERT_EQ(expected_type, array.type()) << " Argument \"" << node.name() << "\" has unexpected type";
469
470
cv::FileNode valnode = node["val"];
471
if (isVector(array))
472
{
473
int expected_length = (int)node["len"];
474
ASSERT_EQ(expected_length, (int)array.total()) << " Vector \"" << node.name() << "\" has unexpected length";
475
int idx = node["idx"];
476
477
cv::Mat actual = array.getMat(idx);
478
479
if (valnode.isNone())
480
{
481
ASSERT_LE((size_t)26, actual.total() * (size_t)actual.channels())
482
<< " \"" << node.name() << "[" << idx << "]\" has unexpected number of elements";
483
verify(node, actual, eps, cv::format("%s[%d]", node.name().c_str(), idx), err);
484
}
485
else
486
{
487
cv::Mat expected;
488
valnode >> expected;
489
490
if(expected.empty())
491
{
492
ASSERT_TRUE(actual.empty())
493
<< " expected empty " << node.name() << "[" << idx<< "]";
494
}
495
else
496
{
497
ASSERT_EQ(expected.size(), actual.size())
498
<< " " << node.name() << "[" << idx<< "] has unexpected size";
499
500
cv::Mat diff;
501
cv::absdiff(expected, actual, diff);
502
503
if (err == ERROR_ABSOLUTE)
504
{
505
if (!cv::checkRange(diff, true, 0, 0, eps))
506
{
507
if(expected.total() * expected.channels() < 12)
508
std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
509
510
double max;
511
cv::minMaxIdx(diff.reshape(1), 0, &max);
512
513
FAIL() << " Absolute difference (=" << max << ") between argument \""
514
<< node.name() << "[" << idx << "]\" and expected value is greater than " << eps;
515
}
516
}
517
else if (err == ERROR_RELATIVE)
518
{
519
double maxv, maxa;
520
int violations = countViolations(expected, actual, diff, eps, &maxv, &maxa);
521
if (violations > 0)
522
{
523
if(expected.total() * expected.channels() < 12)
524
std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
525
526
FAIL() << " Relative difference (" << maxv << " of " << maxa << " allowed) between argument \""
527
<< node.name() << "[" << idx << "]\" and expected value is greater than " << eps << " in " << violations << " points";
528
}
529
}
530
}
531
}
532
}
533
else
534
{
535
if (valnode.isNone())
536
{
537
ASSERT_LE((size_t)26, array.total() * (size_t)array.channels())
538
<< " Argument \"" << node.name() << "\" has unexpected number of elements";
539
verify(node, array.getMat(), eps, "Argument \"" + node.name() + "\"", err);
540
}
541
else
542
{
543
cv::Mat expected;
544
valnode >> expected;
545
cv::Mat actual = array.getMat();
546
547
if(expected.empty())
548
{
549
ASSERT_TRUE(actual.empty())
550
<< " expected empty " << node.name();
551
}
552
else
553
{
554
ASSERT_EQ(expected.size(), actual.size())
555
<< " Argument \"" << node.name() << "\" has unexpected size";
556
557
cv::Mat diff;
558
cv::absdiff(expected, actual, diff);
559
560
if (err == ERROR_ABSOLUTE)
561
{
562
if (!cv::checkRange(diff, true, 0, 0, eps))
563
{
564
if(expected.total() * expected.channels() < 12)
565
std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
566
567
double max;
568
cv::minMaxIdx(diff.reshape(1), 0, &max);
569
570
FAIL() << " Difference (=" << max << ") between argument1 \"" << node.name()
571
<< "\" and expected value is greater than " << eps;
572
}
573
}
574
else if (err == ERROR_RELATIVE)
575
{
576
double maxv, maxa;
577
int violations = countViolations(expected, actual, diff, eps, &maxv, &maxa);
578
if (violations > 0)
579
{
580
if(expected.total() * expected.channels() < 12)
581
std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
582
583
FAIL() << " Relative difference (" << maxv << " of " << maxa << " allowed) between argument \"" << node.name()
584
<< "\" and expected value is greater than " << eps << " in " << violations << " points";
585
}
586
}
587
}
588
}
589
}
590
}
591
592
Regression& Regression::operator() (const std::string& name, cv::InputArray array, double eps, ERROR_TYPE err)
593
{
594
// exit if current test is already failed
595
if(::testing::UnitTest::GetInstance()->current_test_info()->result()->Failed()) return *this;
596
597
/*if(!array.empty() && array.depth() == CV_USRTYPE1)
598
{
599
ADD_FAILURE() << " Can not check regression for CV_USRTYPE1 data type for " << name;
600
return *this;
601
}*/
602
603
std::string nodename = getCurrentTestNodeName();
604
605
cv::FileNode n = rootIn[nodename];
606
if(n.isNone())
607
{
608
if(param_write_sanity)
609
{
610
if (nodename != currentTestNodeName)
611
{
612
if (!currentTestNodeName.empty())
613
write() << "}";
614
currentTestNodeName = nodename;
615
616
write() << nodename << "{";
617
}
618
// TODO: verify that name is alphanumeric, current error message is useless
619
write() << name << "{";
620
write(array);
621
write() << "}";
622
}
623
else if(param_verify_sanity)
624
{
625
ADD_FAILURE() << " No regression data for " << name << " argument, test node: " << nodename;
626
}
627
}
628
else
629
{
630
cv::FileNode this_arg = n[name];
631
if (!this_arg.isMap())
632
ADD_FAILURE() << " No regression data for " << name << " argument";
633
else
634
verify(this_arg, array, eps, err);
635
}
636
637
return *this;
638
}
639
640
641
/*****************************************************************************************\
642
* ::perf::performance_metrics
643
\*****************************************************************************************/
644
performance_metrics::performance_metrics()
645
{
646
clear();
647
}
648
649
void performance_metrics::clear()
650
{
651
bytesIn = 0;
652
bytesOut = 0;
653
samples = 0;
654
outliers = 0;
655
gmean = 0;
656
gstddev = 0;
657
mean = 0;
658
stddev = 0;
659
median = 0;
660
min = 0;
661
frequency = 0;
662
terminationReason = TERM_UNKNOWN;
663
}
664
665
/*****************************************************************************************\
666
* Performance validation results
667
\*****************************************************************************************/
668
669
static bool perf_validation_enabled = false;
670
671
static std::string perf_validation_results_directory;
672
static std::map<std::string, float> perf_validation_results;
673
static std::string perf_validation_results_outfile;
674
675
static double perf_validation_criteria = 0.03; // 3 %
676
static double perf_validation_time_threshold_ms = 0.1;
677
static int perf_validation_idle_delay_ms = 3000; // 3 sec
678
679
static void loadPerfValidationResults(const std::string& fileName)
680
{
681
perf_validation_results.clear();
682
std::ifstream infile(fileName.c_str());
683
while (!infile.eof())
684
{
685
std::string name;
686
float value = 0;
687
if (!(infile >> value))
688
{
689
if (infile.eof())
690
break; // it is OK
691
std::cout << "ERROR: Can't load performance validation results from " << fileName << "!" << std::endl;
692
return;
693
}
694
infile.ignore(1);
695
if (!(std::getline(infile, name)))
696
{
697
std::cout << "ERROR: Can't load performance validation results from " << fileName << "!" << std::endl;
698
return;
699
}
700
if (!name.empty() && name[name.size() - 1] == '\r') // CRLF processing on Linux
701
name.resize(name.size() - 1);
702
perf_validation_results[name] = value;
703
}
704
std::cout << "Performance validation results loaded from " << fileName << " (" << perf_validation_results.size() << " entries)" << std::endl;
705
}
706
707
static void savePerfValidationResult(const std::string& name, float value)
708
{
709
perf_validation_results[name] = value;
710
}
711
712
static void savePerfValidationResults()
713
{
714
if (!perf_validation_results_outfile.empty())
715
{
716
std::ofstream outfile((perf_validation_results_directory + perf_validation_results_outfile).c_str());
717
std::map<std::string, float>::const_iterator i;
718
for (i = perf_validation_results.begin(); i != perf_validation_results.end(); ++i)
719
{
720
outfile << i->second << ';';
721
outfile << i->first << std::endl;
722
}
723
outfile.close();
724
std::cout << "Performance validation results saved (" << perf_validation_results.size() << " entries)" << std::endl;
725
}
726
}
727
728
class PerfValidationEnvironment : public ::testing::Environment
729
{
730
public:
731
virtual ~PerfValidationEnvironment() {}
732
virtual void SetUp() {}
733
734
virtual void TearDown()
735
{
736
savePerfValidationResults();
737
}
738
};
739
740
#ifdef ENABLE_INSTRUMENTATION
741
static void printShift(cv::instr::InstrNode *pNode, cv::instr::InstrNode* pRoot)
742
{
743
// Print empty line for a big tree nodes
744
if(pNode->m_pParent)
745
{
746
int parendIdx = pNode->m_pParent->findChild(pNode);
747
if(parendIdx > 0 && pNode->m_pParent->m_childs[parendIdx-1]->m_childs.size())
748
{
749
printShift(pNode->m_pParent->m_childs[parendIdx-1]->m_childs[0], pRoot);
750
printf("\n");
751
}
752
}
753
754
// Check if parents have more childes
755
std::vector<cv::instr::InstrNode*> cache;
756
cv::instr::InstrNode *pTmpNode = pNode;
757
while(pTmpNode->m_pParent && pTmpNode->m_pParent != pRoot)
758
{
759
cache.push_back(pTmpNode->m_pParent);
760
pTmpNode = pTmpNode->m_pParent;
761
}
762
for(int i = (int)cache.size()-1; i >= 0; i--)
763
{
764
if(cache[i]->m_pParent)
765
{
766
if(cache[i]->m_pParent->findChild(cache[i]) == (int)cache[i]->m_pParent->m_childs.size()-1)
767
printf(" ");
768
else
769
printf("| ");
770
}
771
}
772
}
773
774
static double calcLocalWeight(cv::instr::InstrNode *pNode)
775
{
776
if(pNode->m_pParent && pNode->m_pParent->m_pParent)
777
return ((double)pNode->m_payload.m_ticksTotal*100/pNode->m_pParent->m_payload.m_ticksTotal);
778
else
779
return 100;
780
}
781
782
static double calcGlobalWeight(cv::instr::InstrNode *pNode)
783
{
784
cv::instr::InstrNode* globNode = pNode;
785
786
while(globNode->m_pParent && globNode->m_pParent->m_pParent)
787
globNode = globNode->m_pParent;
788
789
return ((double)pNode->m_payload.m_ticksTotal*100/(double)globNode->m_payload.m_ticksTotal);
790
}
791
792
static void printNodeRec(cv::instr::InstrNode *pNode, cv::instr::InstrNode *pRoot)
793
{
794
printf("%s", (pNode->m_payload.m_funName.substr(0, 40) + ((pNode->m_payload.m_funName.size()>40)?"...":"")).c_str());
795
796
// Write instrumentation flags
797
if(pNode->m_payload.m_instrType != cv::instr::TYPE_GENERAL || pNode->m_payload.m_implType != cv::instr::IMPL_PLAIN)
798
{
799
printf("<");
800
if(pNode->m_payload.m_instrType == cv::instr::TYPE_WRAPPER)
801
printf("W");
802
else if(pNode->m_payload.m_instrType == cv::instr::TYPE_FUN)
803
printf("F");
804
else if(pNode->m_payload.m_instrType == cv::instr::TYPE_MARKER)
805
printf("MARK");
806
807
if(pNode->m_payload.m_instrType != cv::instr::TYPE_GENERAL && pNode->m_payload.m_implType != cv::instr::IMPL_PLAIN)
808
printf("_");
809
810
if(pNode->m_payload.m_implType == cv::instr::IMPL_IPP)
811
printf("IPP");
812
else if(pNode->m_payload.m_implType == cv::instr::IMPL_OPENCL)
813
printf("OCL");
814
815
printf(">");
816
}
817
818
if(pNode->m_pParent)
819
{
820
printf(" - TC:%d C:%d", pNode->m_payload.m_threads, pNode->m_payload.m_counter);
821
printf(" T:%.2fms", pNode->m_payload.getTotalMs());
822
if(pNode->m_pParent->m_pParent)
823
printf(" L:%.0f%% G:%.0f%%", calcLocalWeight(pNode), calcGlobalWeight(pNode));
824
}
825
printf("\n");
826
827
{
828
// Group childes by name
829
for(size_t i = 1; i < pNode->m_childs.size(); i++)
830
{
831
if(pNode->m_childs[i-1]->m_payload.m_funName == pNode->m_childs[i]->m_payload.m_funName )
832
continue;
833
for(size_t j = i+1; j < pNode->m_childs.size(); j++)
834
{
835
if(pNode->m_childs[i-1]->m_payload.m_funName == pNode->m_childs[j]->m_payload.m_funName )
836
{
837
cv::swap(pNode->m_childs[i], pNode->m_childs[j]);
838
i++;
839
}
840
}
841
}
842
}
843
844
for(size_t i = 0; i < pNode->m_childs.size(); i++)
845
{
846
printShift(pNode->m_childs[i], pRoot);
847
848
if(i == pNode->m_childs.size()-1)
849
printf("\\---");
850
else
851
printf("|---");
852
printNodeRec(pNode->m_childs[i], pRoot);
853
}
854
}
855
856
template <typename T>
857
std::string to_string_with_precision(const T value, const int p = 3)
858
{
859
std::ostringstream out;
860
out << std::fixed << std::setprecision(p) << value;
861
return out.str();
862
}
863
864
static cv::String nodeToString(cv::instr::InstrNode *pNode)
865
{
866
cv::String string;
867
if (pNode->m_payload.m_funName == "ROOT")
868
string = pNode->m_payload.m_funName;
869
else
870
{
871
string = "#";
872
string += std::to_string((int)pNode->m_payload.m_instrType);
873
string += pNode->m_payload.m_funName;
874
string += " - L:";
875
string += to_string_with_precision(calcLocalWeight(pNode));
876
string += ", G:";
877
string += to_string_with_precision(calcGlobalWeight(pNode));
878
}
879
string += "(";
880
for(size_t i = 0; i < pNode->m_childs.size(); i++)
881
string += nodeToString(pNode->m_childs[i]);
882
string += ")";
883
884
return string;
885
}
886
887
static uint64 getNodeTimeRec(cv::instr::InstrNode *pNode, cv::instr::TYPE type, cv::instr::IMPL impl)
888
{
889
uint64 ticks = 0;
890
891
if (pNode->m_pParent && (type < 0 || pNode->m_payload.m_instrType == type) && pNode->m_payload.m_implType == impl)
892
{
893
ticks = pNode->m_payload.m_ticksTotal;
894
return ticks;
895
}
896
897
for(size_t i = 0; i < pNode->m_childs.size(); i++)
898
ticks += getNodeTimeRec(pNode->m_childs[i], type, impl);
899
900
return ticks;
901
}
902
903
static uint64 getImplTime(cv::instr::IMPL impl)
904
{
905
uint64 ticks = 0;
906
cv::instr::InstrNode *pRoot = cv::instr::getTrace();
907
908
ticks = getNodeTimeRec(pRoot, cv::instr::TYPE_FUN, impl);
909
910
return ticks;
911
}
912
913
static uint64 getTotalTime()
914
{
915
uint64 ticks = 0;
916
cv::instr::InstrNode *pRoot = cv::instr::getTrace();
917
918
for(size_t i = 0; i < pRoot->m_childs.size(); i++)
919
ticks += pRoot->m_childs[i]->m_payload.m_ticksTotal;
920
921
return ticks;
922
}
923
924
::cv::String InstumentData::treeToString()
925
{
926
cv::String string = nodeToString(cv::instr::getTrace());
927
return string;
928
}
929
930
void InstumentData::printTree()
931
{
932
printf("[ TRACE ]\n");
933
printNodeRec(cv::instr::getTrace(), cv::instr::getTrace());
934
#ifdef HAVE_IPP
935
printf("\nIPP weight: %.1f%%", ((double)getImplTime(cv::instr::IMPL_IPP)*100/(double)getTotalTime()));
936
#endif
937
#ifdef HAVE_OPENCL
938
printf("\nOPENCL weight: %.1f%%", ((double)getImplTime(cv::instr::IMPL_OPENCL)*100/(double)getTotalTime()));
939
#endif
940
printf("\n[/TRACE ]\n");
941
fflush(stdout);
942
}
943
#endif
944
945
/*****************************************************************************************\
946
* ::perf::TestBase
947
\*****************************************************************************************/
948
949
950
void TestBase::Init(int argc, const char* const argv[])
951
{
952
std::vector<std::string> plain_only;
953
plain_only.push_back("plain");
954
TestBase::Init(plain_only, argc, argv);
955
}
956
957
void TestBase::Init(const std::vector<std::string> & availableImpls,
958
int argc, const char* const argv[])
959
{
960
CV_TRACE_FUNCTION();
961
962
available_impls = availableImpls;
963
964
const std::string command_line_keys =
965
"{ perf_max_outliers |8 |percent of allowed outliers}"
966
"{ perf_min_samples |10 |minimal required numer of samples}"
967
"{ perf_force_samples |100 |force set maximum number of samples for all tests}"
968
"{ perf_seed |809564 |seed for random numbers generator}"
969
"{ perf_threads |-1 |the number of worker threads, if parallel execution is enabled}"
970
"{ perf_write_sanity |false |create new records for sanity checks}"
971
"{ perf_verify_sanity |false |fail tests having no regression data for sanity checks}"
972
"{ perf_impl |" + available_impls[0] +
973
"|the implementation variant of functions under test}"
974
"{ perf_list_impls |false |list available implementation variants and exit}"
975
"{ perf_run_cpu |false |deprecated, equivalent to --perf_impl=plain}"
976
"{ perf_strategy |default |specifies performance measuring strategy: default, base or simple (weak restrictions)}"
977
"{ perf_read_validation_results | |specifies file name with performance results from previous run}"
978
"{ perf_write_validation_results | |specifies file name to write performance validation results}"
979
#ifdef __ANDROID__
980
"{ perf_time_limit |6.0 |default time limit for a single test (in seconds)}"
981
"{ perf_affinity_mask |0 |set affinity mask for the main thread}"
982
"{ perf_log_power_checkpoints | |additional xml logging for power measurement}"
983
#else
984
"{ perf_time_limit |3.0 |default time limit for a single test (in seconds)}"
985
#endif
986
"{ perf_max_deviation |1.0 |}"
987
#ifdef HAVE_IPP
988
"{ perf_ipp_check |false |check whether IPP works without failures}"
989
#endif
990
#ifdef CV_COLLECT_IMPL_DATA
991
"{ perf_collect_impl |false |collect info about executed implementations}"
992
#endif
993
#ifdef ENABLE_INSTRUMENTATION
994
"{ perf_instrument |0 |instrument code to collect implementations trace: 1 - perform instrumentation; 2 - separate functions with the same name }"
995
#endif
996
"{ help h |false |print help info}"
997
#ifdef HAVE_CUDA
998
"{ perf_cuda_device |0 |run CUDA test suite onto specific CUDA capable device}"
999
"{ perf_cuda_info_only |false |print an information about system and an available CUDA devices and then exit.}"
1000
#endif
1001
"{ skip_unstable |false |skip unstable tests }"
1002
;
1003
1004
cv::CommandLineParser args(argc, argv, command_line_keys);
1005
if (args.get<bool>("help"))
1006
{
1007
args.printMessage();
1008
return;
1009
}
1010
1011
::testing::AddGlobalTestEnvironment(new PerfEnvironment);
1012
1013
param_impl = args.get<bool>("perf_run_cpu") ? "plain" : args.get<std::string>("perf_impl");
1014
std::string perf_strategy = args.get<std::string>("perf_strategy");
1015
if (perf_strategy == "default")
1016
{
1017
// nothing
1018
}
1019
else if (perf_strategy == "base")
1020
{
1021
strategyForce = PERF_STRATEGY_BASE;
1022
}
1023
else if (perf_strategy == "simple")
1024
{
1025
strategyForce = PERF_STRATEGY_SIMPLE;
1026
}
1027
else
1028
{
1029
printf("No such strategy: %s\n", perf_strategy.c_str());
1030
exit(1);
1031
}
1032
param_max_outliers = std::min(100., std::max(0., args.get<double>("perf_max_outliers")));
1033
param_min_samples = std::max(1u, args.get<unsigned int>("perf_min_samples"));
1034
param_max_deviation = std::max(0., args.get<double>("perf_max_deviation"));
1035
param_seed = args.get<unsigned int>("perf_seed");
1036
param_time_limit = std::max(0., args.get<double>("perf_time_limit"));
1037
param_force_samples = args.get<unsigned int>("perf_force_samples");
1038
param_write_sanity = args.get<bool>("perf_write_sanity");
1039
param_verify_sanity = args.get<bool>("perf_verify_sanity");
1040
1041
#ifdef HAVE_IPP
1042
test_ipp_check = !args.get<bool>("perf_ipp_check") ? getenv("OPENCV_IPP_CHECK") != NULL : true;
1043
#endif
1044
testThreads = args.get<int>("perf_threads");
1045
#ifdef CV_COLLECT_IMPL_DATA
1046
param_collect_impl = args.get<bool>("perf_collect_impl");
1047
#endif
1048
#ifdef ENABLE_INSTRUMENTATION
1049
param_instrument = args.get<int>("perf_instrument");
1050
#endif
1051
#ifdef __ANDROID__
1052
param_affinity_mask = args.get<int>("perf_affinity_mask");
1053
log_power_checkpoints = args.has("perf_log_power_checkpoints");
1054
#endif
1055
1056
bool param_list_impls = args.get<bool>("perf_list_impls");
1057
1058
if (param_list_impls)
1059
{
1060
fputs("Available implementation variants:", stdout);
1061
for (size_t i = 0; i < available_impls.size(); ++i) {
1062
putchar(' ');
1063
fputs(available_impls[i].c_str(), stdout);
1064
}
1065
putchar('\n');
1066
exit(0);
1067
}
1068
1069
if (std::find(available_impls.begin(), available_impls.end(), param_impl) == available_impls.end())
1070
{
1071
printf("No such implementation: %s\n", param_impl.c_str());
1072
exit(1);
1073
}
1074
1075
#ifdef CV_COLLECT_IMPL_DATA
1076
if(param_collect_impl)
1077
cv::setUseCollection(1);
1078
else
1079
cv::setUseCollection(0);
1080
#endif
1081
#ifdef ENABLE_INSTRUMENTATION
1082
if(param_instrument > 0)
1083
{
1084
if(param_instrument == 2)
1085
cv::instr::setFlags(cv::instr::getFlags()|cv::instr::FLAGS_EXPAND_SAME_NAMES);
1086
cv::instr::setUseInstrumentation(true);
1087
}
1088
else
1089
cv::instr::setUseInstrumentation(false);
1090
#endif
1091
1092
#ifdef HAVE_CUDA
1093
1094
bool printOnly = args.get<bool>("perf_cuda_info_only");
1095
1096
if (printOnly)
1097
exit(0);
1098
#endif
1099
1100
skipUnstableTests = args.get<bool>("skip_unstable");
1101
1102
if (available_impls.size() > 1)
1103
printf("[----------]\n[ INFO ] \tImplementation variant: %s.\n[----------]\n", param_impl.c_str()), fflush(stdout);
1104
1105
#ifdef HAVE_CUDA
1106
1107
param_cuda_device = std::max(0, std::min(cv::cuda::getCudaEnabledDeviceCount(), args.get<int>("perf_cuda_device")));
1108
1109
if (param_impl == "cuda")
1110
{
1111
cv::cuda::DeviceInfo info(param_cuda_device);
1112
if (!info.isCompatible())
1113
{
1114
printf("[----------]\n[ FAILURE ] \tDevice %s is NOT compatible with current CUDA module build.\n[----------]\n", info.name()), fflush(stdout);
1115
exit(-1);
1116
}
1117
1118
cv::cuda::setDevice(param_cuda_device);
1119
1120
printf("[----------]\n[ GPU INFO ] \tRun test suite on %s GPU.\n[----------]\n", info.name()), fflush(stdout);
1121
}
1122
#endif
1123
1124
{
1125
#ifndef WINRT
1126
const char* path = getenv("OPENCV_PERF_VALIDATION_DIR");
1127
#else
1128
const char* path = OPENCV_PERF_VALIDATION_DIR;
1129
#endif
1130
if (path)
1131
perf_validation_results_directory = path;
1132
}
1133
1134
std::string fileName_perf_validation_results_src = args.get<std::string>("perf_read_validation_results");
1135
if (!fileName_perf_validation_results_src.empty())
1136
{
1137
perf_validation_enabled = true;
1138
loadPerfValidationResults(perf_validation_results_directory + fileName_perf_validation_results_src);
1139
}
1140
1141
perf_validation_results_outfile = args.get<std::string>("perf_write_validation_results");
1142
if (!perf_validation_results_outfile.empty())
1143
{
1144
perf_validation_enabled = true;
1145
::testing::AddGlobalTestEnvironment(new PerfValidationEnvironment());
1146
}
1147
1148
if (!args.check())
1149
{
1150
args.printErrors();
1151
exit(1);
1152
}
1153
1154
timeLimitDefault = param_time_limit == 0.0 ? 1 : (int64)(param_time_limit * cv::getTickFrequency());
1155
iterationsLimitDefault = param_force_samples == 0 ? (unsigned)(-1) : param_force_samples;
1156
_timeadjustment = _calibrate();
1157
}
1158
1159
void TestBase::RecordRunParameters()
1160
{
1161
::testing::Test::RecordProperty("cv_implementation", param_impl);
1162
::testing::Test::RecordProperty("cv_num_threads", testThreads);
1163
1164
#ifdef HAVE_CUDA
1165
if (param_impl == "cuda")
1166
{
1167
cv::cuda::DeviceInfo info(param_cuda_device);
1168
::testing::Test::RecordProperty("cv_cuda_gpu", info.name());
1169
}
1170
#endif
1171
}
1172
1173
std::string TestBase::getSelectedImpl()
1174
{
1175
return param_impl;
1176
}
1177
1178
enum PERF_STRATEGY TestBase::setModulePerformanceStrategy(enum PERF_STRATEGY strategy)
1179
{
1180
enum PERF_STRATEGY ret = strategyModule;
1181
strategyModule = strategy;
1182
return ret;
1183
}
1184
1185
enum PERF_STRATEGY TestBase::getCurrentModulePerformanceStrategy()
1186
{
1187
return strategyForce == PERF_STRATEGY_DEFAULT ? strategyModule : strategyForce;
1188
}
1189
1190
1191
int64 TestBase::_calibrate()
1192
{
1193
CV_TRACE_FUNCTION();
1194
class _helper : public ::perf::TestBase
1195
{
1196
public:
1197
performance_metrics& getMetrics() { return calcMetrics(); }
1198
virtual void TestBody() {}
1199
virtual void PerfTestBody()
1200
{
1201
//the whole system warmup
1202
SetUp();
1203
cv::Mat a(2048, 2048, CV_32S, cv::Scalar(1));
1204
cv::Mat b(2048, 2048, CV_32S, cv::Scalar(2));
1205
declare.time(30);
1206
double s = 0;
1207
for(declare.iterations(20); next() && startTimer(); stopTimer())
1208
s+=a.dot(b);
1209
declare.time(s);
1210
1211
//self calibration
1212
SetUp();
1213
for(declare.iterations(1000); next() && startTimer(); stopTimer()){}
1214
}
1215
};
1216
1217
// Initialize ThreadPool
1218
class _dummyParallel : public ParallelLoopBody
1219
{
1220
public:
1221
void operator()(const cv::Range& range) const
1222
{
1223
// nothing
1224
CV_UNUSED(range);
1225
}
1226
};
1227
parallel_for_(cv::Range(0, 1000), _dummyParallel());
1228
1229
_timeadjustment = 0;
1230
_helper h;
1231
h.PerfTestBody();
1232
double compensation = h.getMetrics().min;
1233
if (getCurrentModulePerformanceStrategy() == PERF_STRATEGY_SIMPLE)
1234
{
1235
CV_Assert(compensation < 0.01 * cv::getTickFrequency());
1236
compensation = 0.0f; // simple strategy doesn't require any compensation
1237
}
1238
LOGD("Time compensation is %.0f", compensation);
1239
return (int64)compensation;
1240
}
1241
1242
#ifdef _MSC_VER
1243
# pragma warning(push)
1244
# pragma warning(disable:4355) // 'this' : used in base member initializer list
1245
#endif
1246
TestBase::TestBase(): testStrategy(PERF_STRATEGY_DEFAULT), declare(this)
1247
{
1248
lastTime = totalTime = timeLimit = 0;
1249
nIters = currentIter = runsPerIteration = 0;
1250
minIters = param_min_samples;
1251
verified = false;
1252
perfValidationStage = 0;
1253
}
1254
#ifdef _MSC_VER
1255
# pragma warning(pop)
1256
#endif
1257
1258
1259
void TestBase::declareArray(SizeVector& sizes, cv::InputOutputArray a, WarmUpType wtype)
1260
{
1261
if (!a.empty())
1262
{
1263
sizes.push_back(std::pair<int, cv::Size>(getSizeInBytes(a), getSize(a)));
1264
warmup(a, wtype);
1265
}
1266
else if (a.kind() != cv::_InputArray::NONE)
1267
ADD_FAILURE() << " Uninitialized input/output parameters are not allowed for performance tests";
1268
}
1269
1270
void TestBase::warmup(cv::InputOutputArray a, WarmUpType wtype)
1271
{
1272
CV_TRACE_FUNCTION();
1273
if (a.empty())
1274
return;
1275
else if (a.isUMat())
1276
{
1277
if (wtype == WARMUP_RNG || wtype == WARMUP_WRITE)
1278
{
1279
int depth = a.depth();
1280
if (depth == CV_8U)
1281
cv::randu(a, 0, 256);
1282
else if (depth == CV_8S)
1283
cv::randu(a, -128, 128);
1284
else if (depth == CV_16U)
1285
cv::randu(a, 0, 1024);
1286
else if (depth == CV_32F || depth == CV_64F)
1287
cv::randu(a, -1.0, 1.0);
1288
else if (depth == CV_16S || depth == CV_32S)
1289
cv::randu(a, -4096, 4096);
1290
else
1291
CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported format");
1292
}
1293
return;
1294
}
1295
else if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
1296
warmup_impl(a.getMat(), wtype);
1297
else
1298
{
1299
size_t total = a.total();
1300
for (size_t i = 0; i < total; ++i)
1301
warmup_impl(a.getMat((int)i), wtype);
1302
}
1303
}
1304
1305
int TestBase::getSizeInBytes(cv::InputArray a)
1306
{
1307
if (a.empty()) return 0;
1308
int total = (int)a.total();
1309
if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
1310
return total * CV_ELEM_SIZE(a.type());
1311
1312
int size = 0;
1313
for (int i = 0; i < total; ++i)
1314
size += (int)a.total(i) * CV_ELEM_SIZE(a.type(i));
1315
1316
return size;
1317
}
1318
1319
cv::Size TestBase::getSize(cv::InputArray a)
1320
{
1321
if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
1322
return a.size();
1323
return cv::Size();
1324
}
1325
1326
PERF_STRATEGY TestBase::getCurrentPerformanceStrategy() const
1327
{
1328
if (strategyForce == PERF_STRATEGY_DEFAULT)
1329
return (testStrategy == PERF_STRATEGY_DEFAULT) ? strategyModule : testStrategy;
1330
else
1331
return strategyForce;
1332
}
1333
1334
bool TestBase::next()
1335
{
1336
static int64 lastActivityPrintTime = 0;
1337
1338
if (currentIter != (unsigned int)-1)
1339
{
1340
if (currentIter + 1 != times.size())
1341
ADD_FAILURE() << " next() is called before stopTimer()";
1342
}
1343
else
1344
{
1345
lastActivityPrintTime = 0;
1346
metrics.clear();
1347
}
1348
1349
cv::theRNG().state = param_seed; //this rng should generate same numbers for each run
1350
++currentIter;
1351
1352
bool has_next = false;
1353
1354
do {
1355
assert(currentIter == times.size());
1356
if (currentIter == 0)
1357
{
1358
has_next = true;
1359
break;
1360
}
1361
1362
if (getCurrentPerformanceStrategy() == PERF_STRATEGY_BASE)
1363
{
1364
has_next = currentIter < nIters && totalTime < timeLimit;
1365
}
1366
else
1367
{
1368
assert(getCurrentPerformanceStrategy() == PERF_STRATEGY_SIMPLE);
1369
if (totalTime - lastActivityPrintTime >= cv::getTickFrequency() * 10)
1370
{
1371
std::cout << '.' << std::endl;
1372
lastActivityPrintTime = totalTime;
1373
}
1374
if (currentIter >= nIters)
1375
{
1376
has_next = false;
1377
break;
1378
}
1379
if (currentIter < minIters)
1380
{
1381
has_next = true;
1382
break;
1383
}
1384
1385
calcMetrics();
1386
1387
if (fabs(metrics.mean) > 1e-6)
1388
has_next = metrics.stddev > perf_stability_criteria * fabs(metrics.mean);
1389
else
1390
has_next = true;
1391
}
1392
} while (false);
1393
1394
if (perf_validation_enabled && !has_next)
1395
{
1396
calcMetrics();
1397
double median_ms = metrics.median * 1000.0f / metrics.frequency;
1398
1399
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
1400
std::string name = (test_info == 0) ? "" :
1401
std::string(test_info->test_case_name()) + "--" + test_info->name();
1402
1403
if (!perf_validation_results.empty() && !name.empty())
1404
{
1405
std::map<std::string, float>::iterator i = perf_validation_results.find(name);
1406
bool isSame = false;
1407
bool found = false;
1408
bool grow = false;
1409
if (i != perf_validation_results.end())
1410
{
1411
found = true;
1412
double prev_result = i->second;
1413
grow = median_ms > prev_result;
1414
isSame = fabs(median_ms - prev_result) <= perf_validation_criteria * fabs(median_ms);
1415
if (!isSame)
1416
{
1417
if (perfValidationStage == 0)
1418
{
1419
printf("Performance is changed (samples = %d, median):\n %.2f ms (current)\n %.2f ms (previous)\n", (int)times.size(), median_ms, prev_result);
1420
}
1421
}
1422
}
1423
else
1424
{
1425
if (perfValidationStage == 0)
1426
printf("New performance result is detected\n");
1427
}
1428
if (!isSame)
1429
{
1430
if (perfValidationStage < 2)
1431
{
1432
if (perfValidationStage == 0 && currentIter <= minIters * 3 && currentIter < nIters)
1433
{
1434
unsigned int new_minIters = std::max(minIters * 5, currentIter * 3);
1435
printf("Increase minIters from %u to %u\n", minIters, new_minIters);
1436
minIters = new_minIters;
1437
has_next = true;
1438
perfValidationStage++;
1439
}
1440
else if (found && currentIter >= nIters &&
1441
median_ms > perf_validation_time_threshold_ms &&
1442
(grow || metrics.stddev > perf_stability_criteria * fabs(metrics.mean)))
1443
{
1444
CV_TRACE_REGION("idle_delay");
1445
printf("Performance is unstable, it may be a result of overheat problems\n");
1446
printf("Idle delay for %d ms... \n", perf_validation_idle_delay_ms);
1447
#if defined _WIN32
1448
#ifndef WINRT_8_0
1449
Sleep(perf_validation_idle_delay_ms);
1450
#else
1451
WaitForSingleObjectEx(GetCurrentThread(), perf_validation_idle_delay_ms, FALSE);
1452
#endif
1453
#else
1454
usleep(perf_validation_idle_delay_ms * 1000);
1455
#endif
1456
has_next = true;
1457
minIters = std::min(minIters * 5, nIters);
1458
// reset collected samples
1459
currentIter = 0;
1460
times.clear();
1461
metrics.clear();
1462
perfValidationStage += 2;
1463
}
1464
if (!has_next)
1465
{
1466
printf("Assume that current result is valid\n");
1467
}
1468
}
1469
else
1470
{
1471
printf("Re-measured performance result: %.2f ms\n", median_ms);
1472
}
1473
}
1474
}
1475
1476
if (!has_next && !name.empty())
1477
{
1478
savePerfValidationResult(name, (float)median_ms);
1479
}
1480
}
1481
1482
#ifdef __ANDROID__
1483
if (log_power_checkpoints)
1484
{
1485
timeval tim;
1486
gettimeofday(&tim, NULL);
1487
unsigned long long t1 = tim.tv_sec * 1000LLU + (unsigned long long)(tim.tv_usec / 1000.f);
1488
1489
if (currentIter == 1) RecordProperty("test_start", cv::format("%llu",t1).c_str());
1490
if (!has_next) RecordProperty("test_complete", cv::format("%llu",t1).c_str());
1491
}
1492
#endif
1493
1494
return has_next;
1495
}
1496
1497
void TestBase::warmup_impl(cv::Mat m, WarmUpType wtype)
1498
{
1499
switch(wtype)
1500
{
1501
case WARMUP_READ:
1502
cv::sum(m.reshape(1));
1503
return;
1504
case WARMUP_WRITE:
1505
m.reshape(1).setTo(cv::Scalar::all(0));
1506
return;
1507
case WARMUP_RNG:
1508
randu(m);
1509
return;
1510
default:
1511
return;
1512
}
1513
}
1514
1515
unsigned int TestBase::getTotalInputSize() const
1516
{
1517
unsigned int res = 0;
1518
for (SizeVector::const_iterator i = inputData.begin(); i != inputData.end(); ++i)
1519
res += i->first;
1520
return res;
1521
}
1522
1523
unsigned int TestBase::getTotalOutputSize() const
1524
{
1525
unsigned int res = 0;
1526
for (SizeVector::const_iterator i = outputData.begin(); i != outputData.end(); ++i)
1527
res += i->first;
1528
return res;
1529
}
1530
1531
bool TestBase::startTimer()
1532
{
1533
#ifdef ENABLE_INSTRUMENTATION
1534
if(currentIter == 0)
1535
{
1536
cv::instr::setFlags(cv::instr::getFlags()|cv::instr::FLAGS_MAPPING); // enable mapping for the first run
1537
cv::instr::resetTrace();
1538
}
1539
#endif
1540
lastTime = cv::getTickCount();
1541
return true; // dummy true for conditional loop
1542
}
1543
1544
void TestBase::stopTimer()
1545
{
1546
int64 time = cv::getTickCount();
1547
if (lastTime == 0)
1548
ADD_FAILURE() << " stopTimer() is called before startTimer()/next()";
1549
lastTime = time - lastTime;
1550
totalTime += lastTime;
1551
lastTime -= _timeadjustment;
1552
if (lastTime < 0) lastTime = 0;
1553
times.push_back(lastTime);
1554
lastTime = 0;
1555
1556
#ifdef ENABLE_INSTRUMENTATION
1557
cv::instr::setFlags(cv::instr::getFlags()&~cv::instr::FLAGS_MAPPING); // disable mapping to decrease overhead for +1 run
1558
#endif
1559
}
1560
1561
performance_metrics& TestBase::calcMetrics()
1562
{
1563
CV_Assert(metrics.samples <= (unsigned int)currentIter);
1564
if ((metrics.samples == (unsigned int)currentIter) || times.size() == 0)
1565
return metrics;
1566
1567
metrics.bytesIn = getTotalInputSize();
1568
metrics.bytesOut = getTotalOutputSize();
1569
metrics.frequency = cv::getTickFrequency();
1570
metrics.samples = (unsigned int)times.size();
1571
metrics.outliers = 0;
1572
1573
if (metrics.terminationReason != performance_metrics::TERM_INTERRUPT && metrics.terminationReason != performance_metrics::TERM_EXCEPTION)
1574
{
1575
if (currentIter == nIters)
1576
metrics.terminationReason = performance_metrics::TERM_ITERATIONS;
1577
else if (totalTime >= timeLimit)
1578
metrics.terminationReason = performance_metrics::TERM_TIME;
1579
else
1580
metrics.terminationReason = performance_metrics::TERM_UNKNOWN;
1581
}
1582
1583
std::sort(times.begin(), times.end());
1584
1585
TimeVector::const_iterator start = times.begin();
1586
TimeVector::const_iterator end = times.end();
1587
1588
if (getCurrentPerformanceStrategy() == PERF_STRATEGY_BASE)
1589
{
1590
//estimate mean and stddev for log(time)
1591
double gmean = 0;
1592
double gstddev = 0;
1593
int n = 0;
1594
for(TimeVector::const_iterator i = times.begin(); i != times.end(); ++i)
1595
{
1596
double x = static_cast<double>(*i)/runsPerIteration;
1597
if (x < DBL_EPSILON) continue;
1598
double lx = log(x);
1599
1600
++n;
1601
double delta = lx - gmean;
1602
gmean += delta / n;
1603
gstddev += delta * (lx - gmean);
1604
}
1605
1606
gstddev = n > 1 ? sqrt(gstddev / (n - 1)) : 0;
1607
1608
//filter outliers assuming log-normal distribution
1609
//http://stackoverflow.com/questions/1867426/modeling-distribution-of-performance-measurements
1610
if (gstddev > DBL_EPSILON)
1611
{
1612
double minout = exp(gmean - 3 * gstddev) * runsPerIteration;
1613
double maxout = exp(gmean + 3 * gstddev) * runsPerIteration;
1614
while(*start < minout) ++start, ++metrics.outliers;
1615
do --end, ++metrics.outliers; while(*end > maxout);
1616
++end, --metrics.outliers;
1617
}
1618
}
1619
else if (getCurrentPerformanceStrategy() == PERF_STRATEGY_SIMPLE)
1620
{
1621
metrics.outliers = static_cast<int>(times.size() * param_max_outliers / 100);
1622
for (unsigned int i = 0; i < metrics.outliers; i++)
1623
--end;
1624
}
1625
else
1626
{
1627
assert(false);
1628
}
1629
1630
int offset = static_cast<int>(start - times.begin());
1631
1632
metrics.min = static_cast<double>(*start)/runsPerIteration;
1633
//calc final metrics
1634
unsigned int n = 0;
1635
double gmean = 0;
1636
double gstddev = 0;
1637
double mean = 0;
1638
double stddev = 0;
1639
unsigned int m = 0;
1640
for(; start != end; ++start)
1641
{
1642
double x = static_cast<double>(*start)/runsPerIteration;
1643
if (x > DBL_EPSILON)
1644
{
1645
double lx = log(x);
1646
++m;
1647
double gdelta = lx - gmean;
1648
gmean += gdelta / m;
1649
gstddev += gdelta * (lx - gmean);
1650
}
1651
++n;
1652
double delta = x - mean;
1653
mean += delta / n;
1654
stddev += delta * (x - mean);
1655
}
1656
1657
metrics.mean = mean;
1658
metrics.gmean = exp(gmean);
1659
metrics.gstddev = m > 1 ? sqrt(gstddev / (m - 1)) : 0;
1660
metrics.stddev = n > 1 ? sqrt(stddev / (n - 1)) : 0;
1661
metrics.median = (n % 2
1662
? (double)times[offset + n / 2]
1663
: 0.5 * (times[offset + n / 2] + times[offset + n / 2 - 1])
1664
) / runsPerIteration;
1665
1666
return metrics;
1667
}
1668
1669
void TestBase::validateMetrics()
1670
{
1671
performance_metrics& m = calcMetrics();
1672
1673
if (HasFailure()) return;
1674
1675
ASSERT_GE(m.samples, 1u)
1676
<< " No time measurements was performed.\nstartTimer() and stopTimer() commands are required for performance tests.";
1677
1678
if (getCurrentPerformanceStrategy() == PERF_STRATEGY_BASE)
1679
{
1680
EXPECT_GE(m.samples, param_min_samples)
1681
<< " Only a few samples are collected.\nPlease increase number of iterations or/and time limit to get reliable performance measurements.";
1682
1683
if (m.gstddev > DBL_EPSILON)
1684
{
1685
EXPECT_GT(/*m.gmean * */1., /*m.gmean * */ 2 * sinh(m.gstddev * param_max_deviation))
1686
<< " Test results are not reliable ((mean-sigma,mean+sigma) deviation interval is greater than measured time interval).";
1687
}
1688
1689
EXPECT_LE(m.outliers, std::max((unsigned int)cvCeil(m.samples * param_max_outliers / 100.), 1u))
1690
<< " Test results are not reliable (too many outliers).";
1691
}
1692
else if (getCurrentPerformanceStrategy() == PERF_STRATEGY_SIMPLE)
1693
{
1694
double mean = metrics.mean * 1000.0f / metrics.frequency;
1695
double median = metrics.median * 1000.0f / metrics.frequency;
1696
double min_value = metrics.min * 1000.0f / metrics.frequency;
1697
double stddev = metrics.stddev * 1000.0f / metrics.frequency;
1698
double percents = stddev / mean * 100.f;
1699
printf("[ PERFSTAT ] (samples=%d mean=%.2f median=%.2f min=%.2f stddev=%.2f (%.1f%%))\n", (int)metrics.samples, mean, median, min_value, stddev, percents);
1700
}
1701
else
1702
{
1703
assert(false);
1704
}
1705
}
1706
1707
void TestBase::reportMetrics(bool toJUnitXML)
1708
{
1709
CV_TRACE_FUNCTION();
1710
1711
performance_metrics& m = calcMetrics();
1712
1713
CV_TRACE_ARG_VALUE(samples, "samples", (int64)m.samples);
1714
CV_TRACE_ARG_VALUE(outliers, "outliers", (int64)m.outliers);
1715
CV_TRACE_ARG_VALUE(median, "mean_ms", (double)(m.mean * 1000.0f / metrics.frequency));
1716
CV_TRACE_ARG_VALUE(median, "median_ms", (double)(m.median * 1000.0f / metrics.frequency));
1717
CV_TRACE_ARG_VALUE(stddev, "stddev_ms", (double)(m.stddev * 1000.0f / metrics.frequency));
1718
CV_TRACE_ARG_VALUE(stddev_percents, "stddev_percents", (double)(m.stddev / (double)m.mean * 100.0f));
1719
1720
if (m.terminationReason == performance_metrics::TERM_SKIP_TEST)
1721
{
1722
if (toJUnitXML)
1723
{
1724
RecordProperty("custom_status", "skipped");
1725
}
1726
}
1727
else if (toJUnitXML)
1728
{
1729
RecordProperty("bytesIn", (int)m.bytesIn);
1730
RecordProperty("bytesOut", (int)m.bytesOut);
1731
RecordProperty("term", m.terminationReason);
1732
RecordProperty("samples", (int)m.samples);
1733
RecordProperty("outliers", (int)m.outliers);
1734
RecordProperty("frequency", cv::format("%.0f", m.frequency).c_str());
1735
RecordProperty("min", cv::format("%.0f", m.min).c_str());
1736
RecordProperty("median", cv::format("%.0f", m.median).c_str());
1737
RecordProperty("gmean", cv::format("%.0f", m.gmean).c_str());
1738
RecordProperty("gstddev", cv::format("%.6f", m.gstddev).c_str());
1739
RecordProperty("mean", cv::format("%.0f", m.mean).c_str());
1740
RecordProperty("stddev", cv::format("%.0f", m.stddev).c_str());
1741
#ifdef ENABLE_INSTRUMENTATION
1742
if(cv::instr::useInstrumentation())
1743
{
1744
cv::String tree = InstumentData::treeToString();
1745
RecordProperty("functions_hierarchy", tree.c_str());
1746
RecordProperty("total_ipp_weight", cv::format("%.1f", ((double)getImplTime(cv::instr::IMPL_IPP)*100/(double)getTotalTime())));
1747
RecordProperty("total_opencl_weight", cv::format("%.1f", ((double)getImplTime(cv::instr::IMPL_OPENCL)*100/(double)getTotalTime())));
1748
cv::instr::resetTrace();
1749
}
1750
#endif
1751
#ifdef CV_COLLECT_IMPL_DATA
1752
if(param_collect_impl)
1753
{
1754
RecordProperty("impl_ipp", (int)(implConf.ipp || implConf.icv));
1755
RecordProperty("impl_ocl", (int)implConf.ocl);
1756
RecordProperty("impl_plain", (int)implConf.plain);
1757
1758
std::string rec_line;
1759
std::vector<cv::String> rec;
1760
rec_line.clear();
1761
rec = implConf.GetCallsForImpl(CV_IMPL_IPP|CV_IMPL_MT);
1762
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1763
rec = implConf.GetCallsForImpl(CV_IMPL_IPP);
1764
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1765
RecordProperty("impl_rec_ipp", rec_line.c_str());
1766
1767
rec_line.clear();
1768
rec = implConf.GetCallsForImpl(CV_IMPL_OCL);
1769
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1770
RecordProperty("impl_rec_ocl", rec_line.c_str());
1771
}
1772
#endif
1773
}
1774
else
1775
{
1776
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
1777
const char* type_param = test_info->type_param();
1778
const char* value_param = test_info->value_param();
1779
1780
#if defined(__ANDROID__) && defined(USE_ANDROID_LOGGING)
1781
LOGD("[ FAILED ] %s.%s", test_info->test_case_name(), test_info->name());
1782
#endif
1783
1784
if (type_param) LOGD("type = %11s", type_param);
1785
if (value_param) LOGD("params = %11s", value_param);
1786
1787
switch (m.terminationReason)
1788
{
1789
case performance_metrics::TERM_ITERATIONS:
1790
LOGD("termination reason: reached maximum number of iterations");
1791
break;
1792
case performance_metrics::TERM_TIME:
1793
LOGD("termination reason: reached time limit");
1794
break;
1795
case performance_metrics::TERM_INTERRUPT:
1796
LOGD("termination reason: aborted by the performance testing framework");
1797
break;
1798
case performance_metrics::TERM_EXCEPTION:
1799
LOGD("termination reason: unhandled exception");
1800
break;
1801
case performance_metrics::TERM_UNKNOWN:
1802
default:
1803
LOGD("termination reason: unknown");
1804
break;
1805
};
1806
1807
#ifdef CV_COLLECT_IMPL_DATA
1808
if(param_collect_impl)
1809
{
1810
LOGD("impl_ipp =%11d", (int)(implConf.ipp || implConf.icv));
1811
LOGD("impl_ocl =%11d", (int)implConf.ocl);
1812
LOGD("impl_plain =%11d", (int)implConf.plain);
1813
1814
std::string rec_line;
1815
std::vector<cv::String> rec;
1816
rec_line.clear();
1817
rec = implConf.GetCallsForImpl(CV_IMPL_IPP|CV_IMPL_MT);
1818
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1819
rec = implConf.GetCallsForImpl(CV_IMPL_IPP);
1820
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1821
LOGD("impl_rec_ipp =%s", rec_line.c_str());
1822
1823
rec_line.clear();
1824
rec = implConf.GetCallsForImpl(CV_IMPL_OCL);
1825
for(int i=0; i<rec.size();i++ ){rec_line += rec[i].c_str(); rec_line += " ";}
1826
LOGD("impl_rec_ocl =%s", rec_line.c_str());
1827
}
1828
#endif
1829
1830
LOGD("bytesIn =%11lu", (unsigned long)m.bytesIn);
1831
LOGD("bytesOut =%11lu", (unsigned long)m.bytesOut);
1832
if (nIters == (unsigned int)-1 || m.terminationReason == performance_metrics::TERM_ITERATIONS)
1833
LOGD("samples =%11u", m.samples);
1834
else
1835
LOGD("samples =%11u of %u", m.samples, nIters);
1836
LOGD("outliers =%11u", m.outliers);
1837
LOGD("frequency =%11.0f", m.frequency);
1838
if (m.samples > 0)
1839
{
1840
LOGD("min =%11.0f = %.2fms", m.min, m.min * 1e3 / m.frequency);
1841
LOGD("median =%11.0f = %.2fms", m.median, m.median * 1e3 / m.frequency);
1842
LOGD("gmean =%11.0f = %.2fms", m.gmean, m.gmean * 1e3 / m.frequency);
1843
LOGD("gstddev =%11.8f = %.2fms for 97%% dispersion interval", m.gstddev, m.gmean * 2 * sinh(m.gstddev * 3) * 1e3 / m.frequency);
1844
LOGD("mean =%11.0f = %.2fms", m.mean, m.mean * 1e3 / m.frequency);
1845
LOGD("stddev =%11.0f = %.2fms", m.stddev, m.stddev * 1e3 / m.frequency);
1846
}
1847
}
1848
}
1849
1850
void TestBase::SetUp()
1851
{
1852
cv::theRNG().state = param_seed; // this rng should generate same numbers for each run
1853
1854
if (testThreads >= 0)
1855
cv::setNumThreads(testThreads);
1856
else
1857
cv::setNumThreads(-1);
1858
1859
#ifdef __ANDROID__
1860
if (param_affinity_mask)
1861
setCurrentThreadAffinityMask(param_affinity_mask);
1862
#endif
1863
1864
verified = false;
1865
lastTime = 0;
1866
totalTime = 0;
1867
runsPerIteration = 1;
1868
nIters = iterationsLimitDefault;
1869
currentIter = (unsigned int)-1;
1870
timeLimit = timeLimitDefault;
1871
times.clear();
1872
}
1873
1874
void TestBase::TearDown()
1875
{
1876
if (metrics.terminationReason == performance_metrics::TERM_SKIP_TEST)
1877
{
1878
LOGI("\tTest was skipped");
1879
GTEST_SUCCEED() << "Test was skipped";
1880
}
1881
else
1882
{
1883
if (!HasFailure() && !verified)
1884
ADD_FAILURE() << "The test has no sanity checks. There should be at least one check at the end of performance test.";
1885
1886
validateMetrics();
1887
if (HasFailure())
1888
{
1889
reportMetrics(false);
1890
1891
#ifdef ENABLE_INSTRUMENTATION
1892
if(cv::instr::useInstrumentation())
1893
InstumentData::printTree();
1894
#endif
1895
return;
1896
}
1897
}
1898
1899
#ifdef CV_COLLECT_IMPL_DATA
1900
if(param_collect_impl)
1901
{
1902
implConf.ShapeUp();
1903
printf("[ I. FLAGS ] \t");
1904
if(implConf.ipp_mt)
1905
{
1906
if(implConf.icv) {printf("ICV_MT "); std::vector<cv::String> fun = implConf.GetCallsForImpl(CV_IMPL_IPP|CV_IMPL_MT); printf("("); for(int i=0; i<fun.size();i++ ){printf("%s ", fun[i].c_str());} printf(") "); }
1907
if(implConf.ipp) {printf("IPP_MT "); std::vector<cv::String> fun = implConf.GetCallsForImpl(CV_IMPL_IPP|CV_IMPL_MT); printf("("); for(int i=0; i<fun.size();i++ ){printf("%s ", fun[i].c_str());} printf(") "); }
1908
}
1909
else
1910
{
1911
if(implConf.icv) {printf("ICV "); std::vector<cv::String> fun = implConf.GetCallsForImpl(CV_IMPL_IPP); printf("("); for(int i=0; i<fun.size();i++ ){printf("%s ", fun[i].c_str());} printf(") "); }
1912
if(implConf.ipp) {printf("IPP "); std::vector<cv::String> fun = implConf.GetCallsForImpl(CV_IMPL_IPP); printf("("); for(int i=0; i<fun.size();i++ ){printf("%s ", fun[i].c_str());} printf(") "); }
1913
}
1914
if(implConf.ocl) {printf("OCL "); std::vector<cv::String> fun = implConf.GetCallsForImpl(CV_IMPL_OCL); printf("("); for(int i=0; i<fun.size();i++ ){printf("%s ", fun[i].c_str());} printf(") "); }
1915
if(implConf.plain) printf("PLAIN ");
1916
if(!(implConf.ipp_mt || implConf.icv || implConf.ipp || implConf.ocl || implConf.plain))
1917
printf("ERROR ");
1918
printf("\n");
1919
fflush(stdout);
1920
}
1921
#endif
1922
1923
#ifdef ENABLE_INSTRUMENTATION
1924
if(cv::instr::useInstrumentation())
1925
InstumentData::printTree();
1926
#endif
1927
1928
reportMetrics(true);
1929
}
1930
1931
std::string TestBase::getDataPath(const std::string& relativePath)
1932
{
1933
if (relativePath.empty())
1934
{
1935
ADD_FAILURE() << " Bad path to test resource";
1936
throw PerfEarlyExitException();
1937
}
1938
1939
#ifndef WINRT
1940
const char *data_path_dir = getenv("OPENCV_TEST_DATA_PATH");
1941
#else
1942
const char *data_path_dir = OPENCV_TEST_DATA_PATH;
1943
#endif
1944
const char *path_separator = "/";
1945
1946
std::string path;
1947
if (data_path_dir)
1948
{
1949
int len = (int)strlen(data_path_dir) - 1;
1950
if (len < 0) len = 0;
1951
path = (data_path_dir[0] == 0 ? std::string(".") : std::string(data_path_dir))
1952
+ (data_path_dir[len] == '/' || data_path_dir[len] == '\\' ? "" : path_separator);
1953
}
1954
else
1955
{
1956
path = ".";
1957
path += path_separator;
1958
}
1959
1960
if (relativePath[0] == '/' || relativePath[0] == '\\')
1961
path += relativePath.substr(1);
1962
else
1963
path += relativePath;
1964
1965
FILE* fp = fopen(path.c_str(), "r");
1966
if (fp)
1967
fclose(fp);
1968
else
1969
{
1970
ADD_FAILURE() << " Requested file \"" << path << "\" does not exist.";
1971
throw PerfEarlyExitException();
1972
}
1973
return path;
1974
}
1975
1976
void TestBase::RunPerfTestBody()
1977
{
1978
try
1979
{
1980
#ifdef CV_COLLECT_IMPL_DATA
1981
if(param_collect_impl)
1982
implConf.Reset();
1983
#endif
1984
this->PerfTestBody();
1985
#ifdef CV_COLLECT_IMPL_DATA
1986
if(param_collect_impl)
1987
implConf.GetImpl();
1988
#endif
1989
}
1990
catch(SkipTestException&)
1991
{
1992
metrics.terminationReason = performance_metrics::TERM_SKIP_TEST;
1993
return;
1994
}
1995
catch(PerfSkipTestException&)
1996
{
1997
metrics.terminationReason = performance_metrics::TERM_SKIP_TEST;
1998
return;
1999
}
2000
catch(PerfEarlyExitException&)
2001
{
2002
metrics.terminationReason = performance_metrics::TERM_INTERRUPT;
2003
return;//no additional failure logging
2004
}
2005
catch(cv::Exception& e)
2006
{
2007
metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
2008
#ifdef HAVE_CUDA
2009
if (e.code == cv::Error::GpuApiCallError)
2010
cv::cuda::resetDevice();
2011
#endif
2012
FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n Actual: it throws cv::Exception:\n " << e.what();
2013
}
2014
catch(std::exception& e)
2015
{
2016
metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
2017
FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n Actual: it throws std::exception:\n " << e.what();
2018
}
2019
catch(...)
2020
{
2021
metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
2022
FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n Actual: it throws...";
2023
}
2024
}
2025
2026
/*****************************************************************************************\
2027
* ::perf::TestBase::_declareHelper
2028
\*****************************************************************************************/
2029
TestBase::_declareHelper& TestBase::_declareHelper::iterations(unsigned int n)
2030
{
2031
test->times.clear();
2032
test->times.reserve(n);
2033
test->nIters = std::min(n, TestBase::iterationsLimitDefault);
2034
test->currentIter = (unsigned int)-1;
2035
test->metrics.clear();
2036
return *this;
2037
}
2038
2039
TestBase::_declareHelper& TestBase::_declareHelper::time(double timeLimitSecs)
2040
{
2041
test->times.clear();
2042
test->currentIter = (unsigned int)-1;
2043
test->timeLimit = (int64)(timeLimitSecs * cv::getTickFrequency());
2044
test->metrics.clear();
2045
return *this;
2046
}
2047
2048
TestBase::_declareHelper& TestBase::_declareHelper::tbb_threads(int n)
2049
{
2050
cv::setNumThreads(n);
2051
return *this;
2052
}
2053
2054
TestBase::_declareHelper& TestBase::_declareHelper::runs(unsigned int runsNumber)
2055
{
2056
test->runsPerIteration = runsNumber;
2057
return *this;
2058
}
2059
2060
TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, WarmUpType wtype)
2061
{
2062
if (!test->times.empty()) return *this;
2063
TestBase::declareArray(test->inputData, a1, wtype);
2064
return *this;
2065
}
2066
2067
TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, WarmUpType wtype)
2068
{
2069
if (!test->times.empty()) return *this;
2070
TestBase::declareArray(test->inputData, a1, wtype);
2071
TestBase::declareArray(test->inputData, a2, wtype);
2072
return *this;
2073
}
2074
2075
TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, WarmUpType wtype)
2076
{
2077
if (!test->times.empty()) return *this;
2078
TestBase::declareArray(test->inputData, a1, wtype);
2079
TestBase::declareArray(test->inputData, a2, wtype);
2080
TestBase::declareArray(test->inputData, a3, wtype);
2081
return *this;
2082
}
2083
2084
TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, cv::InputOutputArray a4, WarmUpType wtype)
2085
{
2086
if (!test->times.empty()) return *this;
2087
TestBase::declareArray(test->inputData, a1, wtype);
2088
TestBase::declareArray(test->inputData, a2, wtype);
2089
TestBase::declareArray(test->inputData, a3, wtype);
2090
TestBase::declareArray(test->inputData, a4, wtype);
2091
return *this;
2092
}
2093
2094
TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, WarmUpType wtype)
2095
{
2096
if (!test->times.empty()) return *this;
2097
TestBase::declareArray(test->outputData, a1, wtype);
2098
return *this;
2099
}
2100
2101
TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, WarmUpType wtype)
2102
{
2103
if (!test->times.empty()) return *this;
2104
TestBase::declareArray(test->outputData, a1, wtype);
2105
TestBase::declareArray(test->outputData, a2, wtype);
2106
return *this;
2107
}
2108
2109
TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, WarmUpType wtype)
2110
{
2111
if (!test->times.empty()) return *this;
2112
TestBase::declareArray(test->outputData, a1, wtype);
2113
TestBase::declareArray(test->outputData, a2, wtype);
2114
TestBase::declareArray(test->outputData, a3, wtype);
2115
return *this;
2116
}
2117
2118
TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, cv::InputOutputArray a4, WarmUpType wtype)
2119
{
2120
if (!test->times.empty()) return *this;
2121
TestBase::declareArray(test->outputData, a1, wtype);
2122
TestBase::declareArray(test->outputData, a2, wtype);
2123
TestBase::declareArray(test->outputData, a3, wtype);
2124
TestBase::declareArray(test->outputData, a4, wtype);
2125
return *this;
2126
}
2127
2128
TestBase::_declareHelper& TestBase::_declareHelper::strategy(enum PERF_STRATEGY s)
2129
{
2130
test->testStrategy = s;
2131
return *this;
2132
}
2133
2134
TestBase::_declareHelper::_declareHelper(TestBase* t) : test(t)
2135
{
2136
}
2137
2138
/*****************************************************************************************\
2139
* miscellaneous
2140
\*****************************************************************************************/
2141
2142
namespace {
2143
struct KeypointComparator
2144
{
2145
std::vector<cv::KeyPoint>& pts_;
2146
comparators::KeypointGreater cmp;
2147
2148
KeypointComparator(std::vector<cv::KeyPoint>& pts) : pts_(pts), cmp() {}
2149
2150
bool operator()(int idx1, int idx2) const
2151
{
2152
return cmp(pts_[idx1], pts_[idx2]);
2153
}
2154
private:
2155
const KeypointComparator& operator=(const KeypointComparator&); // quiet MSVC
2156
};
2157
}//namespace
2158
2159
void perf::sort(std::vector<cv::KeyPoint>& pts, cv::InputOutputArray descriptors)
2160
{
2161
cv::Mat desc = descriptors.getMat();
2162
2163
CV_Assert(pts.size() == (size_t)desc.rows);
2164
cv::AutoBuffer<int> idxs(desc.rows);
2165
2166
for (int i = 0; i < desc.rows; ++i)
2167
idxs[i] = i;
2168
2169
std::sort(idxs.data(), idxs.data() + desc.rows, KeypointComparator(pts));
2170
2171
std::vector<cv::KeyPoint> spts(pts.size());
2172
cv::Mat sdesc(desc.size(), desc.type());
2173
2174
for(int j = 0; j < desc.rows; ++j)
2175
{
2176
spts[j] = pts[idxs[j]];
2177
cv::Mat row = sdesc.row(j);
2178
desc.row(idxs[j]).copyTo(row);
2179
}
2180
2181
spts.swap(pts);
2182
sdesc.copyTo(desc);
2183
}
2184
2185
/*****************************************************************************************\
2186
* ::perf::GpuPerf
2187
\*****************************************************************************************/
2188
bool perf::GpuPerf::targetDevice()
2189
{
2190
return param_impl == "cuda";
2191
}
2192
2193
/*****************************************************************************************\
2194
* ::perf::PrintTo
2195
\*****************************************************************************************/
2196
namespace perf
2197
{
2198
2199
void PrintTo(const MatType& t, ::std::ostream* os)
2200
{
2201
String name = typeToString(t);
2202
if (name.size() > 3 && name[0] == 'C' && name[1] == 'V' && name[2] == '_')
2203
*os << name.substr(3);
2204
else
2205
*os << name;
2206
}
2207
2208
} //namespace perf
2209
2210
/*****************************************************************************************\
2211
* ::cv::PrintTo
2212
\*****************************************************************************************/
2213
namespace cv {
2214
2215
void PrintTo(const String& str, ::std::ostream* os)
2216
{
2217
*os << "\"" << str << "\"";
2218
}
2219
2220
void PrintTo(const Size& sz, ::std::ostream* os)
2221
{
2222
*os << /*"Size:" << */sz.width << "x" << sz.height;
2223
}
2224
2225
} // namespace cv
2226
2227