diff --git a/autotests/udsentry_benchmark.cpp b/autotests/udsentry_benchmark.cpp --- a/autotests/udsentry_benchmark.cpp +++ b/autotests/udsentry_benchmark.cpp @@ -55,8 +55,15 @@ void testHashStructApp(); void testMapStructSlave(); void testMapStructApp(); - void testTwoVectorsSlave(); + void testTwoVectorsSlaveFill(); + void testTwoVectorsSlaveCompare(); void testTwoVectorsApp(); + void testAnotherSlaveFill(); + void testAnotherSlaveCompare(); + void testAnotherApp(); + void testAnotherV2SlaveFill(); + void testAnotherV2SlaveCompare(); + void testAnotherV2App(); private: const QString nameStr; const QDateTime now; @@ -144,6 +151,7 @@ // QHash or QMap? doesn't seem to make much difference. typedef QHash UDSEntryHV; +// This uses QDateTime instead of time_t static void fillUDSEntryHV(UDSEntryHV &entry, const QDateTime &now, const QString &nameStr) { entry.reserve(8); @@ -222,31 +230,33 @@ // Another possibility, to save on QVariant costs typedef QHash UDSEntryHS; // hash+struct -static void fillQHashStructEntry(UDSEntryHS &entry, time_t now_time_t, const QString &nameStr) +template static void fillUDSEntries(T &entry, time_t now_time_t, const QString &nameStr) { entry.reserve(8); - entry.insert(KIO::UDSEntry::UDS_NAME, nameStr); - entry.insert(KIO::UDSEntry::UDS_SIZE, 123456ULL); - entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, now_time_t); - entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, now_time_t); - entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); - entry.insert(KIO::UDSEntry::UDS_ACCESS, 0644); - entry.insert(KIO::UDSEntry::UDS_USER, nameStr); + // In reverse order of index entry.insert(KIO::UDSEntry::UDS_GROUP, nameStr); + entry.insert(KIO::UDSEntry::UDS_USER, nameStr); + entry.insert(KIO::UDSEntry::UDS_ACCESS, 0644); + entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); + entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, now_time_t); + entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, now_time_t); + entry.insert(KIO::UDSEntry::UDS_SIZE, 123456ULL); + entry.insert(KIO::UDSEntry::UDS_NAME, nameStr); } + void UdsEntryBenchmark::testHashStructSlave() { QBENCHMARK { UDSEntryHS entry; - fillQHashStructEntry(entry, now_time_t, nameStr); + fillUDSEntries(entry, now_time_t, nameStr); QCOMPARE(entry.count(), 8); } } void UdsEntryBenchmark::testHashStructApp() { UDSEntryHS entry; - fillQHashStructEntry(entry, now_time_t, nameStr); + fillUDSEntries(entry, now_time_t, nameStr); QString displayName; KIO::filesize_t size; @@ -398,32 +408,40 @@ } }; -static void fillFrankUDSEntry(FrankUDSEntry &entry, time_t now_time_t, const QString &nameStr) +template void testFill(time_t now_time_t, const QString &nameStr) { - entry.reserve(8); - entry.insert(KIO::UDSEntry::UDS_NAME, nameStr); - entry.insert(KIO::UDSEntry::UDS_SIZE, 123456ULL); - entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, now_time_t); - entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, now_time_t); - entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); - entry.insert(KIO::UDSEntry::UDS_ACCESS, 0644); - entry.insert(KIO::UDSEntry::UDS_USER, nameStr); - entry.insert(KIO::UDSEntry::UDS_GROUP, nameStr); + QBENCHMARK { + T entry; + fillUDSEntries (entry, now_time_t, nameStr); + QCOMPARE(entry.count(), 8); + } } -void UdsEntryBenchmark::testTwoVectorsSlave() +template void testCompare(time_t now_time_t, const QString &nameStr) { + T entry; + T entry2; + fillUDSEntries (entry, now_time_t, nameStr); + fillUDSEntries (entry2, now_time_t, nameStr); + QCOMPARE(entry.count(), 8); + QCOMPARE(entry2.count(), 8); QBENCHMARK { - FrankUDSEntry entry; - fillFrankUDSEntry(entry, now_time_t, nameStr); - QCOMPARE(entry.count(), 8); + bool equal = entry.stringValue(KIO::UDSEntry::UDS_NAME) == entry2.stringValue(KIO::UDSEntry::UDS_NAME) && + entry.numberValue(KIO::UDSEntry::UDS_SIZE) == entry2.numberValue(KIO::UDSEntry::UDS_SIZE) && + entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) == entry2.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) && + entry.numberValue(KIO::UDSEntry::UDS_ACCESS_TIME) == entry2.numberValue(KIO::UDSEntry::UDS_ACCESS_TIME) && + entry.numberValue(KIO::UDSEntry::UDS_FILE_TYPE) == entry2.numberValue(KIO::UDSEntry::UDS_FILE_TYPE) && + entry.numberValue(KIO::UDSEntry::UDS_ACCESS) == entry2.numberValue(KIO::UDSEntry::UDS_ACCESS) && + entry.stringValue(KIO::UDSEntry::UDS_USER) == entry2.stringValue(KIO::UDSEntry::UDS_USER) && + entry.stringValue(KIO::UDSEntry::UDS_GROUP) == entry2.stringValue(KIO::UDSEntry::UDS_GROUP); + QVERIFY(equal); } } -void UdsEntryBenchmark::testTwoVectorsApp() +template void testApp(time_t now_time_t, const QString &nameStr) { - FrankUDSEntry entry; - fillFrankUDSEntry(entry, now_time_t, nameStr); + T entry; + fillUDSEntries (entry, now_time_t, nameStr); QString displayName; KIO::filesize_t size; @@ -439,6 +457,214 @@ } } +void UdsEntryBenchmark::testTwoVectorsSlaveFill() +{ + testFill(now_time_t, nameStr); +} +void UdsEntryBenchmark::testTwoVectorsSlaveCompare() +{ + testCompare(now_time_t, nameStr); +} +void UdsEntryBenchmark::testTwoVectorsApp() +{ + testApp(now_time_t, nameStr); +} + + +// Instead of two vectors, use only one +class AnotherUDSEntry +{ +private: + struct Field + { + inline Field() {} + inline Field(const uint index, const QString &value) : m_str(value), m_index(index) {} + inline Field(const uint index, long long value = 0) : m_long(value), m_index(index) {} + // This operator is essential to gain some speed, because the default == is slow + inline bool operator == (const Field &other) const { + return m_index == other.m_index; + } + + QString m_str = QStringLiteral(); + long long m_long = LLONG_MIN; + uint m_index = -1; + }; + std::vector storage; +public: + void reserve(int size) + { + storage.reserve(size); + } + void insert(uint field, const QString &value) + { + Q_ASSERT(std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}) == storage.end()); + storage.push_back(Field(field, value)); + } + void replaceOrInsert(uint field, const QString &value) + { + auto index = std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}); + if (index != storage.end()) { + Q_ASSERT(index->m_long != LLONG_MIN); + index->m_str = value; + return; + } + storage.push_back(Field(field, value)); + } + void insert(uint field, long long value) + { + Q_ASSERT(std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}) == storage.end()); + storage.push_back(Field(field, value)); + } + void replaceOrInsert(uint field, long long value) + { + auto index = std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}); + if (index != storage.end()) { + Q_ASSERT(index->m_str != QStringLiteral()); + index->m_long = value; + return; + } + storage.push_back(Field(field, value)); + } + int count() const + { + return storage.size(); + } + QString stringValue(uint field) const + { + auto index = std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}); + if (index != storage.end()) { + return index->m_str; + } + return QString(); + } + long long numberValue(uint field, long long defaultValue = -1) const + { + auto index = std::find_if(storage.begin(), storage.end(), + [field](const Field &index) {return index.m_index == field;}); + if (index != storage.end()) { + return index->m_long; + } + return defaultValue; + } +}; +Q_DECLARE_TYPEINFO(AnotherUDSEntry, Q_MOVABLE_TYPE); + +void UdsEntryBenchmark::testAnotherSlaveFill() +{ + testFill(now_time_t, nameStr); +} +void UdsEntryBenchmark::testAnotherSlaveCompare() +{ + testCompare(now_time_t, nameStr); +} +void UdsEntryBenchmark::testAnotherApp() +{ + testApp(now_time_t, nameStr); +} + +// Instead of two vectors, use only one sorted by index and acceded using a binary search. +class AnotherV2UDSEntry +{ +private: + struct Field + { + inline Field() {} + inline Field(const uint index, const QString &value) : m_str(value), m_index(index) {} + inline Field(const uint index, long long value = 0) : m_long(value), m_index(index) { } + // This operator is essential to gain some speed, because the default == is slow + inline bool operator == (const Field &other) const { + return m_index == other.m_index; + } + + QString m_str = QStringLiteral(); + long long m_long = LLONG_MIN; + uint m_index = -1; + }; + std::vector storage; +private: + static inline bool less (const Field &other, const uint index) { + return other.m_index < index; + } + +public: + void reserve(int size) + { + storage.reserve(size); + } + void insert(uint field, const QString &value) + { + auto index = std::lower_bound(storage.begin(), storage.end(), field, less); + Q_ASSERT(index == storage.end() || index->m_index != field); + storage.insert(index, Field(field, value)); + } + void replaceOrInsert(uint field, const QString &value) + { + auto index = std::lower_bound(storage.begin(), storage.end(), field, less); + if (index != storage.end() && index->m_index == field ) { + Q_ASSERT(index->m_long != LLONG_MIN); + index->m_str = value; + return; + } + storage.insert(index, Field(field, value)); + } + void insert(uint field, long long value) + { + auto index = std::lower_bound(storage.begin(), storage.end(), field, less); + Q_ASSERT(index == storage.end() || index->m_index != field); + storage.insert(index, Field(field, value)); + } + void replaceOrInsert(uint field, long long value) + { + auto index = std::lower_bound(storage.begin(), storage.end(), field, less); + if (index != storage.end() && index->m_index == field ) { + Q_ASSERT(index->m_str != QStringLiteral()); + index->m_long = value; + return; + } + storage.insert(index, Field(field, value)); + } + int count() const + { + return storage.size(); + } + QString stringValue(uint field) const + { + auto index = std::lower_bound(storage.cbegin(), storage.cend(), field, less); + if (index != storage.end() && index->m_index == field ) { + return index->m_str; + } + return QString(); + } + long long numberValue(uint field, long long defaultValue = -1) const + { + auto index = std::lower_bound(storage.cbegin(), storage.cend(), field, less); + if (index != storage.end() && index->m_index == field ) { + return index->m_long; + } + return defaultValue; + } +}; +Q_DECLARE_TYPEINFO(AnotherV2UDSEntry, Q_MOVABLE_TYPE); + +void UdsEntryBenchmark::testAnotherV2SlaveFill() +{ + testFill(now_time_t, nameStr); +} +void UdsEntryBenchmark::testAnotherV2SlaveCompare() +{ + testCompare(now_time_t, nameStr); +} +void UdsEntryBenchmark::testAnotherV2App() +{ + testApp(now_time_t, nameStr); +} + + QTEST_MAIN(UdsEntryBenchmark) #include "udsentry_benchmark.moc"