File indexing completed on 2024-05-19 04:39:27

0001 /*
0002     SPDX-FileCopyrightText: 2012-2013 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "bench_itemrepository.h"
0008 
0009 #include <serialization/itemrepository.h>
0010 #include <serialization/indexedstring.h>
0011 #include <serialization/referencecounting.h>
0012 
0013 #include <algorithm>
0014 #include <limits>
0015 #include <random>
0016 #include <vector>
0017 #include <QTest>
0018 
0019 QTEST_GUILESS_MAIN(BenchItemRepository)
0020 
0021 using namespace KDevelop;
0022 
0023 struct TestData
0024 {
0025     uint length;
0026 
0027     TestData& operator=(const TestData& rhs) = delete;
0028 
0029     uint itemSize() const
0030     {
0031         return sizeof(TestData) + length;
0032     }
0033     uint hash() const
0034     {
0035         const char* str = reinterpret_cast<const char*>(this) + sizeof(TestData);
0036         return IndexedString::hashString(str, length);
0037     }
0038 };
0039 
0040 struct TestDataRepositoryItemRequest
0041 {
0042     //The text is supposed to be utf8 encoded
0043     TestDataRepositoryItemRequest(const char* text, uint length)
0044         : m_length(length)
0045         , m_text(text)
0046         , m_hash(IndexedString::hashString(text, length))
0047     {
0048     }
0049 
0050     enum {
0051         AverageSize = 10 //This should be the approximate average size of an Item
0052     };
0053 
0054     using HashType = uint;
0055 
0056     //Should return the hash-value associated with this request(For example the hash of a string)
0057     HashType hash() const
0058     {
0059         return m_hash;
0060     }
0061 
0062     //Should return the size of an item created with createItem
0063     uint itemSize() const
0064     {
0065         return sizeof(TestData) + m_length;
0066     }
0067     //Should create an item where the information of the requested item is permanently stored. The pointer
0068     //@param item equals an allocated range with the size of itemSize().
0069     void createItem(TestData* item) const
0070     {
0071         item->length = m_length;
0072         void* itemText = reinterpret_cast<void*>(item + 1);
0073         memcpy(itemText, m_text, m_length);
0074     }
0075 
0076     static void destroy(TestData* item, AbstractItemRepository&)
0077     {
0078         Q_UNUSED(item);
0079         //Nothing to do here (The object is not intelligent)
0080     }
0081 
0082     static bool persistent(const TestData* item)
0083     {
0084         Q_UNUSED(item);
0085         return true;
0086     }
0087 
0088     //Should return whether the here requested item equals the given item
0089     bool equals(const TestData* item) const
0090     {
0091         return item->length == m_length && (memcmp(++item, m_text, m_length) == 0);
0092     }
0093     unsigned short m_length;
0094     const char* m_text;
0095     unsigned int m_hash;
0096 };
0097 
0098 using TestDataRepository = ItemRepository<TestData, TestDataRepositoryItemRequest, false>;
0099 
0100 static QVector<QString> generateData()
0101 {
0102     QVector<QString> data;
0103     static const int NUM_ITEMS = 100000;
0104     data.resize(NUM_ITEMS);
0105     for (int i = 0; i < NUM_ITEMS; ++i) {
0106         data[i] = QStringLiteral("/foo/%1").arg(i);
0107     }
0108 
0109     return data;
0110 }
0111 
0112 static QVector<uint> insertData(const QVector<QString>& data, TestDataRepository& repo)
0113 {
0114     QVector<uint> indices;
0115     indices.reserve(data.size());
0116     for (const QString& item : data) {
0117         const QByteArray byteArray = item.toUtf8();
0118         indices << repo.index(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length()));
0119     }
0120 
0121     return indices;
0122 }
0123 
0124 void BenchItemRepository::insert()
0125 {
0126     QMutex mutex;
0127     TestDataRepository repo("TestDataRepositoryInsert", &mutex);
0128     const QVector<QString> data = generateData();
0129     QVector<uint> indices;
0130     QBENCHMARK_ONCE {
0131         indices = insertData(data, repo);
0132         repo.store();
0133     }
0134     Q_ASSERT(indices.size() == data.size());
0135     QCOMPARE(repo.statistics().totalItems, uint(data.size()));
0136 }
0137 
0138 void BenchItemRepository::remove()
0139 {
0140     QMutex mutex;
0141     TestDataRepository repo("TestDataRepositoryRemove", &mutex);
0142     const QVector<QString> data = generateData();
0143     const QVector<uint> indices = insertData(data, repo);
0144     repo.store();
0145     QVERIFY(indices.size() == QSet<uint>(indices.begin(), indices.end()).size());
0146     QVERIFY(indices.size() == data.size());
0147     QBENCHMARK_ONCE {
0148         for (uint index : indices) {
0149             repo.deleteItem(index);
0150         }
0151 
0152         repo.store();
0153     }
0154     QCOMPARE(repo.statistics().totalItems, 0u);
0155 }
0156 
0157 void BenchItemRepository::removeDisk()
0158 {
0159     const QVector<QString> data = generateData();
0160     QVector<uint> indices;
0161     QMutex mutex;
0162     {
0163         TestDataRepository repo("TestDataRepositoryRemoveDisk", &mutex);
0164         indices = insertData(data, repo);
0165         repo.store();
0166     }
0167     TestDataRepository repo("TestDataRepositoryRemoveDisk", &mutex);
0168     QVERIFY(repo.statistics().totalItems == static_cast<uint>(data.size()));
0169     QBENCHMARK_ONCE {
0170         for (uint index : qAsConst(indices)) {
0171             repo.deleteItem(index);
0172         }
0173 
0174         repo.store();
0175     }
0176     QCOMPARE(repo.statistics().totalItems, 0u);
0177 }
0178 
0179 void BenchItemRepository::lookupKey()
0180 {
0181     QMutex mutex;
0182     TestDataRepository repo("TestDataRepositoryLookupKey", &mutex);
0183     const QVector<QString> data = generateData();
0184     QVector<uint> indices = insertData(data, repo);
0185     std::shuffle(indices.begin(), indices.end(), std::default_random_engine(0));
0186     QBENCHMARK {
0187         for (uint index : qAsConst(indices)) {
0188             repo.itemFromIndex(index);
0189         }
0190     }
0191 }
0192 
0193 void BenchItemRepository::lookupValue()
0194 {
0195     QMutex mutex;
0196     TestDataRepository repo("TestDataRepositoryLookupValue", &mutex);
0197     const QVector<QString> data = generateData();
0198     QVector<uint> indices = insertData(data, repo);
0199     std::shuffle(indices.begin(), indices.end(), std::default_random_engine(0));
0200     QBENCHMARK {
0201         for (const QString& item : data) {
0202             const QByteArray byteArray = item.toUtf8();
0203             repo.findIndex(TestDataRepositoryItemRequest(byteArray.constData(), byteArray.length()));
0204         }
0205     }
0206 }
0207 
0208 void BenchItemRepository::shouldDoReferenceCounting_data()
0209 {
0210     QTest::addColumn<bool>("enableReferenceCounting");
0211     QTest::newRow("disabled") << false;
0212     QTest::newRow("enabled") << true;
0213 }
0214 
0215 void BenchItemRepository::shouldDoReferenceCounting()
0216 {
0217     using Type = unsigned;
0218     static_assert(sizeof(Type) == sizeof(IndexedString),
0219                   "Type emulates inlined IndexedString and should be of the same size.");
0220     constexpr std::size_t valueCount{100'000'000};
0221     std::vector<Type> values(valueCount);
0222 
0223     const auto referenceCountingStart = &values.front();
0224     constexpr auto referenceCountingSize = valueCount * sizeof(Type);
0225     QFETCH(bool, enableReferenceCounting);
0226     if (enableReferenceCounting) {
0227         enableDUChainReferenceCounting(referenceCountingStart, referenceCountingSize);
0228     }
0229 
0230     // NOTE: switching CountType from int to std::size_t slows down the "disabled" benchmark
0231     // by 17% and the "enabled" benchmark by 5%, as does removing the static_cast<CountType> below!
0232     using CountType = int;
0233     CountType count{0};
0234     static_assert(valueCount <= std::numeric_limits<CountType>::max());
0235     QBENCHMARK_ONCE {
0236         for (auto& v : values) {
0237             count += shouldDoDUChainReferenceCounting(&v);
0238         }
0239     }
0240 
0241     if (enableReferenceCounting) {
0242         disableDUChainReferenceCounting(referenceCountingStart, referenceCountingSize);
0243         QCOMPARE(count, static_cast<CountType>(values.size()));
0244     } else {
0245         QCOMPARE(count, 0);
0246     }
0247 }
0248 
0249 #include "moc_bench_itemrepository.cpp"