Rewrite QMap to use a RB tree

QMap used to use a skiplist in Qt 4.x, which has variable
sized nodes and we can thus not optimise using custom
allocators.

The rewrite now uses a red-black tree, and all allocations
and tree operations happen in the cpp file. This will allow
us to introduce custom allocation schemes in later versions
of Qt.

Added some more tests and a benchmark. Memory consumption
of the new QMap implementation is pretty much the same as before.
Performance of insertion and lookup has increased by 10-30%. iteration
is slower, but still extremely fast and should not matter compared
to the work usually done when iterating.

Change-Id: I8796c0e4b207d01111e2ead7ae55afb464dd88f5
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Lars Knoll 2012-03-19 20:53:20 +01:00 committed by Qt by Nokia
parent 3f7741fbe7
commit 5cb0368516
7 changed files with 1084 additions and 594 deletions

View File

@ -385,7 +385,6 @@ Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &ma
in >> n;
map.detach();
map.setInsertInOrder(true);
for (quint32 i = 0; i < n; ++i) {
if (in.status() != QDataStream::Ok)
break;
@ -395,7 +394,6 @@ Q_OUTOFLINE_TEMPLATE QDataStream &operator>>(QDataStream &in, QMap<aKey, aT> &ma
in >> key >> value;
map.insertMulti(key, value);
}
map.setInsertInOrder(false);
if (in.status() != QDataStream::Ok)
map.clear();
if (oldStatus != QDataStream::Ok)

View File

@ -50,171 +50,317 @@
QT_BEGIN_NAMESPACE
const QMapData QMapData::shared_null = {
const_cast<QMapData *>(&shared_null),
{ const_cast<QMapData *>(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0
};
const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, 0, 0 } };
QMapData *QMapData::createData(int alignment)
const QMapNodeBase *QMapNodeBase::nextNode() const
{
QMapData *d = new QMapData;
Q_CHECK_PTR(d);
Node *e = reinterpret_cast<Node *>(d);
e->backward = e;
e->forward[0] = e;
const QMapNodeBase *n = this;
if (n->right) {
n = n->right;
while (n->left)
n = n->left;
} else {
const QMapNodeBase *y = n->parent();
while (y && n == y->right) {
n = y;
y = n->parent();
}
n = y;
}
return n;
}
const QMapNodeBase *QMapNodeBase::previousNode() const
{
const QMapNodeBase *n = this;
if (n->left) {
n = n->left;
while (n->right)
n = n->right;
} else {
const QMapNodeBase *y = n->parent();
while (y && n == y->left) {
n = y;
y = n->parent();
}
n = y;
}
return n;
}
void QMapDataBase::rotateLeft(QMapNodeBase *x)
{
QMapNodeBase *&root = header.left;
QMapNodeBase *y = x->right;
x->right = y->left;
if (y->left != 0)
y->left->setParent(x);
y->setParent(x->parent());
if (x == root)
root = y;
else if (x == x->parent()->left)
x->parent()->left = y;
else
x->parent()->right = y;
y->left = x;
x->setParent(y);
}
void QMapDataBase::rotateRight(QMapNodeBase *x)
{
QMapNodeBase *&root = header.left;
QMapNodeBase *y = x->left;
x->left = y->right;
if (y->right != 0)
y->right->setParent(x);
y->setParent(x->parent());
if (x == root)
root = y;
else if (x == x->parent()->right)
x->parent()->right = y;
else
x->parent()->left = y;
y->right = x;
x->setParent(y);
}
void QMapDataBase::rebalance(QMapNodeBase *x)
{
QMapNodeBase *&root = header.left;
x->setColor(QMapNodeBase::Red);
while (x != root && x->parent()->color() == QMapNodeBase::Red) {
if (x->parent() == x->parent()->parent()->left) {
QMapNodeBase *y = x->parent()->parent()->right;
if (y && y->color() == QMapNodeBase::Red) {
x->parent()->setColor(QMapNodeBase::Black);
y->setColor(QMapNodeBase::Black);
x->parent()->parent()->setColor(QMapNodeBase::Red);
x = x->parent()->parent();
} else {
if (x == x->parent()->right) {
x = x->parent();
rotateLeft(x);
}
x->parent()->setColor(QMapNodeBase::Black);
x->parent()->parent()->setColor(QMapNodeBase::Red);
rotateRight (x->parent()->parent());
}
} else {
QMapNodeBase *y = x->parent()->parent()->left;
if (y && y->color() == QMapNodeBase::Red) {
x->parent()->setColor(QMapNodeBase::Black);
y->setColor(QMapNodeBase::Black);
x->parent()->parent()->setColor(QMapNodeBase::Red);
x = x->parent()->parent();
} else {
if (x == x->parent()->left) {
x = x->parent();
rotateRight(x);
}
x->parent()->setColor(QMapNodeBase::Black);
x->parent()->parent()->setColor(QMapNodeBase::Red);
rotateLeft(x->parent()->parent());
}
}
}
root->setColor(QMapNodeBase::Black);
}
void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
{
QMapNodeBase *&root = header.left;
QMapNodeBase *y = z;
QMapNodeBase *x;
QMapNodeBase *x_parent;
if (y->left == 0) {
x = y->right;
} else {
if (y->right == 0) {
x = y->left;
} else {
y = y->right;
while (y->left != 0)
y = y->left;
x = y->right;
}
}
if (y != z) {
z->left->setParent(y);
y->left = z->left;
if (y != z->right) {
x_parent = y->parent();
if (x)
x->setParent(y->parent());
y->parent()->left = x;
y->right = z->right;
z->right->setParent(y);
} else {
x_parent = y;
}
if (root == z)
root = y;
else if (z->parent()->left == z)
z->parent()->left = y;
else
z->parent()->right = y;
y->setParent(z->parent());
// Swap the colors
QMapNodeBase::Color c = y->color();
y->setColor(z->color());
z->setColor(c);
y = z;
} else {
x_parent = y->parent();
if (x)
x->setParent(y->parent());
if (root == z)
root = x;
else if (z->parent()->left == z)
z->parent()->left = x;
else
z->parent()->right = x;
}
if (y->color() != QMapNodeBase::Red) {
while (x != root && (x == 0 || x->color() == QMapNodeBase::Black)) {
if (x == x_parent->left) {
QMapNodeBase *w = x_parent->right;
if (w->color() == QMapNodeBase::Red) {
w->setColor(QMapNodeBase::Black);
x_parent->setColor(QMapNodeBase::Red);
rotateLeft(x_parent);
w = x_parent->right;
}
if ((w->left == 0 || w->left->color() == QMapNodeBase::Black) &&
(w->right == 0 || w->right->color() == QMapNodeBase::Black)) {
w->setColor(QMapNodeBase::Red);
x = x_parent;
x_parent = x_parent->parent();
} else {
if (w->right == 0 || w->right->color() == QMapNodeBase::Black) {
if (w->left)
w->left->setColor(QMapNodeBase::Black);
w->setColor(QMapNodeBase::Red);
rotateRight(w);
w = x_parent->right;
}
w->setColor(x_parent->color());
x_parent->setColor(QMapNodeBase::Black);
if (w->right)
w->right->setColor(QMapNodeBase::Black);
rotateLeft(x_parent);
break;
}
} else {
QMapNodeBase *w = x_parent->left;
if (w->color() == QMapNodeBase::Red) {
w->setColor(QMapNodeBase::Black);
x_parent->setColor(QMapNodeBase::Red);
rotateRight(x_parent);
w = x_parent->left;
}
if ((w->right == 0 || w->right->color() == QMapNodeBase::Black) &&
(w->left == 0 || w->left->color() == QMapNodeBase::Black)) {
w->setColor(QMapNodeBase::Red);
x = x_parent;
x_parent = x_parent->parent();
} else {
if (w->left == 0 || w->left->color() == QMapNodeBase::Black) {
if (w->right)
w->right->setColor(QMapNodeBase::Black);
w->setColor(QMapNodeBase::Red);
rotateLeft(w);
w = x_parent->left;
}
w->setColor(x_parent->color());
x_parent->setColor(QMapNodeBase::Black);
if (w->left)
w->left->setColor(QMapNodeBase::Black);
rotateRight(x_parent);
break;
}
}
}
if (x)
x->setColor(QMapNodeBase::Black);
}
free(y);
--size;
}
static inline int qMapAlignmentThreshold()
{
// malloc on 32-bit platforms should return pointers that are 8-byte
// aligned or more while on 64-bit platforms they should be 16-byte aligned
// or more
return 2 * sizeof(void*);
}
static inline void *qMapAllocate(int alloc, int alignment)
{
return alignment > qMapAlignmentThreshold()
? qMallocAligned(alloc, alignment)
: ::malloc(alloc);
}
static inline void qMapDeallocate(QMapNodeBase *node, int alignment)
{
if (alignment > qMapAlignmentThreshold())
qFreeAligned(node);
else
::free(node);
}
QMapNodeBase *QMapDataBase::createNode(int alloc, int alignment, QMapNodeBase *parent, bool left)
{
QMapNodeBase *node = static_cast<QMapNodeBase *>(qMapAllocate(alloc, alignment));
Q_CHECK_PTR(node);
memset(node, 0, alloc);
++size;
if (parent) {
if (left) {
parent->left = node;
} else {
parent->right = node;
}
node->setParent(parent);
rebalance(node);
}
return node;
}
void QMapDataBase::freeTree(QMapNodeBase *root, int alignment)
{
if (root->left)
freeTree(root->left, alignment);
if (root->right)
freeTree(root->right, alignment);
qMapDeallocate(root, alignment);
}
QMapDataBase *QMapDataBase::createData()
{
QMapDataBase *d = new QMapDataBase;
d->ref.initializeOwned();
d->topLevel = 0;
d->size = 0;
d->randomBits = 0;
d->insertInOrder = false;
d->sharable = true;
d->strictAlignment = alignment > 8;
d->reserved = 0;
d->header.p = 0;
d->header.left = 0;
d->header.right = 0;
return d;
}
void QMapData::continueFreeData(int offset)
void QMapDataBase::freeData(QMapDataBase *d)
{
Node *e = reinterpret_cast<Node *>(this);
Node *cur = e->forward[0];
Node *prev;
while (cur != e) {
prev = cur;
cur = cur->forward[0];
if (strictAlignment)
qFreeAligned(reinterpret_cast<char *>(prev) - offset);
else
free(reinterpret_cast<char *>(prev) - offset);
}
delete this;
delete d;
}
/*!
Creates a new node inside the data structure.
\a update is an array with pointers to the node after which the new node
should be inserted. Because of the strange skip list data structure there
could be several pointers to this node on different levels.
\a offset is an amount of bytes that needs to reserved just before the
QMapData::Node structure.
\a alignment dictates the alignment for the data.
\internal
\since 4.6
*/
QMapData::Node *QMapData::node_create(Node *update[], int offset, int alignment)
{
int level = 0;
uint mask = (1 << Sparseness) - 1;
while ((randomBits & mask) == mask && level < LastLevel) {
++level;
mask <<= Sparseness;
}
if (level > topLevel) {
Node *e = reinterpret_cast<Node *>(this);
level = ++topLevel;
e->forward[level] = e;
update[level] = e;
}
++randomBits;
if (level == 3 && !insertInOrder)
randomBits = qrand();
void *concreteNode = strictAlignment ?
qMallocAligned(offset + sizeof(Node) + level * sizeof(Node *), alignment) :
malloc(offset + sizeof(Node) + level * sizeof(Node *));
Q_CHECK_PTR(concreteNode);
Node *abstractNode = reinterpret_cast<Node *>(reinterpret_cast<char *>(concreteNode) + offset);
abstractNode->backward = update[0];
update[0]->forward[0]->backward = abstractNode;
for (int i = level; i >= 0; i--) {
abstractNode->forward[i] = update[i]->forward[i];
update[i]->forward[i] = abstractNode;
update[i] = abstractNode;
}
++size;
return abstractNode;
}
void QMapData::node_delete(Node *update[], int offset, Node *node)
{
node->forward[0]->backward = node->backward;
for (int i = 0; i <= topLevel; ++i) {
if (update[i]->forward[i] != node)
break;
update[i]->forward[i] = node->forward[i];
}
--size;
if (strictAlignment)
qFreeAligned(reinterpret_cast<char *>(node) - offset);
else
free(reinterpret_cast<char *>(node) - offset);
}
#ifdef QT_QMAP_DEBUG
uint QMapData::adjust_ptr(Node *node)
{
if (node == reinterpret_cast<Node *>(this)) {
return (uint)0xDEADBEEF;
} else {
return (uint)node;
}
}
void QMapData::dump()
{
qDebug("Map data (ref = %d, size = %d, randomBits = %#.8x)", int(ref), size, randomBits);
QString preOutput;
QVector<QString> output(topLevel + 1);
Node *e = reinterpret_cast<Node *>(this);
QString str;
str.sprintf(" %.8x", adjust_ptr(reinterpret_cast<Node *>(this)));
preOutput += str;
Node *update[LastLevel + 1];
for (int i = 0; i <= topLevel; ++i) {
str.sprintf("%d: [%.8x] -", i, adjust_ptr(reinterpret_cast<Node *>(forward[i])));
output[i] += str;
update[i] = reinterpret_cast<Node *>(forward[i]);
}
Node *node = reinterpret_cast<Node *>(forward[0]);
while (node != e) {
int level = 0;
while (level < topLevel && update[level + 1] == node)
++level;
str.sprintf(" %.8x", adjust_ptr(node));
preOutput += str;
for (int i = 0; i <= level; ++i) {
str.sprintf("-> [%.8x] -", adjust_ptr(node->forward[i]));
output[i] += str;
update[i] = node->forward[i];
}
for (int j = level + 1; j <= topLevel; ++j)
output[j] += QLatin1String("---------------");
node = node->forward[0];
}
qDebug("%s", preOutput.ascii());
for (int i = 0; i <= topLevel; ++i)
qDebug("%s", output[i].ascii());
}
#endif
/*!
\class QMap
\brief The QMap class is a template class that provides a skip-list-based dictionary.
@ -482,11 +628,6 @@ void QMapData::dump()
\internal
*/
/*! \fn void QMap::setInsertInOrder(bool sharable)
\internal
*/
/*! \fn void QMap::clear()
Removes all items from the map.
@ -529,22 +670,21 @@ void QMapData::dump()
/*! \fn const T QMap::value(const Key &key) const
Returns the value associated with the key \a key.
If the map contains no item with key \a key, the function
returns a \l{default-constructed value}. If there are multiple
items for \a key in the map, the value of the most recently
inserted one is returned.
\sa key(), values(), contains(), operator[]()
*/
/*! \fn const T QMap::value(const Key &key, const T &defaultValue) const
\overload
Returns the value associated with the key \a key.
If the map contains no item with key \a key, the function returns
\a defaultValue.
\a defaultValue. If no \a defaultValue is specified, the function
returns a \l{default-constructed value}. If there are multiple
items for \a key in the map, the value of the most recently
inserted one is returned.
\sa key(), values(), contains(), operator[]()
*/
/*! \fn T &QMap::operator[](const Key &key)
@ -606,32 +746,21 @@ void QMapData::dump()
by value.
*/
/*! \fn Key QMap::key(const T &value) const
Returns the first key with value \a value.
If the map contains no item with value \a value, the function
returns a \link {default-constructed value} default-constructed
key \endlink.
This function can be slow (\l{linear time}), because QMap's
internal data structure is optimized for fast lookup by key, not
by value.
\sa value(), keys()
*/
/*!
\fn Key QMap::key(const T &value, const Key &defaultKey) const
\since 4.3
\overload
Returns the first key with value \a value, or \a defaultKey if
the map contains no item with value \a value.
the map contains no item with value \a value. If no \a defaultKey
is provided the function returns a \link {default-constructed value}
default-constructed key \endlink.
This function can be slow (\l{linear time}), because QMap's
internal data structure is optimized for fast lookup by key, not
by value.
\sa value(), keys()
*/
/*! \fn QList<T> QMap::values() const

File diff suppressed because it is too large Load Diff

View File

@ -41,10 +41,10 @@
#define QT_STRICT_ITERATORS
#include <qmap.h>
#include <QtTest/QtTest>
#include <QDebug>
#include <qmap.h>
class tst_QMap : public QObject
{
@ -74,6 +74,11 @@ private slots:
void qmultimap_specific();
void const_shared_null();
void equal_range();
void setSharable();
void insert();
};
typedef QMap<QString, QString> StringMap;
@ -105,6 +110,11 @@ int MyClass::count = 0;
typedef QMap<QString, MyClass> MyMap;
QDebug operator << (QDebug d, const MyClass &c) {
d << c.str;
return d;
}
void tst_QMap::init()
{
MyClass::count = 0;
@ -152,6 +162,7 @@ void tst_QMap::count()
map.insert( "Paul", MyClass("Tvete 6") );
QCOMPARE( map.count(), 9 );
QCOMPARE( map.count("Paul"), 1 );
#ifndef Q_CC_SUN
QCOMPARE( MyClass::count, 9 );
#endif
@ -519,6 +530,7 @@ void tst_QMap::find()
for(i = 3; i < 10; ++i) {
compareString = testString.arg(i);
map1.insertMulti(4, compareString);
QCOMPARE(map1.count(4), i - 2);
}
QMap<int, QString>::const_iterator it=map1.constFind(4);
@ -588,6 +600,33 @@ void tst_QMap::lowerUpperBound()
QCOMPARE(map1.lowerBound(2).key(), 5); // returns iterator to (5, "five")
QCOMPARE(map1.lowerBound(10).key(), 10); // returns iterator to (10, "ten")
QVERIFY(map1.lowerBound(999) == map1.end()); // returns end()
map1.insert(3, "three");
map1.insert(7, "seven");
map1.insertMulti(7, "seven_2");
QCOMPARE(map1.upperBound(0).key(), 1);
QCOMPARE(map1.upperBound(1).key(), 3);
QCOMPARE(map1.upperBound(2).key(), 3);
QCOMPARE(map1.upperBound(3).key(), 5);
QCOMPARE(map1.upperBound(7).key(), 10);
QVERIFY(map1.upperBound(10) == map1.end());
QVERIFY(map1.upperBound(999) == map1.end());
QCOMPARE(map1.lowerBound(0).key(), 1);
QCOMPARE(map1.lowerBound(1).key(), 1);
QCOMPARE(map1.lowerBound(2).key(), 3);
QCOMPARE(map1.lowerBound(3).key(), 3);
QCOMPARE(map1.lowerBound(4).key(), 5);
QCOMPARE(map1.lowerBound(5).key(), 5);
QCOMPARE(map1.lowerBound(6).key(), 7);
QCOMPARE(map1.lowerBound(7).key(), 7);
QCOMPARE(map1.lowerBound(6).value(), QString("seven_2"));
QCOMPARE(map1.lowerBound(7).value(), QString("seven_2"));
QCOMPARE((++map1.lowerBound(6)).value(), QString("seven"));
QCOMPARE((++map1.lowerBound(7)).value(), QString("seven"));
QCOMPARE(map1.lowerBound(10).key(), 10);
QVERIFY(map1.lowerBound(999) == map1.end());
}
void tst_QMap::mergeCompare()
@ -846,10 +885,129 @@ void tst_QMap::const_shared_null()
QMap<int, QString> map2;
map2.setSharable(true);
QVERIFY(!map2.isDetached());
}
QMap<int, QString> map3;
map3.setInsertInOrder(true);
map3.setInsertInOrder(false);
void tst_QMap::equal_range()
{
QMap<int, QString> map;
QPair<QMap<int, QString>::iterator, QMap<int, QString>::iterator> result = map.equal_range(0);
QCOMPARE(result.first, map.end());
QCOMPARE(result.second, map.end());
map.insert(1, "one");
result = map.equal_range(0);
QCOMPARE(result.first, map.find(1));
QCOMPARE(result.second, map.find(1));
result = map.equal_range(1);
QCOMPARE(result.first, map.find(1));
QCOMPARE(result.second, map.end());
result = map.equal_range(2);
QCOMPARE(result.first, map.end());
QCOMPARE(result.second, map.end());
for (int i = -10; i < 10; i += 2)
map.insert(i, QString("%1").arg(i));
result = map.equal_range(0);
QCOMPARE(result.first, map.find(0));
QCOMPARE(result.second, map.find(1));
result = map.equal_range(1);
QCOMPARE(result.first, map.find(1));
QCOMPARE(result.second, map.find(2));
result = map.equal_range(2);
QCOMPARE(result.first, map.find(2));
QCOMPARE(result.second, map.find(4));
map.insertMulti(1, "another one");
result = map.equal_range(1);
QCOMPARE(result.first, map.find(1));
QCOMPARE(result.second, map.find(2));
QCOMPARE(map.count(1), 2);
}
template <class T>
const T &const_(const T &t)
{
return t;
}
void tst_QMap::setSharable()
{
QMap<int, QString> map;
map.insert(1, "um");
map.insert(2, "dois");
map.insert(4, "quatro");
map.insert(5, "cinco");
map.setSharable(true);
QCOMPARE(map.size(), 4);
QCOMPARE(const_(map)[4], QString("quatro"));
{
QMap<int, QString> copy(map);
QVERIFY(!map.isDetached());
QVERIFY(copy.isSharedWith(map));
}
map.setSharable(false);
QVERIFY(map.isDetached());
QCOMPARE(map.size(), 4);
QCOMPARE(const_(map)[4], QString("quatro"));
{
QMap<int, QString> copy(map);
QVERIFY(map.isDetached());
QVERIFY(copy.isDetached());
QCOMPARE(copy.size(), 4);
QCOMPARE(const_(copy)[4], QString("quatro"));
QCOMPARE(map, copy);
}
map.setSharable(true);
QCOMPARE(map.size(), 4);
QCOMPARE(const_(map)[4], QString("quatro"));
{
QMap<int, QString> copy(map);
QVERIFY(!map.isDetached());
QVERIFY(copy.isSharedWith(map));
}
}
void tst_QMap::insert()
{
QMap<QString, float> map;
map.insert("cs/key1", 1);
map.insert("cs/key2", 2);
map.insert("cs/key1", 3);
QCOMPARE(map.count(), 2);
QMap<int, int> intMap;
for (int i = 0; i < 1000; ++i) {
intMap.insert(i, i);
}
QCOMPARE(intMap.size(), 1000);
for (int i = 0; i < 1000; ++i) {
QCOMPARE(intMap.value(i), i);
intMap.insert(i, -1);
QCOMPARE(intMap.size(), 1000);
QCOMPARE(intMap.value(i), -1);
}
}
QTEST_APPLESS_MAIN(tst_QMap)

View File

@ -0,0 +1,165 @@
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QFile>
#include <QMap>
#include <QString>
#include <QTest>
#include <qdebug.h>
class tst_QMap : public QObject
{
Q_OBJECT
private slots:
void insertion_int_int();
void insertion_int_string();
void insertion_string_int();
void lookup_int_int();
void lookup_int_string();
void lookup_string_int();
void iteration();
};
void tst_QMap::insertion_int_int()
{
QMap<int, int> map;
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
map.insert(i, i);
}
}
void tst_QMap::insertion_int_string()
{
QMap<int, QString> map;
QString str("Hello World");
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
map.insert(i, str);
}
}
void tst_QMap::insertion_string_int()
{
QMap<QString, int> map;
QString str("Hello World");
QBENCHMARK {
for (int i = 1; i < 100000; ++i) {
str[0] = QChar(i);
map.insert(str, i);
}
}
}
void tst_QMap::lookup_int_int()
{
QMap<int, int> map;
for (int i = 0; i < 100000; ++i)
map.insert(i, i);
int sum = 0;
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
sum += map.value(i);
}
}
void tst_QMap::lookup_int_string()
{
QMap<int, QString> map;
QString str("Hello World");
for (int i = 0; i < 100000; ++i)
map.insert(i, str);
QBENCHMARK {
for (int i = 0; i < 100000; ++i)
str += map.value(i);
}
}
void tst_QMap::lookup_string_int()
{
QMap<QString, int> map;
QString str("Hello World");
for (int i = 1; i < 100000; ++i) {
str[0] = QChar(i);
map.insert(str, i);
}
int sum = 0;
QBENCHMARK {
for (int i = 1; i < 100000; ++i) {
str[0] = QChar(i);
sum += map.value(str);
}
}
}
// iteration speed doesn't depend on the type of the map.
void tst_QMap::iteration()
{
QMap<int, int> map;
for (int i = 0; i < 100000; ++i)
map.insert(i, i);
int j = 0;
QBENCHMARK {
for (int i = 0; i < 100; ++i) {
QMap<int, int>::const_iterator it = map.constBegin();
QMap<int, int>::const_iterator end = map.constEnd();
while (it != end) {
j += *it;
++it;
}
}
}
}
QTEST_MAIN(tst_QMap)
#include "main.moc"

View File

@ -0,0 +1,5 @@
TARGET = tst_qmap
QT = core testlib
INCLUDEPATH += .
SOURCES += main.cpp
CONFIG += release

View File

@ -5,6 +5,7 @@ SUBDIRS = \
qbytearray \
qcontiguouscache \
qlist \
qmap \
qrect \
qregexp \
qstring \