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"