File indexing completed on 2024-05-19 04:39:58
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 unsigned int extractor_div_with = 0; 0008 0009 #include <QObject> 0010 #include <QTest> 0011 #include <QVector> 0012 #include <QDebug> 0013 0014 #include <util/embeddedfreetree.h> 0015 #include <util/convenientfreelist.h> 0016 #include <language/util/setrepository.h> 0017 #include <tests/testcore.h> 0018 #include <tests/autotestshell.h> 0019 0020 #include <cstdlib> 0021 #include <ctime> 0022 #include <set> 0023 0024 struct TestItem 0025 { 0026 explicit TestItem(uint _value = 0) : value(_value) 0027 { 0028 } 0029 uint value; 0030 int leftChild = -1; 0031 int rightChild = -1; 0032 bool operator==(const TestItem& rhs) const 0033 { 0034 return value == rhs.value; 0035 } 0036 0037 bool operator<(const TestItem& item) const 0038 { 0039 return value < item.value; 0040 } 0041 }; 0042 0043 struct TestItemConversion 0044 { 0045 static uint toIndex(const TestItem& item) 0046 { 0047 return item.value; 0048 } 0049 static TestItem toItem(uint index) 0050 { 0051 return TestItem(index); 0052 } 0053 }; 0054 0055 struct Extractor 0056 { 0057 static TestItem extract(const TestItem& item) 0058 { 0059 return TestItem(item.value / extractor_div_with); 0060 } 0061 }; 0062 0063 clock_t std_insertion = 0, std_removal = 0, std_contains = 0, std_iteration = 0, emb_insertion = 0, emb_removal = 0, 0064 emb_contains = 0, emb_iteration = 0; 0065 0066 QString toString(const std::set<uint>& set) 0067 { 0068 QString ret; 0069 for (unsigned int i : set) 0070 ret += QStringLiteral("%1 ").arg(i); 0071 0072 return ret; 0073 } 0074 0075 bool operator==(const std::set<uint>& a, const std::set<uint>& b) 0076 { 0077 if (a.size() != b.size()) { 0078 qDebug() << "size mismatch" << toString(a) << ": " << toString(b); 0079 return false; 0080 } 0081 auto aIt = a.begin(); 0082 auto bIt = b.begin(); 0083 for (; aIt != a.end(); ++aIt, ++bIt) 0084 if (*aIt != *bIt) { 0085 qDebug() << "mismatch" << toString(a) << ": " << toString(b); 0086 return false; 0087 } 0088 0089 return true; 0090 } 0091 0092 struct TestItemHandler 0093 { 0094 public: 0095 static int rightChild(const TestItem& m_data) 0096 { 0097 return m_data.rightChild; 0098 } 0099 static int leftChild(const TestItem& m_data) 0100 { 0101 return m_data.leftChild; 0102 } 0103 static void setLeftChild(TestItem& m_data, int child) 0104 { 0105 m_data.leftChild = child; 0106 } 0107 static void setRightChild(TestItem& m_data, int child) 0108 { 0109 m_data.rightChild = child; 0110 } 0111 //Copies this item into the given one 0112 static void copyTo(const TestItem& m_data, TestItem& data) 0113 { 0114 data = m_data; 0115 } 0116 static void createFreeItem(TestItem& data) 0117 { 0118 data = TestItem(); 0119 } 0120 static inline bool isFree(const TestItem& m_data) 0121 { 0122 return !m_data.value; 0123 } 0124 0125 static const TestItem& data(const TestItem& m_data) 0126 { 0127 return m_data; 0128 } 0129 0130 inline static bool equals(const TestItem& m_data, const TestItem& rhs) 0131 { 0132 return m_data.value == rhs.value; 0133 } 0134 }; 0135 0136 class TestItemBasedSet 0137 { 0138 public: 0139 TestItemBasedSet() 0140 { 0141 } 0142 0143 void insert(uint i) 0144 { 0145 TestItem item(i); 0146 KDevelop::EmbeddedTreeAddItem<TestItem, TestItemHandler> add(data.data(), data.size(), m_centralFree, item); 0147 0148 if (( int )add.newItemCount() != ( int )data.size()) { 0149 QVector<TestItem> newData; 0150 newData.resize(add.newItemCount()); 0151 add.transferData(newData.data(), newData.size()); 0152 data = newData; 0153 } 0154 } 0155 0156 bool contains(uint item) 0157 { 0158 KDevelop::EmbeddedTreeAlgorithms<TestItem, TestItemHandler> alg(data.data(), data.size(), m_centralFree); 0159 return alg.indexOf(TestItem(item)) != -1; 0160 } 0161 0162 void remove(uint i) 0163 { 0164 TestItem item(i); 0165 KDevelop::EmbeddedTreeRemoveItem<TestItem, TestItemHandler> remove(data.data(), data.size(), m_centralFree, 0166 item); 0167 0168 if (( int )remove.newItemCount() != ( int )data.size()) { 0169 QVector<TestItem> newData; 0170 newData.resize(remove.newItemCount()); 0171 remove.transferData(newData.data(), newData.size()); 0172 data = newData; 0173 } 0174 } 0175 0176 std::set<uint> toSet() const 0177 { 0178 std::set<uint> ret; 0179 0180 for (auto& d : data) 0181 if (d.value) 0182 ret.insert(d.value); 0183 0184 return ret; 0185 } 0186 0187 void verify() 0188 { 0189 //1. verify order 0190 uint last = 0; 0191 uint freeCount = 0; 0192 for (auto& d : qAsConst(data)) { 0193 if (d.value) { 0194 QVERIFY(last < d.value); 0195 last = d.value; 0196 } else { 0197 ++freeCount; 0198 } 0199 } 0200 0201 KDevelop::EmbeddedTreeAlgorithms<TestItem, TestItemHandler> algorithms(data.data(), data.size(), m_centralFree); 0202 uint countFree = algorithms.countFreeItems(); 0203 QCOMPARE(freeCount, countFree); 0204 algorithms.verifyTreeConsistent(); 0205 } 0206 0207 uint getItem(uint number) const 0208 { 0209 Q_ASSERT(number < ( uint )data.size()); 0210 uint ret = 0; 0211 uint current = 0; 0212 for (auto& d : data) { 0213 if (d.value) { 0214 //Only count the non-free items 0215 if (current == number) 0216 ret = d.value; 0217 ++current; 0218 } else { 0219 //This is a free item 0220 } 0221 } 0222 0223 return ret; 0224 } 0225 0226 private: 0227 int m_centralFree = -1; 0228 QVector<TestItem> data; 0229 }; 0230 0231 class TestSet 0232 { 0233 public: 0234 void add(uint i) 0235 { 0236 if (realSet.find(i) != realSet.end()) { 0237 QVERIFY(set.contains(i)); 0238 return; 0239 } else { 0240 QVERIFY(!set.contains(i)); 0241 } 0242 clock_t start = clock(); 0243 realSet.insert(i); 0244 std_insertion += clock() - start; 0245 0246 start = clock(); 0247 set.insert(i); 0248 emb_insertion += clock() - start; 0249 0250 start = clock(); 0251 bool contained = realSet.find(i) != realSet.end(); 0252 std_contains += clock() - start; 0253 0254 start = clock(); 0255 set.contains(i); 0256 emb_contains += clock() - start; 0257 0258 QVERIFY(set.contains(i)); 0259 QVERIFY(contained); 0260 set.verify(); 0261 } 0262 0263 void remove(uint i) 0264 { 0265 if (realSet.find(i) != realSet.end()) { 0266 QVERIFY(set.contains(i)); 0267 } else { 0268 QVERIFY(!set.contains(i)); 0269 return; 0270 } 0271 clock_t start = clock(); 0272 set.remove(i); 0273 emb_removal += clock() - start; 0274 0275 start = clock(); 0276 realSet.erase(i); 0277 std_removal += clock() - start; 0278 0279 QVERIFY(!set.contains(i)); 0280 } 0281 0282 uint size() const 0283 { 0284 return static_cast<uint>(realSet.size()); 0285 } 0286 0287 uint getItem(uint number) const 0288 { 0289 Q_ASSERT(number < size()); 0290 uint current = 0; 0291 uint ret = 0; 0292 0293 clock_t start = clock(); 0294 for (unsigned int i : realSet) { 0295 if (current == number) { 0296 ret = i; 0297 } 0298 ++current; 0299 } 0300 0301 std_iteration += clock() - start; 0302 0303 start = clock(); 0304 set.getItem(number); 0305 emb_iteration += clock() - start; 0306 0307 Q_ASSERT(ret); 0308 return ret; 0309 } 0310 0311 void verify() 0312 { 0313 QVERIFY(realSet == set.toSet()); 0314 set.verify(); 0315 } 0316 0317 private: 0318 std::set<uint> realSet; 0319 TestItemBasedSet set; 0320 }; 0321 0322 float toSeconds(clock_t time) 0323 { 0324 return (( float )time) / CLOCKS_PER_SEC; 0325 } 0326 0327 struct StaticRepository 0328 { 0329 static Utils::BasicSetRepository* repository() 0330 { 0331 static QRecursiveMutex mutex; 0332 static Utils::BasicSetRepository repository(QStringLiteral("test repository"), &mutex); 0333 return &repository; 0334 } 0335 }; 0336 0337 struct UintSetVisitor 0338 { 0339 std::set<uint>& s; 0340 explicit UintSetVisitor(std::set<uint>& _s) : s(_s) 0341 { 0342 } 0343 0344 inline bool operator()(const TestItem& item) 0345 { 0346 s.insert(item.value); 0347 return true; 0348 } 0349 }; 0350 0351 struct NothingDoVisitor 0352 { 0353 inline bool operator()(const TestItem& item) 0354 { 0355 Q_UNUSED(item); 0356 return true; 0357 } 0358 }; 0359 0360 class TestEmbeddedFreeTree : public QObject 0361 { 0362 Q_OBJECT 0363 0364 private Q_SLOTS: 0365 void initTestCase() 0366 { 0367 KDevelop::AutoTestShell::init(); 0368 KDevelop::TestCore::initialize(KDevelop::Core::NoUi); 0369 } 0370 void cleanupTestCase() 0371 { 0372 KDevelop::TestCore::shutdown(); 0373 } 0374 void randomizedTest() 0375 { 0376 const int cycles = 10000; 0377 const int valueRange = 1000; 0378 const int removeProbability = 40; //Percent 0379 0380 TestSet set; 0381 srand(time(nullptr)); 0382 for (int a = 0; a < cycles; ++a) { 0383 if (a % (cycles / 10) == 0) { 0384 qDebug() << "cycle" << a; 0385 } 0386 0387 bool remove = (rand() % 100) < removeProbability; 0388 if (remove && set.size()) { 0389 set.remove(set.getItem(rand() % set.size())); 0390 } else { 0391 int value = (rand() % valueRange) + 1; 0392 set.add(value); 0393 } 0394 set.verify(); 0395 } 0396 0397 qDebug() << "Performance embedded list: insertion:" << toSeconds(emb_insertion) << "removal:" << toSeconds( 0398 emb_removal) << "contains:" << toSeconds(emb_contains) << "iteration:" << toSeconds(emb_iteration); 0399 qDebug() << "Performance std::set: insertion:" << toSeconds(std_insertion) << "removal:" << toSeconds( 0400 std_removal) << "contains:" << toSeconds(std_contains) << "iteration:" << toSeconds(std_iteration); 0401 } 0402 void sequentialTest() 0403 { 0404 TestSet set; 0405 set.add(5); 0406 set.verify(); 0407 0408 set.remove(5); 0409 set.verify(); 0410 0411 set.add(3); 0412 set.verify(); 0413 0414 set.add(4); 0415 set.verify(); 0416 0417 set.add(7); 0418 set.verify(); 0419 0420 set.remove(3); 0421 set.verify(); 0422 0423 set.remove(7); 0424 set.verify(); 0425 0426 set.add(6); 0427 set.verify(); 0428 0429 set.add(1); 0430 set.verify(); 0431 0432 set.add(9); 0433 set.verify(); 0434 0435 set.remove(4); 0436 set.verify(); 0437 0438 set.remove(9); 0439 set.verify(); 0440 0441 set.add(1); 0442 set.verify(); 0443 set.add(2); 0444 set.verify(); 0445 set.add(3); 0446 set.verify(); 0447 set.add(4); 0448 set.verify(); 0449 set.add(5); 0450 set.verify(); 0451 set.remove(1); 0452 set.verify(); 0453 set.remove(3); 0454 set.verify(); 0455 set.add(15); 0456 set.verify(); 0457 set.add(16); 0458 set.verify(); 0459 set.add(17); 0460 set.verify(); 0461 set.add(18); 0462 set.verify(); 0463 set.remove(18); 0464 set.verify(); 0465 set.remove(17); 0466 set.verify(); 0467 set.add(9); 0468 set.verify(); 0469 } 0470 0471 void testFiltering() 0472 { 0473 clock_t stdTime = 0; 0474 clock_t algoTime = 0; 0475 clock_t treeAlgoTime = 0; 0476 clock_t treeAlgoVisitorTime = 0; 0477 0478 clock_t insertionStdTime = 0; 0479 clock_t insertionAlgoTime = 0; 0480 clock_t insertionTreeAlgoTime = 0; 0481 0482 using RepositorySet = Utils::StorableSet<TestItem, TestItemConversion, StaticRepository>; 0483 0484 const uint cycles = 3000; 0485 const uint setSize = 1500; 0486 0487 uint totalItems = 0, totalFilteredItems = 0; 0488 0489 srand(time(nullptr)); 0490 for (uint a = 0; a < cycles; ++a) { 0491 KDevelop::ConvenientFreeListSet<TestItem, TestItemHandler> set1; 0492 std::set<uint> testSet1; 0493 0494 KDevelop::ConvenientFreeListSet<TestItem, TestItemHandler> set2; 0495 std::set<uint> testSet2; 0496 RepositorySet repSet2; 0497 0498 if (a % (cycles / 10) == 0) { 0499 qDebug() << "cycle" << a; 0500 } 0501 0502 //Build the sets 0503 extractor_div_with = (rand() % 10) + 1; 0504 for (uint a = 0; a < setSize; ++a) { 0505 uint value = rand() % 3000; 0506 uint divValue = value / extractor_div_with; 0507 if (!divValue) 0508 continue; 0509 // qDebug() << "inserting" << value; 0510 0511 auto it = testSet1.lower_bound(value); 0512 int pos = set1.iterator().lowerBound(TestItem(value)); 0513 //This tests the upperBound functionality 0514 if (pos != -1) { 0515 QVERIFY(it != testSet1.end()); 0516 QVERIFY(set1.data()[pos].value == *it); 0517 } else { 0518 QVERIFY(it == testSet1.end()); 0519 } 0520 0521 if ((rand() % 10) == 0) { 0522 0523 set1.insert(TestItem(value)); 0524 testSet1.insert(value); 0525 } 0526 0527 //This is tuned so in the end, about 99% of all declarations are filtered out, like in the symbol table. 0528 if ((rand() % (extractor_div_with * 100)) == 0) { 0529 0530 clock_t start = clock(); 0531 0532 set2.insert(TestItem(divValue)); 0533 0534 insertionStdTime += clock() - start; 0535 start = clock(); 0536 0537 testSet2.insert(divValue); 0538 0539 insertionAlgoTime += clock() - start; 0540 start = clock(); 0541 0542 repSet2.insert(TestItem(divValue)); 0543 0544 insertionTreeAlgoTime += clock() - start; 0545 start = clock(); 0546 } 0547 } 0548 0549 std::set<uint> verifySet1; 0550 for (KDevelop::ConvenientFreeListSet<TestItem, TestItemHandler>::Iterator it = set1.iterator(); it; ++it) 0551 verifySet1.insert(it->value); 0552 0553 std::set<uint> verifySet2; 0554 for (KDevelop::ConvenientFreeListSet<TestItem, TestItemHandler>::Iterator it = set2.iterator(); it; ++it) 0555 verifySet2.insert(it->value); 0556 0557 std::set<uint> verifyRepSet2; 0558 for (RepositorySet::Iterator it = repSet2.iterator(); it; ++it) 0559 verifyRepSet2.insert((*it).value); 0560 0561 QCOMPARE(verifySet1, testSet1); 0562 QCOMPARE(verifySet2, testSet2); 0563 QCOMPARE(verifyRepSet2, testSet2); 0564 0565 std::set<uint> algoFiltered; 0566 std::set<uint> treeAlgoFiltered; 0567 std::set<uint> treeAlgoVisitorFiltered; 0568 0569 { 0570 //Do the filtering once without actions on the filtered items, just for calculating the time 0571 clock_t start = clock(); 0572 0573 { 0574 KDevelop::ConvenientEmbeddedSetFilterIterator<TestItem, TestItemHandler, TestItem, TestItemHandler, 0575 Extractor> filterIterator(set1.iterator(), set2.iterator()); 0576 while (filterIterator) 0577 ++filterIterator; 0578 0579 algoTime += clock() - start; 0580 } 0581 0582 start = clock(); 0583 0584 { 0585 KDevelop::ConvenientEmbeddedSetTreeFilterIterator<TestItem, TestItemHandler, TestItem, 0586 RepositorySet, Extractor> filterIterator(set1.iterator(), repSet2); 0587 while (filterIterator) 0588 ++filterIterator; 0589 0590 treeAlgoTime += clock() - start; 0591 } 0592 0593 { 0594 start = clock(); 0595 0596 NothingDoVisitor v; 0597 KDevelop::ConvenientEmbeddedSetTreeFilterVisitor<TestItem, TestItemHandler, TestItem, RepositorySet, 0598 Extractor, NothingDoVisitor> visit(v, set1.iterator(), repSet2); 0599 0600 treeAlgoVisitorTime += clock() - start; 0601 } 0602 0603 start = clock(); 0604 0605 for (unsigned int i : testSet1) { 0606 if (testSet2.count(i / extractor_div_with) == 1) { 0607 } 0608 } 0609 0610 stdTime += clock() - start; 0611 } 0612 0613 { 0614 KDevelop::ConvenientEmbeddedSetFilterIterator<TestItem, TestItemHandler, TestItem, TestItemHandler, 0615 Extractor> filterIterator(set1.iterator(), set2.iterator()); 0616 while (filterIterator) { 0617 algoFiltered.insert(filterIterator->value); 0618 ++filterIterator; 0619 } 0620 } 0621 { 0622 KDevelop::ConvenientEmbeddedSetTreeFilterIterator<TestItem, TestItemHandler, TestItem, RepositorySet, 0623 Extractor> filterIterator(set1.iterator(), repSet2); 0624 while (filterIterator) { 0625 treeAlgoFiltered.insert((*filterIterator).value); 0626 ++filterIterator; 0627 } 0628 } 0629 { 0630 UintSetVisitor v(treeAlgoVisitorFiltered); 0631 KDevelop::ConvenientEmbeddedSetTreeFilterVisitor<TestItem, TestItemHandler, TestItem, RepositorySet, 0632 Extractor, UintSetVisitor> visit(v, set1.iterator(), repSet2); 0633 } 0634 0635 totalItems += static_cast<uint>(testSet1.size()); 0636 totalFilteredItems += static_cast<uint>(algoFiltered.size()); 0637 0638 std::set<uint> stdFiltered; 0639 0640 for (unsigned int i : testSet1) { 0641 if (testSet2.count(i / extractor_div_with) == 1) { 0642 stdFiltered.insert(i); 0643 } 0644 } 0645 0646 QCOMPARE(algoFiltered, stdFiltered); 0647 QCOMPARE(treeAlgoFiltered, stdFiltered); 0648 QCOMPARE(treeAlgoVisitorFiltered, stdFiltered); 0649 0650 } 0651 0652 qDebug() << "Filtering performance: embedded-list filtering:" << toSeconds(algoTime) << 0653 "set-repository filtering:" << toSeconds(treeAlgoTime) << "set-repository visitor filtering:" << toSeconds( 0654 treeAlgoVisitorTime) << "std::set filtering:" << toSeconds(stdTime) << 0655 "Normal -> Embedded speedup ratio:" << (toSeconds(stdTime) / toSeconds(algoTime)) << 0656 "Normal -> Repository speedup ratio:" << (toSeconds(stdTime) / toSeconds(treeAlgoVisitorTime)) << 0657 "total processed items:" << totalItems << "total items after filtering:" << totalFilteredItems; 0658 qDebug() << "Insertion: embedded-list:" << toSeconds(insertionAlgoTime) << "set-repository:" << toSeconds( 0659 insertionTreeAlgoTime) << "std::set:" << toSeconds(insertionStdTime); 0660 0661 } 0662 }; 0663 0664 #include "test_embeddedfreetree.moc" 0665 0666 QTEST_MAIN(TestEmbeddedFreeTree)