core: parallel_for_(): update RNG state of the main thread
authorAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 28 Feb 2017 15:21:44 +0000 (18:21 +0300)
committerAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 28 Feb 2017 15:28:15 +0000 (18:28 +0300)
modules/core/include/opencv2/core.hpp
modules/core/include/opencv2/core/operations.hpp
modules/core/src/parallel.cpp

index b6c1c79..75b5cc5 100644 (file)
@@ -2834,6 +2834,8 @@ public:
     double gaussian(double sigma);
 
     uint64 state;
+
+    bool operator ==(const RNG& other) const;
 };
 
 /** @brief Mersenne Twister random number generator
index f69790f..9858d06 100644 (file)
@@ -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);
index 0d3a93a..cf31055 100644 (file)
@@ -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