Change the sort and merge functions to a more generic form.

This might make it easier to reuse the parallel sorting code. Moreover,
some the upperBound/lowerBound functions have been removed because
equivalents are provided by the STL.
This commit is contained in:
Frank Reininghaus 2013-01-15 18:47:00 +01:00
parent c652807a19
commit 87ac18f031
3 changed files with 74 additions and 112 deletions

View file

@ -476,7 +476,8 @@ private:
// and done step after step in slotCompleted(). // and done step after step in slotCompleted().
QSet<KUrl> m_urlsToExpand; QSet<KUrl> m_urlsToExpand;
friend class KFileItemModelSortAlgorithm; // Accesses lessThan() method friend class KFileItemModelLessThan; // Accesses lessThan() method
friend class KFileItemModelSortAlgorithm; // Accesses NameRole
friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method friend class KFileItemModelRolesUpdater; // Accesses emitSortProgress() method
friend class KFileItemModelTest; // For unit testing friend class KFileItemModelTest; // For unit testing
friend class KFileItemListViewTest; // For unit testing friend class KFileItemListViewTest; // For unit testing

View file

@ -24,26 +24,46 @@
#include <algorithm> #include <algorithm>
class KFileItemModelLessThan
{
public:
KFileItemModelLessThan(KFileItemModel* model) :
m_model(model)
{
}
bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b)
{
return m_model->lessThan(a, b);
}
private:
KFileItemModel* m_model;
};
void KFileItemModelSortAlgorithm::sort(KFileItemModel* model, void KFileItemModelSortAlgorithm::sort(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin, QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end) QList<KFileItemModel::ItemData*>::iterator end)
{ {
KFileItemModelLessThan lessThan(model);
if (model->sortRole() == model->roleForType(KFileItemModel::NameRole)) { if (model->sortRole() == model->roleForType(KFileItemModel::NameRole)) {
// Sorting by name can be expensive, in particular if natural sorting is // Sorting by name can be expensive, in particular if natural sorting is
// enabled. Use all CPU cores to speed up the sorting process. // enabled. Use all CPU cores to speed up the sorting process.
static const int numberOfThreads = QThread::idealThreadCount(); static const int numberOfThreads = QThread::idealThreadCount();
parallelSort(model, begin, end, numberOfThreads); parallelSort(begin, end, lessThan, numberOfThreads);
} else { } else {
// Sorting by other roles is quite fast. Use only one thread to prevent // Sorting by other roles is quite fast. Use only one thread to prevent
// problems caused by non-reentrant comparison functions, see // problems caused by non-reentrant comparison functions, see
// https://bugs.kde.org/show_bug.cgi?id=312679 // https://bugs.kde.org/show_bug.cgi?id=312679
sequentialSort(model, begin, end); sequentialSort(begin, end, lessThan);
} }
} }
void KFileItemModelSortAlgorithm::sequentialSort(KFileItemModel* model, template <typename RandomAccessIterator, typename LessThan>
QList< KFileItemModel::ItemData* >::iterator begin, static void sequentialSort(RandomAccessIterator begin,
QList< KFileItemModel::ItemData* >::iterator end) RandomAccessIterator end,
LessThan lessThan)
{ {
// The implementation is based on qStableSortHelper() from qalgorithms.h // The implementation is based on qStableSortHelper() from qalgorithms.h
// Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
@ -53,38 +73,41 @@ void KFileItemModelSortAlgorithm::sequentialSort(KFileItemModel* model,
return; return;
} }
const QList<KFileItemModel::ItemData*>::iterator middle = begin + span / 2; const RandomAccessIterator middle = begin + span / 2;
sequentialSort(model, begin, middle); sequentialSort(begin, middle, lessThan);
sequentialSort(model, middle, end); sequentialSort(middle, end, lessThan);
merge(model, begin, middle, end); merge(begin, middle, end, lessThan);
} }
void KFileItemModelSortAlgorithm::parallelSort(KFileItemModel* model, template <typename RandomAccessIterator, typename LessThan>
QList< KFileItemModel::ItemData* >::iterator begin, static void parallelSort(RandomAccessIterator begin,
QList< KFileItemModel::ItemData* >::iterator end, RandomAccessIterator end,
const int numberOfThreads) LessThan lessThan,
int numberOfThreads,
int parallelSortingThreshold)
{ {
const int span = end - begin; const int span = end - begin;
if (numberOfThreads > 1 && span > 100) { if (numberOfThreads > 1 && span > parallelSortingThreshold) {
const int newNumberOfThreads = numberOfThreads / 2; const int newNumberOfThreads = numberOfThreads / 2;
const QList<KFileItemModel::ItemData*>::iterator middle = begin + span / 2; const RandomAccessIterator middle = begin + span / 2;
QFuture<void> future = QtConcurrent::run(parallelSort, model, begin, middle, newNumberOfThreads); QFuture<void> future = QtConcurrent::run(parallelSort<RandomAccessIterator, LessThan>, begin, middle, lessThan, newNumberOfThreads, parallelSortingThreshold);
parallelSort(model, middle, end, newNumberOfThreads); parallelSort(middle, end, lessThan, newNumberOfThreads, parallelSortingThreshold);
future.waitForFinished(); future.waitForFinished();
merge(model, begin, middle, end); merge(begin, middle, end, lessThan);
} else { } else {
sequentialSort(model, begin, end); sequentialSort(begin, end, lessThan);
} }
} }
void KFileItemModelSortAlgorithm::merge(KFileItemModel* model, template <typename RandomAccessIterator, typename LessThan>
QList<KFileItemModel::ItemData*>::iterator begin, static void merge(RandomAccessIterator begin,
QList<KFileItemModel::ItemData*>::iterator pivot, RandomAccessIterator pivot,
QList<KFileItemModel::ItemData*>::iterator end) RandomAccessIterator end,
LessThan lessThan)
{ {
// The implementation is based on qMerge() from qalgorithms.h // The implementation is based on qMerge() from qalgorithms.h
// Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). // Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
@ -97,82 +120,29 @@ void KFileItemModelSortAlgorithm::merge(KFileItemModel* model,
} }
if (len1 + len2 == 2) { if (len1 + len2 == 2) {
if (model->lessThan(*(begin + 1), *(begin))) { if (lessThan(*(begin + 1), *(begin))) {
qSwap(*begin, *(begin + 1)); qSwap(*begin, *(begin + 1));
} }
return; return;
} }
QList<KFileItemModel::ItemData*>::iterator firstCut; RandomAccessIterator firstCut;
QList<KFileItemModel::ItemData*>::iterator secondCut; RandomAccessIterator secondCut;
int len2Half; int len2Half;
if (len1 > len2) { if (len1 > len2) {
const int len1Half = len1 / 2; const int len1Half = len1 / 2;
firstCut = begin + len1Half; firstCut = begin + len1Half;
secondCut = lowerBound(model, pivot, end, *firstCut); secondCut = std::lower_bound(pivot, end, *firstCut, lessThan);
len2Half = secondCut - pivot; len2Half = secondCut - pivot;
} else { } else {
len2Half = len2 / 2; len2Half = len2 / 2;
secondCut = pivot + len2Half; secondCut = pivot + len2Half;
firstCut = upperBound(model, begin, pivot, *secondCut); firstCut = std::upper_bound(begin, pivot, *secondCut, lessThan);
} }
std::rotate(firstCut, pivot, secondCut); std::rotate(firstCut, pivot, secondCut);
const QList<KFileItemModel::ItemData*>::iterator newPivot = firstCut + len2Half; RandomAccessIterator newPivot = firstCut + len2Half;
merge(model, begin, firstCut, newPivot); merge(begin, firstCut, newPivot, lessThan);
merge(model, newPivot, secondCut, end); merge(newPivot, secondCut, end, lessThan);
}
QList<KFileItemModel::ItemData*>::iterator
KFileItemModelSortAlgorithm::lowerBound(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end,
const KFileItemModel::ItemData* value)
{
// The implementation is based on qLowerBound() from qalgorithms.h
// Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
QList<KFileItemModel::ItemData*>::iterator middle;
int n = int(end - begin);
int half;
while (n > 0) {
half = n >> 1;
middle = begin + half;
if (model->lessThan(*middle, value)) {
begin = middle + 1;
n -= half + 1;
} else {
n = half;
}
}
return begin;
}
QList<KFileItemModel::ItemData*>::iterator
KFileItemModelSortAlgorithm::upperBound(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end,
const KFileItemModel::ItemData* value)
{
// The implementation is based on qUpperBound() from qalgorithms.h
// Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
QList<KFileItemModel::ItemData*>::iterator middle;
int n = end - begin;
int half;
while (n > 0) {
half = n >> 1;
middle = begin + half;
if (model->lessThan(value, *middle)) {
n = half;
} else {
begin = middle + 1;
n -= half + 1;
}
}
return begin;
} }

