Resolve Qt6 TODO items, replace Median and BlockSizeManager
* Replaces the, only internaly used, implementation of template class Median with a fixed size none templated version. * Replaces BlockSizeManager with an updated BlockSizeManager V2, but keeping the original name. * adapt the auto-test to take the fixed size array into account Change-Id: If76cb944676c4a06a7566ad0bc37ded25b81c70c Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
parent
9ad8b80fb9
commit
b75c82f645
@ -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
|
||||
|
@ -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<double> controlPartElapsed;
|
||||
Median<double> 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 <typename T>
|
||||
class ResultReporter
|
||||
{
|
||||
@ -221,7 +197,7 @@ public:
|
||||
|
||||
ThreadFunctionResult forThreadFunction()
|
||||
{
|
||||
BlockSizeManagerV2 blockSizeManager(iterationCount);
|
||||
BlockSizeManager blockSizeManager(iterationCount);
|
||||
ResultReporter<T> resultReporter(this);
|
||||
|
||||
for(;;) {
|
||||
|
@ -42,97 +42,21 @@
|
||||
|
||||
#include <QtConcurrent/qtconcurrent_global.h>
|
||||
|
||||
#if !defined(QT_NO_CONCURRENT) ||defined(Q_CLANG_QDOC)
|
||||
|
||||
#include <QtCore/qvector.h>
|
||||
#if !defined(QT_NO_CONCURRENT) || defined(Q_CLANG_QDOC)
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
|
||||
namespace QtConcurrent {
|
||||
|
||||
template <typename T>
|
||||
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<T> sorted = values;
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
currentMedian = sorted.at(bufferSize / 2);
|
||||
#else
|
||||
QVector<T> copy = values;
|
||||
typename QVector<T>::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<T> values;
|
||||
T currentMedian;
|
||||
int bufferSize;
|
||||
int currentIndex;
|
||||
bool valid;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
// ### Qt6: Drop Median<double> 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<int>(BufferSize), 0.0);
|
||||
@ -195,7 +119,6 @@ private:
|
||||
|
||||
} // namespace QtConcurrent
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_CONCURRENT
|
||||
|
@ -39,33 +39,45 @@ private slots:
|
||||
|
||||
void tst_QtConcurrentMedian::median_data()
|
||||
{
|
||||
QTest::addColumn<QList<int> >("values");
|
||||
QTest::addColumn<int>("expectedMedian");
|
||||
QTest::addColumn<QList<double> >("values");
|
||||
QTest::addColumn<double>("expectedMedian");
|
||||
|
||||
QTest::newRow("size=1")
|
||||
<< (QList<int>() << 1)
|
||||
<< 1;
|
||||
<< (QList<double>() << 1.0)
|
||||
<< 0.0; // six 0.0 in front of the actual value
|
||||
|
||||
QTest::newRow("size=2")
|
||||
<< (QList<int>() << 3 << 2)
|
||||
<< 3;
|
||||
<< (QList<double>() << 3.0 << 2.0)
|
||||
<< 0.0; // five 0.0 in front of the actual value
|
||||
|
||||
QTest::newRow("size=3")
|
||||
<< (QList<int>() << 3 << 1 << 2)
|
||||
<< 2;
|
||||
<< (QList<double>() << 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<int>() << 207089 << 202585 << 180067 << 157549 << 211592 << 216096 << 207089)
|
||||
<< 207089;
|
||||
QTest::newRow("size=4")
|
||||
<< (QList<double>() << 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<double>() << 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<double>() << 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<double> { 207089.0, 202585.0, 180067.0, 157549.0, 211592.0, 216096.0, 207089.0 }
|
||||
<< 207089.0;
|
||||
}
|
||||
|
||||
void tst_QtConcurrentMedian::median()
|
||||
{
|
||||
QFETCH(QList<int> , values);
|
||||
QFETCH(int, expectedMedian);
|
||||
QFETCH(QList<double> , values);
|
||||
QFETCH(double, expectedMedian);
|
||||
|
||||
QtConcurrent::Median<int> m(values.size());
|
||||
foreach (int value, values)
|
||||
QtConcurrent::Median m;
|
||||
foreach (double value, values)
|
||||
m.addValue(value);
|
||||
QCOMPARE(m.median(), expectedMedian);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user