diff options
author | Tobias Koenig <tobias.koenig@kdab.com> | 2016-01-19 17:45:42 +0100 |
---|---|---|
committer | Tobias Koenig <tobias.koenig@kdab.com> | 2016-01-21 18:57:04 +0000 |
commit | 24d851dcd2a140bf346343c5eb64d944cd1c1a87 (patch) | |
tree | c298808e3d9b41fe36a88106bc957d5b6e1910aa /src/concurrent | |
parent | c3850dd636ab24c251942fde63f22d8f5b3a639e (diff) |
Avoid heap allocations in Median class
Create a MedianDouble class and V2 version of BlockSizeManager, which
use a fixed size array of double (since we always use 7 elements
to calculate the median anyway).
Change-Id: Ife90b90336a9a8c037b90726dee4cd2a1b8b6cd9
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
Diffstat (limited to 'src/concurrent')
-rw-r--r-- | src/concurrent/qtconcurrentiteratekernel.cpp | 52 | ||||
-rw-r--r-- | src/concurrent/qtconcurrentiteratekernel.h | 28 | ||||
-rw-r--r-- | src/concurrent/qtconcurrentmedian.h | 66 |
3 files changed, 145 insertions, 1 deletions
diff --git a/src/concurrent/qtconcurrentiteratekernel.cpp b/src/concurrent/qtconcurrentiteratekernel.cpp index 49056406c3..3b9b186b39 100644 --- a/src/concurrent/qtconcurrentiteratekernel.cpp +++ b/src/concurrent/qtconcurrentiteratekernel.cpp @@ -182,6 +182,58 @@ 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 32acf03338..40e2c6c115 100644 --- a/src/concurrent/qtconcurrentiteratekernel.h +++ b/src/concurrent/qtconcurrentiteratekernel.h @@ -82,6 +82,32 @@ private: 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 <typename T> class ResultReporter { @@ -190,7 +216,7 @@ public: ThreadFunctionResult forThreadFunction() { - BlockSizeManager blockSizeManager(iterationCount); + BlockSizeManagerV2 blockSizeManager(iterationCount); ResultReporter<T> resultReporter(this); for(;;) { diff --git a/src/concurrent/qtconcurrentmedian.h b/src/concurrent/qtconcurrentmedian.h index 2ae7954b7a..e35e2aec70 100644 --- a/src/concurrent/qtconcurrentmedian.h +++ b/src/concurrent/qtconcurrentmedian.h @@ -121,6 +121,72 @@ private: bool dirty; }; +// ### Qt6: Drop Median<double> in favor of this faster MedianDouble +class MedianDouble +{ +public: + enum { BufferSize = 7 }; + + MedianDouble() + : currentMedian(), currentIndex(0), valid(false), dirty(true) + { + } + + void reset() + { + std::fill_n(values, static_cast<int>(BufferSize), 0.0); + currentIndex = 0; + valid = false; + dirty = true; + } + + void addValue(double value) + { + ++currentIndex; + if (currentIndex == BufferSize) { + currentIndex = 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 double currentIndexValue = values[currentIndex]; + if ((currentIndexValue > currentMedian && currentMedian > value) + || (currentMedian > currentIndexValue && value > currentMedian)) { + dirty = true; + } + + values[currentIndex] = value; + } + + bool isMedianValid() const + { + return valid; + } + + double median() + { + if (dirty) { + dirty = false; + + double sorted[BufferSize]; + ::memcpy(&sorted, &values, sizeof(sorted)); + std::sort(sorted, sorted + static_cast<int>(BufferSize)); + currentMedian = sorted[BufferSize / 2]; + } + + return currentMedian; + } + +private: + double values[BufferSize]; + double currentMedian; + int currentIndex; + bool valid; + bool dirty; +}; + } // namespace QtConcurrent #endif //Q_QDOC |