From 649bb7ac04bf54ffce2cdecb42a64b3c019fbe94 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 28 Feb 2017 18:21:44 +0300 Subject: [PATCH] core: parallel_for_(): update RNG state of the main thread --- modules/core/include/opencv2/core.hpp | 2 ++ .../core/include/opencv2/core/operations.hpp | 2 ++ modules/core/src/parallel.cpp | 21 ++++++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index b6c1c7942c..75b5cc5d57 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -2834,6 +2834,8 @@ public: double gaussian(double sigma); uint64 state; + + bool operator ==(const RNG& other) const; }; /** @brief Mersenne Twister random number generator diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index f69790f069..9858d067f6 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -349,6 +349,8 @@ inline int RNG::uniform(int a, int b) { return a == b ? a : (int)(next( inline float RNG::uniform(float a, float b) { return ((float)*this)*(b - a) + a; } inline double RNG::uniform(double a, double b) { return ((double)*this)*(b - a) + a; } +inline bool RNG::operator ==(const RNG& other) const { return state == other.state; } + inline unsigned RNG::next() { state = (uint64)(unsigned)state* /*CV_RNG_COEFF*/ 4164903690U + (unsigned)(state >> 32); diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 0d3a93a085..cf31055c7f 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -166,7 +166,8 @@ namespace class ParallelLoopBodyWrapper : public cv::ParallelLoopBody { public: - ParallelLoopBodyWrapper(const cv::ParallelLoopBody& _body, const cv::Range& _r, double _nstripes) + ParallelLoopBodyWrapper(const cv::ParallelLoopBody& _body, const cv::Range& _r, double _nstripes) : + is_rng_used(false) { body = &_body; @@ -181,13 +182,23 @@ namespace pThreadRoot = cv::instr::getInstrumentTLSStruct().pCurrentNode; #endif } -#ifdef ENABLE_INSTRUMENTATION ~ParallelLoopBodyWrapper() { +#ifdef ENABLE_INSTRUMENTATION for(size_t i = 0; i < pThreadRoot->m_childs.size(); i++) SyncNodes(pThreadRoot->m_childs[i]); - } #endif + if (is_rng_used) + { + // Some parallel backends execute nested jobs in the main thread, + // so we need to restore initial RNG state here. + cv::theRNG() = rng; + // We can't properly update RNG state based on RNG usage in worker threads, + // so lets just change main thread RNG state to the next value. + // Note: this behaviour is not equal to single-threaded mode. + cv::theRNG().next(); + } + } void operator()(const cv::Range& sr) const { #ifdef ENABLE_INSTRUMENTATION @@ -207,6 +218,9 @@ namespace r.end = sr.end >= nstripes ? wholeRange.end : (int)(wholeRange.start + ((uint64)sr.end*(wholeRange.end - wholeRange.start) + nstripes/2)/nstripes); (*body)(r); + + if (!is_rng_used && !(cv::theRNG() == rng)) + is_rng_used = true; } cv::Range stripeRange() const { return cv::Range(0, nstripes); } @@ -215,6 +229,7 @@ namespace cv::Range wholeRange; int nstripes; cv::RNG rng; + mutable bool is_rng_used; #ifdef ENABLE_INSTRUMENTATION cv::instr::InstrNode *pThreadRoot; #endif