/* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ unsigned int extractor_div_with = 0; #include #include #include #include #include #include #include #include #include #include #include #include struct TestItem { explicit TestItem(uint _value = 0) : value(_value), leftChild(-1), rightChild(-1) { } uint value; int leftChild; int rightChild; bool operator==(const TestItem& rhs) const { return value == rhs.value; } bool operator<(const TestItem& item) const { return value < item.value; } }; struct TestItemConversion { static uint toIndex(const TestItem& item) { return item.value; } static TestItem toItem(uint index) { return TestItem(index); } }; struct Extractor{ static TestItem extract(const TestItem& item) { return TestItem(item.value/extractor_div_with); } }; clock_t std_insertion = 0, std_removal = 0, std_contains = 0, std_iteration = 0, emb_insertion = 0, emb_removal = 0, emb_contains = 0, emb_iteration = 0; QString toString(std::set set) { QString ret; for(std::set::const_iterator it = set.begin(); it != set.end(); ++it) ret += QString("%1 ").arg(*it); return ret; } bool operator==(const std::set a, const std::set b) { if(a.size() != b.size()) { kDebug() << "size mismatch" << toString(a) << ": " << toString(b); return false; } std::set::const_iterator aIt = a.begin(); std::set::const_iterator bIt = b.begin(); for(; aIt != a.end(); ++aIt, ++bIt) if(*aIt != *bIt) { kDebug() << "mismatch" << toString(a) << ": " << toString(b); return false; } return true; } struct TestItemHandler { public: static int rightChild(const TestItem& m_data) { return m_data.rightChild; } static int leftChild(const TestItem& m_data) { return m_data.leftChild; } static void setLeftChild(TestItem& m_data, int child) { m_data.leftChild = child; } static void setRightChild(TestItem& m_data, int child) { m_data.rightChild = child; } //Copies this item into the given one static void copyTo(const TestItem& m_data, TestItem& data) { data = m_data; } static void createFreeItem(TestItem& data) { data = TestItem(); } static inline bool isFree(const TestItem& m_data) { return !m_data.value; } static const TestItem& data(const TestItem& m_data) { return m_data; } inline static bool equals(const TestItem& m_data, const TestItem& rhs) { return m_data.value == rhs.value; } }; class TestItemBasedSet { public: TestItemBasedSet() : m_centralFree(-1) { } void insert(uint i) { TestItem item(i); KDevelop::EmbeddedTreeAddItem add(data.data(), data.size(), m_centralFree, item); if((int)add.newItemCount() != (int)data.size()) { QVector newData; newData.resize(add.newItemCount()); add.transferData(newData.data(), newData.size()); data = newData; } } bool contains(uint item) { KDevelop::EmbeddedTreeAlgorithms alg(data.data(), data.size(), m_centralFree); return alg.indexOf(TestItem(item)) != -1; } void remove(uint i) { TestItem item(i); KDevelop::EmbeddedTreeRemoveItem remove(data.data(), data.size(), m_centralFree, item); if((int)remove.newItemCount() != (int)data.size()) { QVector newData; newData.resize(remove.newItemCount()); remove.transferData(newData.data(), newData.size()); data = newData; } } std::set toSet() const { std::set ret; for(int a = 0; a < data.size(); ++a) if(data[a].value) ret.insert(data[a].value); return ret; } void verify() { //1. verify order uint last = 0; uint freeCount = 0; for(int a = 0; a < data.size(); ++a) { if(data[a].value) { QVERIFY(last < data[a].value); last = data[a].value; }else{ ++freeCount; } } KDevelop::EmbeddedTreeAlgorithms algorithms(data.data(), data.size(), m_centralFree); uint countFree = algorithms.countFreeItems(); QCOMPARE(freeCount, countFree); algorithms.verifyTreeConsistent(); } uint getItem(uint number) const { Q_ASSERT(number < (uint)data.size()); uint ret = 0; uint size = (uint)data.size(); uint current = 0; for(uint a = 0; a < size; ++a) { if(data[a].value) { //Only count the non-free items if(current == number) ret = data[a].value; ++current; }else{ //This is a free item } } return ret; } private: int m_centralFree; QVector data; }; class TestSet { public: void add(uint i) { if(realSet.find(i) != realSet.end()) { QVERIFY(set.contains(i)); return; }else{ QVERIFY(!set.contains(i)); } clock_t start = clock(); realSet.insert(i); std_insertion += clock() - start; start = clock(); set.insert(i); emb_insertion += clock() - start; start = clock(); bool contained = realSet.find(i) != realSet.end(); std_contains += clock() - start; start = clock(); set.contains(i); emb_contains += clock() - start; QVERIFY(set.contains(i)); QVERIFY(contained); set.verify(); } void remove(uint i) { if(realSet.find(i) != realSet.end()) { QVERIFY(set.contains(i)); }else{ QVERIFY(!set.contains(i)); return; } clock_t start = clock(); set.remove(i); emb_removal += clock() - start; start = clock(); realSet.erase(i); std_removal += clock() - start; QVERIFY(!set.contains(i)); } uint size() const { return realSet.size(); } uint getItem(uint number) const { Q_ASSERT(number < size()); uint current = 0; uint ret = 0; clock_t start = clock(); for(std::set::const_iterator it = realSet.begin(); it != realSet.end(); ++it) { if(current == number) { ret = *it; } ++current; } std_iteration += clock() - start; start = clock(); set.getItem(number); emb_iteration += clock() - start; Q_ASSERT(ret); return ret; } void verify() { QVERIFY(realSet == set.toSet()); set.verify(); } private: std::set realSet; TestItemBasedSet set; }; float toSeconds(clock_t time) { return ((float)time) / CLOCKS_PER_SEC; } struct StaticRepository { static Utils::BasicSetRepository* repository() { static Utils::BasicSetRepository repository("test repository"); return &repository; } }; struct UintSetVisitor { std::set& s; UintSetVisitor(std::set& _s) : s(_s) { } inline bool operator() (const TestItem& item) { s.insert(item.value); return true; } }; struct NothingDoVisitor { inline bool operator() (const TestItem& item) { Q_UNUSED(item); return true; } }; class TestEmbeddedFreeTree : public QObject { Q_OBJECT private slots: void initTestCase() { KDevelop::AutoTestShell::init(); KDevelop::TestCore::initialize(KDevelop::Core::NoUi); } void cleanupTestCase() { KDevelop::TestCore::shutdown(); } void randomizedTest() { const int cycles = 10000; const int valueRange = 1000; const int removeProbability = 40; //Percent TestSet set; srand(time(NULL)); for(int a = 0; a < cycles; ++a) { if(a % (cycles / 10) == 0) { kDebug() << "cycle" << a; } bool remove = (rand() % 100) < removeProbability; if(remove && set.size()) { set.remove(set.getItem(rand() % set.size())); }else{ int value = (rand() % valueRange) + 1; set.add(value); } set.verify(); } kDebug() << "Performance embedded list: insertion:" << toSeconds(emb_insertion) << "removal:" << toSeconds(emb_removal) << "contains:" << toSeconds(emb_contains) << "iteration:" << toSeconds(emb_iteration); kDebug() << "Performance std::set: insertion:" << toSeconds(std_insertion) << "removal:" << toSeconds(std_removal) << "contains:" << toSeconds(std_contains) << "iteration:" << toSeconds(std_iteration); } void sequentialTest() { TestSet set; set.add(5); set.verify(); set.remove(5); set.verify(); set.add(3); set.verify(); set.add(4); set.verify(); set.add(7); set.verify(); set.remove(3); set.verify(); set.remove(7); set.verify(); set.add(6); set.verify(); set.add(1); set.verify(); set.add(9); set.verify(); set.remove(4); set.verify(); set.remove(9); set.verify(); set.add(1); set.verify(); set.add(2); set.verify(); set.add(3); set.verify(); set.add(4); set.verify(); set.add(5); set.verify(); set.remove(1); set.verify(); set.remove(3); set.verify(); set.add(15); set.verify(); set.add(16); set.verify(); set.add(17); set.verify(); set.add(18); set.verify(); set.remove(18); set.verify(); set.remove(17); set.verify(); set.add(9); set.verify(); } void testFiltering() { clock_t stdTime = 0; clock_t algoTime = 0; clock_t treeAlgoTime = 0; clock_t treeAlgoVisitorTime = 0; clock_t insertionStdTime = 0; clock_t insertionAlgoTime = 0; clock_t insertionTreeAlgoTime = 0; typedef Utils::StorableSet RepositorySet; const uint cycles = 3000; const uint setSize = 1500; uint totalItems = 0, totalFilteredItems = 0; srand(time(NULL)); for(uint a = 0; a < cycles; ++a) { KDevelop::ConvenientFreeListSet set1; std::set testSet1; KDevelop::ConvenientFreeListSet set2; std::set testSet2; RepositorySet repSet2; if(a % (cycles / 10) == 0) { kDebug() << "cycle" << a; } //Build the sets extractor_div_with = (rand() % 10) + 1; for(uint a = 0; a < setSize; ++a) { uint value = rand() % 3000; uint divValue = value/extractor_div_with; if(!divValue) continue; // kDebug() << "inserting" << value; std::set::const_iterator it = testSet1.lower_bound(value); int pos = set1.iterator().lowerBound(TestItem(value)); //This tests the upperBound functionality if (pos != -1) { QVERIFY(it != testSet1.end()); QVERIFY(set1.data()[pos].value == *it); } else { QVERIFY(it == testSet1.end()); } if((rand() % 10) == 0) { set1.insert(TestItem(value)); testSet1.insert(value); } //This is tuned so in the end, about 99% of all declarations are filtered out, like in the symbol table. if((rand() % (extractor_div_with*100)) == 0) { clock_t start = clock(); set2.insert(TestItem(divValue)); insertionStdTime += clock() - start; start = clock(); testSet2.insert(divValue); insertionAlgoTime += clock() - start; start = clock(); repSet2.insert(TestItem(divValue)); insertionTreeAlgoTime += clock() - start; start = clock(); } } std::set verifySet1; for(KDevelop::ConvenientFreeListSet::Iterator it = set1.iterator(); it; ++it) verifySet1.insert(it->value); std::set verifySet2; for(KDevelop::ConvenientFreeListSet::Iterator it = set2.iterator(); it; ++it) verifySet2.insert(it->value); std::set verifyRepSet2; for(RepositorySet::Iterator it = repSet2.iterator(); it; ++it) verifyRepSet2.insert((*it).value); QCOMPARE(verifySet1, testSet1); QCOMPARE(verifySet2, testSet2); QCOMPARE(verifyRepSet2, testSet2); std::set algoFiltered; std::set treeAlgoFiltered; std::set treeAlgoVisitorFiltered; { //Do the filtering once without actions on the filtered items, just for calculating the time clock_t start = clock(); { KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); while(filterIterator) ++filterIterator; algoTime += clock() - start; } start = clock(); { KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); while(filterIterator) ++filterIterator; treeAlgoTime += clock() - start; } { start = clock(); NothingDoVisitor v; KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); treeAlgoVisitorTime += clock() - start; } std::set stdFiltered; start = clock(); for(std::set::const_iterator it = testSet1.begin(); it != testSet1.end(); ++it) { if(testSet2.count((*it) / extractor_div_with) == 1) { } } stdTime += clock() - start; } { KDevelop::ConvenientEmbeddedSetFilterIterator filterIterator(set1.iterator(), set2.iterator()); while(filterIterator) { algoFiltered.insert(filterIterator->value); ++filterIterator; } } { KDevelop::ConvenientEmbeddedSetTreeFilterIterator filterIterator(set1.iterator(), repSet2); while(filterIterator) { treeAlgoFiltered.insert((*filterIterator).value); ++filterIterator; } } { UintSetVisitor v(treeAlgoVisitorFiltered); KDevelop::ConvenientEmbeddedSetTreeFilterVisitor visit(v, set1.iterator(), repSet2); } totalItems += testSet1.size(); totalFilteredItems += algoFiltered.size(); std::set stdFiltered; for(std::set::const_iterator it = testSet1.begin(); it != testSet1.end(); ++it) { if(testSet2.count((*it) / extractor_div_with) == 1) { stdFiltered.insert(*it); } } QCOMPARE(algoFiltered, stdFiltered); QCOMPARE(treeAlgoFiltered, stdFiltered); QCOMPARE(treeAlgoVisitorFiltered, stdFiltered); } kDebug() << "Filtering performance: embedded-list filtering:" << toSeconds(algoTime) << "set-repository filtering:" << toSeconds(treeAlgoTime) << "set-repository visitor filtering:" << toSeconds(treeAlgoVisitorTime) << "std::set filtering:" << toSeconds(stdTime) << "Normal -> Embedded speedup ratio:" << (toSeconds(stdTime) / toSeconds(algoTime)) << "Normal -> Repository speedup ratio:" << (toSeconds(stdTime) / toSeconds(treeAlgoVisitorTime)) << "total processed items:" << totalItems << "total items after filtering:" << totalFilteredItems; kDebug() << "Insertion: embedded-list:" << toSeconds(insertionAlgoTime) << "set-repository:" << toSeconds(insertionTreeAlgoTime) << "std::set:" << toSeconds(insertionStdTime); } }; #include "test_embeddedfreetree.moc" QTEST_MAIN(TestEmbeddedFreeTree)