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>
This commit is contained in:
parent
c3850dd636
commit
24d851dcd2
@ -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
|
||||
|
@ -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(;;) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user