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);
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;
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
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); }
cv::Range wholeRange;
int nstripes;
cv::RNG rng;
+ mutable bool is_rng_used;
#ifdef ENABLE_INSTRUMENTATION
cv::instr::InstrNode *pThreadRoot;
#endif