diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tests/dummyresourcewritebenchmark.cpp | 227 |
2 files changed, 232 insertions, 2 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4b10b56..5d64511 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt | |||
@@ -12,7 +12,7 @@ add_definitions(-DTESTDATAPATH="${CMAKE_CURRENT_SOURCE_DIR}/data") | |||
12 | 12 | ||
13 | macro(manual_tests) | 13 | macro(manual_tests) |
14 | foreach(_testname ${ARGN}) | 14 | foreach(_testname ${ARGN}) |
15 | add_executable(${_testname} ${_testname}.cpp testimplementations.cpp) | 15 | add_executable(${_testname} ${_testname}.cpp testimplementations.cpp getrssusage.cpp) |
16 | generate_flatbuffers(${_testname} calendar) | 16 | generate_flatbuffers(${_testname} calendar) |
17 | qt5_use_modules(${_testname} Core Test Concurrent) | 17 | qt5_use_modules(${_testname} Core Test Concurrent) |
18 | target_link_libraries(${_testname} akonadi2common libhawd) | 18 | target_link_libraries(${_testname} akonadi2common libhawd) |
@@ -21,7 +21,7 @@ endmacro(manual_tests) | |||
21 | 21 | ||
22 | macro(auto_tests) | 22 | macro(auto_tests) |
23 | foreach(_testname ${ARGN}) | 23 | foreach(_testname ${ARGN}) |
24 | add_executable(${_testname} ${_testname}.cpp testimplementations.cpp) | 24 | add_executable(${_testname} ${_testname}.cpp testimplementations.cpp getrssusage.cpp) |
25 | generate_flatbuffers(${_testname} calendar) | 25 | generate_flatbuffers(${_testname} calendar) |
26 | add_test(${_testname} ${_testname}) | 26 | add_test(${_testname} ${_testname}) |
27 | qt5_use_modules(${_testname} Core Test Concurrent) | 27 | qt5_use_modules(${_testname} Core Test Concurrent) |
@@ -48,9 +48,12 @@ auto_tests ( | |||
48 | resourcecommunicationtest | 48 | resourcecommunicationtest |
49 | pipelinetest | 49 | pipelinetest |
50 | querytest | 50 | querytest |
51 | databasepopulationandfacadequerybenchmark | ||
52 | dummyresourcewritebenchmark | ||
51 | ) | 53 | ) |
52 | target_link_libraries(dummyresourcetest akonadi2_resource_dummy) | 54 | target_link_libraries(dummyresourcetest akonadi2_resource_dummy) |
53 | target_link_libraries(dummyresourcebenchmark akonadi2_resource_dummy) | 55 | target_link_libraries(dummyresourcebenchmark akonadi2_resource_dummy) |
56 | target_link_libraries(dummyresourcewritebenchmark akonadi2_resource_dummy) | ||
54 | target_link_libraries(querytest akonadi2_resource_dummy) | 57 | target_link_libraries(querytest akonadi2_resource_dummy) |
55 | 58 | ||
56 | if (BUILD_MAILDIR) | 59 | if (BUILD_MAILDIR) |
diff --git a/tests/dummyresourcewritebenchmark.cpp b/tests/dummyresourcewritebenchmark.cpp new file mode 100644 index 0000000..3e8f4c5 --- /dev/null +++ b/tests/dummyresourcewritebenchmark.cpp | |||
@@ -0,0 +1,227 @@ | |||
1 | #include <QtTest> | ||
2 | |||
3 | #include <QString> | ||
4 | |||
5 | #include <iostream> | ||
6 | |||
7 | #include "dummyresource/resourcefactory.h" | ||
8 | #include "dummyresource/domainadaptor.h" | ||
9 | #include "clientapi.h" | ||
10 | #include "commands.h" | ||
11 | #include "entitybuffer.h" | ||
12 | #include "pipeline.h" | ||
13 | #include "log.h" | ||
14 | #include "resourceconfig.h" | ||
15 | #include "definitions.h" | ||
16 | |||
17 | #include "hawd/dataset.h" | ||
18 | #include "hawd/formatter.h" | ||
19 | |||
20 | #include "event_generated.h" | ||
21 | #include "entity_generated.h" | ||
22 | #include "metadata_generated.h" | ||
23 | #include "createentity_generated.h" | ||
24 | |||
25 | #include "getrssusage.h" | ||
26 | |||
27 | static double variance(const QList<double> &values) | ||
28 | { | ||
29 | double mean = 0; | ||
30 | for (auto value : values) { | ||
31 | mean += value; | ||
32 | } | ||
33 | mean = mean / static_cast<double>(values.size()); | ||
34 | double variance = 0; | ||
35 | for (auto value : values) { | ||
36 | variance += pow(static_cast<double>(value) - mean, 2); | ||
37 | } | ||
38 | variance = variance / static_cast<double>(values.size() - 1); | ||
39 | return variance; | ||
40 | } | ||
41 | |||
42 | static double maxDifference(const QList<double> &values) | ||
43 | { | ||
44 | auto max = values.first(); | ||
45 | auto min = values.first(); | ||
46 | for (auto value : values) { | ||
47 | if (value > max) { | ||
48 | max = value; | ||
49 | } | ||
50 | if (value < min) { | ||
51 | min = value; | ||
52 | } | ||
53 | } | ||
54 | return max - min; | ||
55 | } | ||
56 | |||
57 | static QByteArray createEntityBuffer(int &bufferSize) | ||
58 | { | ||
59 | flatbuffers::FlatBufferBuilder eventFbb; | ||
60 | eventFbb.Clear(); | ||
61 | { | ||
62 | auto summary = eventFbb.CreateString("summary"); | ||
63 | Akonadi2::ApplicationDomain::Buffer::EventBuilder eventBuilder(eventFbb); | ||
64 | eventBuilder.add_summary(summary); | ||
65 | auto eventLocation = eventBuilder.Finish(); | ||
66 | Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(eventFbb, eventLocation); | ||
67 | } | ||
68 | |||
69 | flatbuffers::FlatBufferBuilder localFbb; | ||
70 | { | ||
71 | auto uid = localFbb.CreateString("testuid"); | ||
72 | auto localBuilder = Akonadi2::ApplicationDomain::Buffer::EventBuilder(localFbb); | ||
73 | localBuilder.add_uid(uid); | ||
74 | auto location = localBuilder.Finish(); | ||
75 | Akonadi2::ApplicationDomain::Buffer::FinishEventBuffer(localFbb, location); | ||
76 | } | ||
77 | |||
78 | flatbuffers::FlatBufferBuilder entityFbb; | ||
79 | Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, eventFbb.GetBufferPointer(), eventFbb.GetSize(), localFbb.GetBufferPointer(), localFbb.GetSize()); | ||
80 | bufferSize = entityFbb.GetSize(); | ||
81 | |||
82 | flatbuffers::FlatBufferBuilder fbb; | ||
83 | auto type = fbb.CreateString(Akonadi2::ApplicationDomain::getTypeName<Akonadi2::ApplicationDomain::Event>().toStdString().data()); | ||
84 | auto delta = fbb.CreateVector<uint8_t>(entityFbb.GetBufferPointer(), entityFbb.GetSize()); | ||
85 | Akonadi2::Commands::CreateEntityBuilder builder(fbb); | ||
86 | builder.add_domainType(type); | ||
87 | builder.add_delta(delta); | ||
88 | auto location = builder.Finish(); | ||
89 | Akonadi2::Commands::FinishCreateEntityBuffer(fbb, location); | ||
90 | |||
91 | return QByteArray(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize()); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Benchmark writing in the synchronizer process. | ||
96 | */ | ||
97 | class DummyResourceWriteBenchmark : public QObject | ||
98 | { | ||
99 | Q_OBJECT | ||
100 | |||
101 | QList<double> mRssGrowthPerEntity; | ||
102 | QList<double> mTimePerEntity; | ||
103 | |||
104 | void writeInProcess(int num) | ||
105 | { | ||
106 | DummyResource::removeFromDisk("org.kde.dummy.instance1"); | ||
107 | |||
108 | |||
109 | QTime time; | ||
110 | time.start(); | ||
111 | |||
112 | auto pipeline = QSharedPointer<Akonadi2::Pipeline>::create("org.kde.dummy.instance1"); | ||
113 | DummyResource resource("org.kde.dummy.instance1", pipeline); | ||
114 | |||
115 | int bufferSize = 0; | ||
116 | auto command = createEntityBuffer(bufferSize); | ||
117 | |||
118 | const auto startingRss = getCurrentRSS(); | ||
119 | for (int i = 0; i < num; i++) { | ||
120 | resource.processCommand(Akonadi2::Commands::CreateEntityCommand, command); | ||
121 | } | ||
122 | auto appendTime = time.elapsed(); | ||
123 | auto bufferSizeTotal = bufferSize * num; | ||
124 | |||
125 | //Wait until all messages have been processed | ||
126 | resource.processAllMessages().exec().waitForFinished(); | ||
127 | |||
128 | auto allProcessedTime = time.elapsed(); | ||
129 | |||
130 | const auto finalRss = getCurrentRSS(); | ||
131 | const auto rssGrowth = finalRss - startingRss; | ||
132 | //Since the database is memory mapped it is attributted to the resident set size. | ||
133 | const auto rssWithoutDb = finalRss - DummyResource::diskUsage("org.kde.dummy.instance1"); | ||
134 | const auto peakRss = getPeakRSS(); | ||
135 | //How much peak deviates from final rss in percent | ||
136 | const auto percentageRssError = static_cast<double>(peakRss - finalRss)*100.0/static_cast<double>(finalRss); | ||
137 | auto rssGrowthPerEntity = rssGrowth/num; | ||
138 | std::cout << "Current Rss usage [kb]: " << finalRss/1024 << std::endl; | ||
139 | std::cout << "Peak Rss usage [kb]: " << peakRss/1024 << std::endl; | ||
140 | std::cout << "Rss growth [kb]: " << rssGrowth/1024 << std::endl; | ||
141 | std::cout << "Rss growth per entity [byte]: " << rssGrowthPerEntity << std::endl; | ||
142 | std::cout << "Rss without db [kb]: " << rssWithoutDb/1024 << std::endl; | ||
143 | std::cout << "Percentage peak rss error: " << percentageRssError << std::endl; | ||
144 | |||
145 | auto onDisk = DummyResource::diskUsage("org.kde.dummy.instance1"); | ||
146 | auto writeAmplification = static_cast<double>(onDisk) / static_cast<double>(bufferSizeTotal); | ||
147 | std::cout << "On disk [kb]: " << onDisk/1024 << std::endl; | ||
148 | std::cout << "Buffer size total [kb]: " << bufferSizeTotal/1024 << std::endl; | ||
149 | std::cout << "Write amplification: " << writeAmplification << std::endl; | ||
150 | |||
151 | |||
152 | mTimePerEntity << static_cast<double>(allProcessedTime)/static_cast<double>(num); | ||
153 | mRssGrowthPerEntity << rssGrowthPerEntity; | ||
154 | |||
155 | QVERIFY(percentageRssError < 10); | ||
156 | //TODO This is much more than it should it seems, although adding the attachment results in pretty exactly a 1k increase, | ||
157 | //so it doesn't look like that memory is being duplicated. | ||
158 | QVERIFY(rssGrowthPerEntity < 2500); | ||
159 | |||
160 | // HAWD::Dataset dataset("dummy_write_in_process", m_hawdState); | ||
161 | // HAWD::Dataset::Row row = dataset.row(); | ||
162 | // | ||
163 | // row.setValue("rows", num); | ||
164 | // row.setValue("append", (qreal)num/appendTime); | ||
165 | // row.setValue("total", (qreal)num/allProcessedTime); | ||
166 | // dataset.insertRow(row); | ||
167 | // HAWD::Formatter::print(dataset); | ||
168 | |||
169 | // Print memory layout, RSS is what is in memory | ||
170 | // std::system("exec pmap -x \"$PPID\""); | ||
171 | } | ||
172 | |||
173 | |||
174 | private Q_SLOTS: | ||
175 | void initTestCase() | ||
176 | { | ||
177 | Akonadi2::Log::setDebugOutputLevel(Akonadi2::Log::Warning); | ||
178 | } | ||
179 | |||
180 | void cleanup() | ||
181 | { | ||
182 | } | ||
183 | |||
184 | void test1k() | ||
185 | { | ||
186 | writeInProcess(1000); | ||
187 | } | ||
188 | |||
189 | void test2k() | ||
190 | { | ||
191 | writeInProcess(2000); | ||
192 | } | ||
193 | |||
194 | void test5k() | ||
195 | { | ||
196 | writeInProcess(5000); | ||
197 | } | ||
198 | |||
199 | // void test20k() | ||
200 | // { | ||
201 | // writeInProcess(20000); | ||
202 | // } | ||
203 | // | ||
204 | void ensureUsedMemoryRemainsStable() | ||
205 | { | ||
206 | auto rssStandardDeviation = sqrt(variance(mRssGrowthPerEntity)); | ||
207 | auto timeStandardDeviation = sqrt(variance(mTimePerEntity)); | ||
208 | std::cout << "Rss standard deviation " << rssStandardDeviation << std::endl; | ||
209 | std::cout << "Rss max difference [byte]" << maxDifference(mRssGrowthPerEntity) << std::endl; | ||
210 | std::cout << "Time standard deviation " << timeStandardDeviation << std::endl; | ||
211 | std::cout << "Time max difference [ms]" << maxDifference(mTimePerEntity) << std::endl; | ||
212 | QVERIFY(rssStandardDeviation < 1000); | ||
213 | QVERIFY(timeStandardDeviation < 1); | ||
214 | } | ||
215 | |||
216 | //This allows to run individual parts without doing a cleanup, but still cleaning up normally | ||
217 | void testCleanupForCompleteTest() | ||
218 | { | ||
219 | DummyResource::removeFromDisk("org.kde.dummy.instance1"); | ||
220 | } | ||
221 | |||
222 | private: | ||
223 | HAWD::State m_hawdState; | ||
224 | }; | ||
225 | |||
226 | QTEST_MAIN(DummyResourceWriteBenchmark) | ||
227 | #include "dummyresourcewritebenchmark.moc" | ||