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)