summaryrefslogtreecommitdiffstats
path: root/tests/databasepopulationandfacadequerybenchmark.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/databasepopulationandfacadequerybenchmark.cpp')
-rw-r--r--tests/databasepopulationandfacadequerybenchmark.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/tests/databasepopulationandfacadequerybenchmark.cpp b/tests/databasepopulationandfacadequerybenchmark.cpp
new file mode 100644
index 0000000..9a06710
--- /dev/null
+++ b/tests/databasepopulationandfacadequerybenchmark.cpp
@@ -0,0 +1,220 @@
1#include <QtTest>
2
3#include <QString>
4
5#include "testimplementations.h"
6
7#include <common/facade.h>
8#include <common/domainadaptor.h>
9#include <common/resultprovider.h>
10#include <common/synclistresult.h>
11#include <common/definitions.h>
12#include <common/query.h>
13#include <common/clientapi.h>
14
15#include <iostream>
16#include <math.h>
17
18#include "event_generated.h"
19#include "getrssusage.h"
20
21/**
22 * Benchmark read performance of the facade implementation.
23 *
24 * The memory used should grow linearly with the number of retrieved entities.
25 * The memory used should be independent from the database size, after accounting for the memory mapped db.
26 */
27class DatabasePopulationAndFacadeQueryBenchmark : public QObject
28{
29 Q_OBJECT
30
31 QByteArray identifier;
32 QList<double> mRssGrowthPerEntity;
33 QList<double> mTimePerEntity;
34
35 void populateDatabase(int count)
36 {
37 Akonadi2::Storage(Akonadi2::storageLocation(), "identifier", Akonadi2::Storage::ReadWrite).removeFromDisk();
38 //Setup
39 auto domainTypeAdaptorFactory = QSharedPointer<TestEventAdaptorFactory>::create();
40 {
41 Akonadi2::Storage storage(Akonadi2::storageLocation(), identifier, Akonadi2::Storage::ReadWrite);
42 auto transaction = storage.createTransaction(Akonadi2::Storage::ReadWrite);
43 auto db = transaction.openDatabase("event.main");
44
45 int bufferSizeTotal = 0;
46 int keysSizeTotal = 0;
47 QByteArray attachment;
48 attachment.fill('c', 1000);
49 for (int i = 0; i < count; i++) {
50 auto domainObject = Akonadi2::ApplicationDomain::Event::Ptr::create();
51 domainObject->setProperty("uid", "uid");
52 domainObject->setProperty("summary", "summary");
53 domainObject->setProperty("attachment", attachment);
54 flatbuffers::FlatBufferBuilder fbb;
55 domainTypeAdaptorFactory->createBuffer(*domainObject, fbb);
56 const auto buffer = QByteArray::fromRawData(reinterpret_cast<const char*>(fbb.GetBufferPointer()), fbb.GetSize());
57 const auto key = QString::number(i).toLatin1();
58 db.write(key, buffer);
59 bufferSizeTotal += buffer.size();
60 keysSizeTotal += key.size();
61 }
62 transaction.commit();
63
64 auto dataSizeTotal = count * (QByteArray("uid").size() + QByteArray("summary").size() + attachment.size());
65 auto size = db.getSize();
66 auto onDisk = storage.diskUsage();
67 auto writeAmplification = static_cast<double>(onDisk) / static_cast<double>(bufferSizeTotal);
68 std::cout << "Database size [kb]: " << size/1024 << std::endl;
69 std::cout << "On disk [kb]: " << onDisk/1024 << std::endl;
70 std::cout << "Buffer size total [kb]: " << bufferSizeTotal/1024 << std::endl;
71 std::cout << "Key size total [kb]: " << keysSizeTotal/1024 << std::endl;
72 std::cout << "Data size total [kb]: " << dataSizeTotal/1024 << std::endl;
73 std::cout << "Write amplification: " << writeAmplification << std::endl;
74
75 //The buffer has an overhead, but with a reasonable attachment size it should be relatively small
76 //A write amplification of 2 should be the worst case
77 QVERIFY(writeAmplification < 2);
78 }
79 }
80
81 void testLoad(int count)
82 {
83 const auto startingRss = getCurrentRSS();
84
85 Akonadi2::Query query;
86 query.liveQuery = false;
87 query.requestedProperties << "uid" << "summary";
88
89 //Benchmark
90 QTime time;
91 time.start();
92
93 auto resultSet = QSharedPointer<Akonadi2::ResultProvider<Akonadi2::ApplicationDomain::Event::Ptr> >::create();
94 auto resourceAccess = QSharedPointer<TestResourceAccess>::create();
95 TestResourceFacade facade(identifier, resourceAccess);
96
97 auto ret = facade.load(query);
98 ret.first.exec().waitForFinished();
99 auto emitter = ret.second;
100 QList<Akonadi2::ApplicationDomain::Event::Ptr> list;
101 emitter->onAdded([&list](const Akonadi2::ApplicationDomain::Event::Ptr &event) {
102 list << event;
103 });
104 bool done = false;
105 emitter->onInitialResultSetComplete([&done](const Akonadi2::ApplicationDomain::Event::Ptr &event) {
106 done = true;
107 });
108 emitter->fetch(Akonadi2::ApplicationDomain::Event::Ptr());
109 QTRY_VERIFY(done);
110 QCOMPARE(list.size(), count);
111
112 const auto elapsed = time.elapsed();
113
114 const auto finalRss = getCurrentRSS();
115 const auto rssGrowth = finalRss - startingRss;
116 //Since the database is memory mapped it is attributted to the resident set size.
117 const auto rssWithoutDb = finalRss - Akonadi2::Storage(Akonadi2::storageLocation(), identifier, Akonadi2::Storage::ReadWrite).diskUsage();
118 const auto peakRss = getPeakRSS();
119 //How much peak deviates from final rss in percent (should be around 0)
120 const auto percentageRssError = static_cast<double>(peakRss - finalRss)*100.0/static_cast<double>(finalRss);
121 auto rssGrowthPerEntity = rssGrowth/count;
122
123 std::cout << "Loaded " << list.size() << "results." << std::endl;
124 std::cout << "The query took [ms]: " << elapsed << std::endl;
125 std::cout << "Current Rss usage [kb]: " << finalRss/1024 << std::endl;
126 std::cout << "Peak Rss usage [kb]: " << peakRss/1024 << std::endl;
127 std::cout << "Rss growth [kb]: " << rssGrowth/1024 << std::endl;
128 std::cout << "Rss growth per entity [byte]: " << rssGrowthPerEntity << std::endl;
129 std::cout << "Rss without db [kb]: " << rssWithoutDb/1024 << std::endl;
130 std::cout << "Percentage error: " << percentageRssError << std::endl;
131
132 mTimePerEntity << static_cast<double>(elapsed)/static_cast<double>(count);
133 mRssGrowthPerEntity << rssGrowthPerEntity;
134
135 QVERIFY(percentageRssError < 10);
136 //This shouldn't include the attachment (but currently does)
137 QEXPECT_FAIL("", "We're loading the attachment", Continue);
138 QVERIFY(rssGrowthPerEntity < 2000);
139
140 // Print memory layout, RSS is what is in memory
141 // std::system("exec pmap -x \"$PPID\"");
142 // std::system("top -p \"$PPID\" -b -n 1");
143 }
144
145private Q_SLOTS:
146
147 void init()
148 {
149 identifier = "identifier";
150 }
151
152 void test1k()
153 {
154 populateDatabase(1000);
155 testLoad(1000);
156 }
157
158 void test2k()
159 {
160 populateDatabase(2000);
161 testLoad(2000);
162 }
163
164 void test5k()
165 {
166 populateDatabase(5000);
167 testLoad(5000);
168 }
169
170 // void test10k()
171 // {
172 // populateDatabase(10000);
173 // testLoad(10000);
174 // }
175
176 static double variance(const QList<double> &values)
177 {
178 double mean = 0;
179 for (auto value : values) {
180 mean += value;
181 }
182 mean = mean / static_cast<double>(values.size());
183 double variance = 0;
184 for (auto value : values) {
185 variance += pow(static_cast<double>(value) - mean, 2);
186 }
187 variance = variance / static_cast<double>(values.size() - 1);
188 return variance;
189 }
190
191 static double maxDifference(const QList<double> &values)
192 {
193 auto max = values.first();
194 auto min = values.first();
195 for (auto value : values) {
196 if (value > max) {
197 max = value;
198 }
199 if (value < min) {
200 min = value;
201 }
202 }
203 return max - min;
204 }
205
206 void ensureUsedMemoryRemainsStable()
207 {
208 auto rssStandardDeviation = sqrt(variance(mRssGrowthPerEntity));
209 auto timeStandardDeviation = sqrt(variance(mTimePerEntity));
210 std::cout << "Rss standard deviation " << rssStandardDeviation << std::endl;
211 std::cout << "Rss max difference [byte]" << maxDifference(mRssGrowthPerEntity) << std::endl;
212 std::cout << "Time standard deviation " << timeStandardDeviation << std::endl;
213 std::cout << "Time max difference [ms]" << maxDifference(mTimePerEntity) << std::endl;
214 QVERIFY(rssStandardDeviation < 500);
215 QVERIFY(timeStandardDeviation < 1);
216 }
217};
218
219QTEST_MAIN(DatabasePopulationAndFacadeQueryBenchmark)
220#include "databasepopulationandfacadequerybenchmark.moc"