View file

@ -42,35 +42,26 @@ public:
static void sort(KFileItemModel* model, static void sort(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin, QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end); QList<KFileItemModel::ItemData*>::iterator end);
private:
static void sequentialSort(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end);
static void parallelSort(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end,
const int numberOfThreads);
static void merge(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator pivot,
QList<KFileItemModel::ItemData*>::iterator end);
static QList<KFileItemModel::ItemData*>::iterator
lowerBound(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end,
const KFileItemModel::ItemData* value);
static QList<KFileItemModel::ItemData*>::iterator
upperBound(KFileItemModel* model,
QList<KFileItemModel::ItemData*>::iterator begin,
QList<KFileItemModel::ItemData*>::iterator end,
const KFileItemModel::ItemData* value);
}; };
template <typename RandomAccessIterator, typename LessThan>
static void sequentialSort(RandomAccessIterator begin,
RandomAccessIterator end,
LessThan lessThan);
template <typename RandomAccessIterator, typename LessThan>
static void parallelSort(RandomAccessIterator begin,
RandomAccessIterator end,
LessThan lessThan,
int numberOfThreads,
int parallelSortingThreshold = 100);
template <typename RandomAccessIterator, typename LessThan>
static void merge(RandomAccessIterator begin,
RandomAccessIterator pivot,
RandomAccessIterator end,
LessThan lessThan);
#endif #endif