diff --git a/src/concurrent/qtconcurrentiteratekernel.cpp b/src/concurrent/qtconcurrentiteratekernel.cpp index b65f7125475..45b54ecfdc4 100644 --- a/src/concurrent/qtconcurrentiteratekernel.cpp +++ b/src/concurrent/qtconcurrentiteratekernel.cpp @@ -48,8 +48,7 @@ QT_BEGIN_NAMESPACE enum { - TargetRatio = 100, - MedianSize = 7 + TargetRatio = 100 }; static qint64 getticks() @@ -70,24 +69,12 @@ namespace QtConcurrent { \internal */ -/*! - \class QtConcurrent::MedianDouble - \inmodule QtConcurrent - \internal - */ - /*! \class QtConcurrent::BlockSizeManager \inmodule QtConcurrent \internal */ -/*! - \class QtConcurrent::BlockSizeManagerV2 - \inmodule QtConcurrent - \internal - */ - /*! \class QtConcurrent::ResultReporter \inmodule QtConcurrent @@ -116,10 +103,9 @@ namespace QtConcurrent { */ BlockSizeManager::BlockSizeManager(int iterationCount) -: maxBlockSize(iterationCount / (QThreadPool::globalInstance()->maxThreadCount() * 2)), - beforeUser(0), afterUser(0), - controlPartElapsed(MedianSize), userPartElapsed(MedianSize), - m_blockSize(1) + : maxBlockSize(iterationCount / (QThreadPool::globalInstance()->maxThreadCount() * 2)), + beforeUser(0), afterUser(0), + m_blockSize(1) { } // Records the time before user code. @@ -165,58 +151,6 @@ int BlockSizeManager::blockSize() return m_blockSize; } -/*! \internal - -*/ -BlockSizeManagerV2::BlockSizeManagerV2(int iterationCount) - : maxBlockSize(iterationCount / (QThreadPool::globalInstance()->maxThreadCount() * 2)), - beforeUser(0), afterUser(0), - m_blockSize(1) -{ } - -// Records the time before user code. -void BlockSizeManagerV2::timeBeforeUser() -{ - if (blockSizeMaxed()) - return; - - beforeUser = getticks(); - controlPartElapsed.addValue(elapsed(beforeUser, afterUser)); -} - - // Records the time after user code and adjust the block size if we are spending - // to much time in the for control code compared with the user code. -void BlockSizeManagerV2::timeAfterUser() -{ - if (blockSizeMaxed()) - return; - - afterUser = getticks(); - userPartElapsed.addValue(elapsed(afterUser, beforeUser)); - - if (controlPartElapsed.isMedianValid() == false) - return; - - if (controlPartElapsed.median() * TargetRatio < userPartElapsed.median()) - return; - - m_blockSize = qMin(m_blockSize * 2, maxBlockSize); - -#ifdef QTCONCURRENT_FOR_DEBUG - qDebug() << QThread::currentThread() << "adjusting block size" << controlPartElapsed.median() << userPartElapsed.median() << m_blockSize; -#endif - - // Reset the medians after adjusting the block size so we get - // new measurements with the new block size. - controlPartElapsed.reset(); - userPartElapsed.reset(); -} - -int BlockSizeManagerV2::blockSize() -{ - return m_blockSize; -} - } // namespace QtConcurrent QT_END_NAMESPACE diff --git a/src/concurrent/qtconcurrentiteratekernel.h b/src/concurrent/qtconcurrentiteratekernel.h index 3095c9ff52e..09009efedcc 100644 --- a/src/concurrent/qtconcurrentiteratekernel.h +++ b/src/concurrent/qtconcurrentiteratekernel.h @@ -61,16 +61,18 @@ namespace QtConcurrent { reserve and process at a time. This is done by measuring the time spent in the user code versus the control part code, and then increasing the block size if the ratio between them is to small. The block size - management is done on the basis of the median of several timing measuremens, - and it is done induvidualy for each thread. + management is done on the basis of the median of several timing measurements, + and it is done individually for each thread. */ class Q_CONCURRENT_EXPORT BlockSizeManager { public: - BlockSizeManager(int iterationCount); + explicit BlockSizeManager(int iterationCount); + void timeBeforeUser(); void timeAfterUser(); int blockSize(); + private: inline bool blockSizeMaxed() { @@ -80,39 +82,13 @@ private: const int maxBlockSize; qint64 beforeUser; qint64 afterUser; - Median controlPartElapsed; - Median userPartElapsed; + Median controlPartElapsed; + Median userPartElapsed; int m_blockSize; Q_DISABLE_COPY(BlockSizeManager) }; -// ### Qt6: Replace BlockSizeManager with V2 implementation -class Q_CONCURRENT_EXPORT BlockSizeManagerV2 -{ -public: - explicit BlockSizeManagerV2(int iterationCount); - - void timeBeforeUser(); - void timeAfterUser(); - int blockSize(); - -private: - inline bool blockSizeMaxed() - { - return (m_blockSize >= maxBlockSize); - } - - const int maxBlockSize; - qint64 beforeUser; - qint64 afterUser; - MedianDouble controlPartElapsed; - MedianDouble userPartElapsed; - int m_blockSize; - - Q_DISABLE_COPY(BlockSizeManagerV2) -}; - template class ResultReporter { @@ -221,7 +197,7 @@ public: ThreadFunctionResult forThreadFunction() { - BlockSizeManagerV2 blockSizeManager(iterationCount); + BlockSizeManager blockSizeManager(iterationCount); ResultReporter resultReporter(this); for(;;) { diff --git a/src/concurrent/qtconcurrentmedian.h b/src/concurrent/qtconcurrentmedian.h index cec2431d6f9..aab80794d94 100644 --- a/src/concurrent/qtconcurrentmedian.h +++ b/src/concurrent/qtconcurrentmedian.h @@ -42,97 +42,21 @@ #include -#if !defined(QT_NO_CONCURRENT) ||defined(Q_CLANG_QDOC) - -#include +#if !defined(QT_NO_CONCURRENT) || defined(Q_CLANG_QDOC) #include +#include QT_BEGIN_NAMESPACE - - namespace QtConcurrent { -template class Median { -public: - Median(int _bufferSize) - : currentMedian(), bufferSize(_bufferSize), currentIndex(0), valid(false), dirty(true) - { - values.resize(bufferSize); - } - - void reset() - { - values.fill(0); - currentIndex = 0; - valid = false; - dirty = true; - } - - void addValue(T value) - { - currentIndex = ((currentIndex + 1) % bufferSize); - if (valid == false && currentIndex % bufferSize == 0) - valid = true; - - // Only update the cached median value when we have to, that - // is when the new value is on then other side of the median - // compared to the current value at the index. - const T currentIndexValue = values[currentIndex]; - if ((currentIndexValue > currentMedian && currentMedian > value) - || (currentMedian > currentIndexValue && value > currentMedian)) { - dirty = true; - } - - values[currentIndex] = value; - } - - bool isMedianValid() const - { - return valid; - } - - T median() - { - if (dirty) { - dirty = false; - -// This is a workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58800 -// Avoid using std::nth_element for the affected stdlibc++ releases 4.7.3 and 4.8.2. -// Note that the official __GLIBCXX__ value of the releases is not used since that -// one might be patched on some GNU/Linux distributions. -#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20140107 - QVector sorted = values; - std::sort(sorted.begin(), sorted.end()); - currentMedian = sorted.at(bufferSize / 2); -#else - QVector copy = values; - typename QVector::iterator begin = copy.begin(), mid = copy.begin() + bufferSize/2, end = copy.end(); - std::nth_element(begin, mid, end); - currentMedian = *mid; -#endif - } - return currentMedian; - } -private: - QVector values; - T currentMedian; - int bufferSize; - int currentIndex; - bool valid; - bool dirty; -}; - -// ### Qt6: Drop Median in favor of this faster MedianDouble -class MedianDouble -{ public: enum { BufferSize = 7 }; - MedianDouble() + Median() : currentMedian(), currentIndex(0), valid(false), dirty(true) { std::fill_n(values, static_cast(BufferSize), 0.0); @@ -195,7 +119,6 @@ private: } // namespace QtConcurrent - QT_END_NAMESPACE #endif // QT_NO_CONCURRENT diff --git a/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp b/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp index f5ae80dca59..22e555d2db8 100644 --- a/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp +++ b/tests/auto/concurrent/qtconcurrentmedian/tst_qtconcurrentmedian.cpp @@ -39,33 +39,45 @@ private slots: void tst_QtConcurrentMedian::median_data() { - QTest::addColumn >("values"); - QTest::addColumn("expectedMedian"); + QTest::addColumn >("values"); + QTest::addColumn("expectedMedian"); QTest::newRow("size=1") - << (QList() << 1) - << 1; + << (QList() << 1.0) + << 0.0; // six 0.0 in front of the actual value QTest::newRow("size=2") - << (QList() << 3 << 2) - << 3; + << (QList() << 3.0 << 2.0) + << 0.0; // five 0.0 in front of the actual value QTest::newRow("size=3") - << (QList() << 3 << 1 << 2) - << 2; + << (QList() << 3.0 << 1.0 << 2.0) + << 0.0; // four 0.0 in front of the actual value - QTest::newRow("gcc bug 58800 (nth_element)") - << (QList() << 207089 << 202585 << 180067 << 157549 << 211592 << 216096 << 207089) - << 207089; + QTest::newRow("size=4") + << (QList() << 3.0 << 1.0 << 2.0 << 4.0) + << 1.0; // three 0.0 in front of the first actual value, pick 1.0 + + QTest::newRow("size=5") + << (QList() << 3.0 << 1.0 << 2.0 << 3.0 << 1.0) + << 1.0; // two 0.0 in front of the first actual value, pick 1.0 + + QTest::newRow("size=6") + << (QList() << 3.0 << 1.0 << 2.0 << 3.0 << 1.0 << 2.0) + << 2.0; // one 0.0 in front of the first actual value, pick 2.0 + + QTest::newRow("size=7") + << QList { 207089.0, 202585.0, 180067.0, 157549.0, 211592.0, 216096.0, 207089.0 } + << 207089.0; } void tst_QtConcurrentMedian::median() { - QFETCH(QList , values); - QFETCH(int, expectedMedian); + QFETCH(QList , values); + QFETCH(double, expectedMedian); - QtConcurrent::Median m(values.size()); - foreach (int value, values) + QtConcurrent::Median m; + foreach (double value, values) m.addValue(value); QCOMPARE(m.median(), expectedMedian); }