1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <benchmark/benchmark.h>
17 #include "safe_queue.h"
18 #include <array>
19 #include <future>
20 #include <iostream>
21 #include <thread>
22 #include <chrono>
23 #include "../log.h"
24 #include "../assert.h"
25 using namespace std;
26
27 namespace OHOS {
28 namespace {
29
30 const unsigned int QUEUE_SLOTS = 10;
31 const unsigned int THREAD_NUM = QUEUE_SLOTS + 1;
32 const int TIME_INCREMENT = 2;
33 const int SLEEP_FOR_TWO_SECONDS = 2;
34 const int SLEEP_FOR_THREE_SECONDS = 3;
35 const int SLEEP_FOR_FOUR_SECONDS = 4;
36
37 class BenchmarkSafeQueue : public benchmark::Fixture {
38 public:
BenchmarkSafeQueue()39 BenchmarkSafeQueue()
40 {
41 Iterations(iterations);
42 Repetitions(repetitions);
43 ReportAggregatesOnly();
44 }
45
46 ~BenchmarkSafeQueue() override = default;
SetUp(const::benchmark::State & state)47 void SetUp(const ::benchmark::State& state) override
48 {
49 }
50
TearDown(const::benchmark::State & state)51 void TearDown(const ::benchmark::State& state) override
52 {
53 }
54
55 protected:
56 const int32_t repetitions = 3;
57 const int32_t iterations = 50;
58 };
59
60 class DemoThreadData {
61 public:
DemoThreadData()62 DemoThreadData()
63 {
64 putStatus = false;
65 getStatus = false;
66 eraseStatus = false;
67 emptyStatus = false;
68 }
69 static SafeQueue<int> shareQueue;
70 bool putStatus;
71 bool getStatus;
72 bool eraseStatus;
73 bool emptyStatus;
74
Put(int i)75 void Put(int i)
76 {
77 shareQueue.Push(i);
78 putStatus = true;
79 }
80
Get(int & i)81 void Get(int &i)
82 {
83 shareQueue.Pop(i);
84 getStatus = true;
85 }
86
Erase(int & i)87 void Erase(int &i)
88 {
89 shareQueue.Erase(i);
90 eraseStatus = true;
91 }
92
Empty()93 void Empty()
94 {
95 shareQueue.Empty();
96 emptyStatus = true;
97 }
98 };
99
100 SafeQueue<int> DemoThreadData::shareQueue;
101
PutHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)102 void PutHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
103 {
104 BENCHMARK_LOGD("SafeQueue PutHandleThreadDataTime is called i:%{public}d .", i);
105 std::this_thread::sleep_until(absTime);
106
107 q.Put(i);
108 }
109
GetHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)110 void GetHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
111 {
112 BENCHMARK_LOGD("SafeQueue GetHandleThreadDataTime is called i:%{public}d.", i);
113 std::this_thread::sleep_until(absTime);
114 int t = 0;
115 q.Get(t);
116 }
117
EraseHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)118 void EraseHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
119 {
120 BENCHMARK_LOGD("SafeQueue EraseHandleThreadDataTime is called i:%{public}d.", i);
121 std::this_thread::sleep_until(absTime);
122
123 q.Erase(i);
124 }
125
EmptyHandleThreadDataTime(DemoThreadData & q,std::chrono::system_clock::time_point absTime)126 void EmptyHandleThreadDataTime(DemoThreadData &q, std::chrono::system_clock::time_point absTime)
127 {
128 BENCHMARK_LOGD("SafeQueue EmptyHandleThreadDataTime is called.");
129 std::this_thread::sleep_until(absTime);
130
131 q.Empty();
132 }
133
134 class TestThreading {
135 public:
TestThreading()136 TestThreading()
137 {
138 demoDatas.fill(DemoThreadData());
139 }
140
AllThreadPut(std::time_t & timeT)141 void AllThreadPut(std::time_t &timeT)
142 {
143 using std::chrono::system_clock;
144 for (unsigned int i = 0; i < THREAD_NUM; i++) {
145 threads[i] = std::thread(PutHandleThreadDataTime,
146 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
147 }
148 }
AllThreadGet(std::time_t & timeT)149 void AllThreadGet(std::time_t &timeT)
150 {
151 using std::chrono::system_clock;
152 for (unsigned int i = 0; i < THREAD_NUM; i++) {
153 threads[i] = std::thread(GetHandleThreadDataTime,
154 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
155 }
156 }
157
AllThreadErase(std::time_t & timeT)158 void AllThreadErase(std::time_t &timeT)
159 {
160 using std::chrono::system_clock;
161 for (unsigned int i = 0; i < THREAD_NUM; i++) {
162 threads[i] = std::thread(EraseHandleThreadDataTime,
163 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
164 }
165 }
166
AllThreadEmpty(std::time_t & timeT)167 void AllThreadEmpty(std::time_t &timeT)
168 {
169 using std::chrono::system_clock;
170 for (unsigned int i = 0; i < THREAD_NUM; i++) {
171 threads[i] = std::thread(EmptyHandleThreadDataTime,
172 std::ref(demoDatas[i]), system_clock::from_time_t(timeT));
173 }
174 }
175
GetThreadDatePushedStatus(unsigned int & pushedIn,unsigned int & unpushedIn)176 void GetThreadDatePushedStatus(unsigned int &pushedIn, unsigned int &unpushedIn)
177 {
178 pushedIn = 0;
179 unpushedIn = 0;
180 for (auto &t : demoDatas) {
181 if (t.putStatus) {
182 pushedIn++;
183 } else {
184 unpushedIn++;
185 }
186 }
187 BENCHMARK_LOGD("SafeQueue GetThreadDatePushedStatus pIn:%{public}d upIn:%{public}d.", pushedIn, unpushedIn);
188 }
189
GetThreadDateGetedStatus(unsigned int & getedOut,unsigned int & ungetedOut)190 void GetThreadDateGetedStatus(unsigned int &getedOut, unsigned int &ungetedOut)
191 {
192 BENCHMARK_LOGD("SafeQueue void GetThreadDateGetedStatus is called.");
193 getedOut = 0;
194 ungetedOut = 0;
195 for (auto &t : demoDatas) {
196 if (t.getStatus) {
197 getedOut++;
198 } else {
199 ungetedOut++;
200 }
201 }
202 BENCHMARK_LOGD("SafeQueue GetThreadDateGetedStatus gOut:%{public}d uOut:%{public}d.", getedOut, ungetedOut);
203 }
204
GetThreadDateEraseStatus(unsigned int & erase,unsigned int & unErase)205 void GetThreadDateEraseStatus(unsigned int &erase, unsigned int &unErase)
206 {
207 erase = 0;
208 unErase = 0;
209 for (auto &t : demoDatas) {
210 if (t.eraseStatus) {
211 erase++;
212 } else {
213 unErase++;
214 }
215 }
216 BENCHMARK_LOGD("SafeQueue GetThreadDateEraseStatus erase:%{public}d unErase:%{public}d.", erase, unErase);
217 }
218
GetThreadDateEmptyStatus(unsigned int & empty,unsigned int & unEmpty)219 void GetThreadDateEmptyStatus(unsigned int &empty, unsigned int &unEmpty)
220 {
221 empty = 0;
222 unEmpty = 0;
223 for (auto &t : demoDatas) {
224 if (t.emptyStatus) {
225 empty++;
226 } else {
227 unEmpty++;
228 }
229 }
230 BENCHMARK_LOGD("SafeQueue GetThreadDateEmptyStatus empty:%{public}d unEmpty:%{public}d.", empty, unEmpty);
231 }
232
ResetStatus()233 void ResetStatus()
234 {
235 BENCHMARK_LOGD("SafeQueue void ResetStatus is called.");
236 for (auto &t : threads) {
237 t.join();
238 }
239
240 DemoThreadData::shareQueue.Clear();
241 }
242
243 std::thread threads[THREAD_NUM];
244 std::array<DemoThreadData, THREAD_NUM> demoDatas;
245 };
246
247 /*
248 * Feature: SafeBlockQueue
249 * Function:put
250 * SubFunction: NA
251 * FunctionPoints:
252 * EnvConditions: NA
253 * CaseDescription: Multiple threads put, one thread gets, all threads finish running normally
254 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadPutAndOneThreadGetOnemptyQueue)255 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadPutAndOneThreadGetOnemptyQueue)(benchmark::State& state)
256 {
257 BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndOneThreadGetOnemptyQueue start.");
258 while (state.KeepRunning()) {
259 TestThreading testThread;
260 using std::chrono::system_clock;
261 std::time_t timeT = system_clock::to_time_t(system_clock::now());
262 timeT += TIME_INCREMENT;
263 testThread.AllThreadPut(timeT);
264
265 // 1. queue is full and some threads is blocked
266 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_THREE_SECONDS));
267 AssertTrue((DemoThreadData::shareQueue.Size() > 0),
268 "DemoThreadData::shareQueue.Size() > 0 did not equal true as expected.", state);
269
270 unsigned int pushedIn = 0;
271 unsigned int unpushedIn = 0;
272
273 testThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
274 AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
275
276 //2. get one out and wait some put in
277 for (unsigned int i = 0; i < THREAD_NUM; i++) {
278 int t = 0;
279 testThread.demoDatas[0].Get(t);
280 }
281
282 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_TWO_SECONDS));
283 // queue is full and some threads is blocked and is not joined
284 AssertTrue((DemoThreadData::shareQueue.Size() == 0),
285 "DemoThreadData::shareQueue.Size() == 0 did not equal true as expected.", state);
286
287 // here means all thread end ok or if some operation blocked and the testcase blocked
288 testThread.ResetStatus();
289 }
290 BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndOneThreadGetOnemptyQueue end.");
291 }
292
293 /*
294 * Feature: SafeBlockQueue
295 * Function:put
296 * SubFunction: NA
297 * FunctionPoints:
298 * EnvConditions: NA
299 * CaseDescription: Multi-threaded put() and Multi-threaded get() on the empty queue.
300 * When all threads are waiting to reach a certain
301 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
302 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadPutAndGetConcurrently)303 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadPutAndGetConcurrently)(benchmark::State& state)
304 {
305 BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndGetConcurrently start.");
306 while (state.KeepRunning()) {
307 using std::chrono::system_clock;
308 std::time_t timeT = system_clock::to_time_t(system_clock::now());
309 timeT += TIME_INCREMENT;
310
311 TestThreading putInTestThread;
312 putInTestThread.AllThreadPut(timeT);
313
314 TestThreading getOutTestThread;
315 getOutTestThread.AllThreadGet(timeT);
316
317 // 1. queue is full and some threads is blocked
318 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_FOUR_SECONDS));
319
320 unsigned int pushedIn = 0;
321 unsigned int unpushedIn = 0;
322 unsigned int getedOut = 0;
323 unsigned int ungetedOut = 0;
324 putInTestThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
325 getOutTestThread.GetThreadDateGetedStatus(getedOut, ungetedOut);
326 AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
327 AssertEqual(getedOut, THREAD_NUM, "getedOut did not equal THREAD_NUM as expected.", state);
328
329 putInTestThread.ResetStatus();
330 getOutTestThread.ResetStatus();
331 }
332 BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndGetConcurrently end.");
333 }
334
335 /*
336 * Feature: SafeBlockQueue
337 * Function:put
338 * SubFunction: NA
339 * FunctionPoints:
340 * EnvConditions: NA
341 * CaseDescription: Multi-threaded put() and Multi-threaded get() on the not empty queue.
342 * When all threads are waiting to reach a certain
343 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
344 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadConcurrentGetAndPopInNotEmptyQueue)345 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadConcurrentGetAndPopInNotEmptyQueue)(benchmark::State& state)
346 {
347 BENCHMARK_LOGD("SafeQueue testMutilthreadConcurrentGetAndPopInNotEmptyQueue start.");
348 while (state.KeepRunning()) {
349 //1. prepare
350 using std::chrono::system_clock;
351 std::time_t timeT = system_clock::to_time_t(system_clock::now());
352 timeT += TIME_INCREMENT;
353
354 AssertTrue((DemoThreadData::shareQueue.Size() == 0),
355 "DemoThreadData::shareQueue.Size() == 0 did not equal true as expected.", state);
356
357 int t = 1;
358 for (unsigned int i = 0; i < THREAD_NUM; i++) {
359 DemoThreadData::shareQueue.Push(t);
360 }
361
362 AssertTrue((DemoThreadData::shareQueue.Size() == THREAD_NUM),
363 "DemoThreadData::shareQueue.Size() == THREAD_NUM did not equal true as expected.", state);
364
365 //2. start thread put in not full queue
366 TestThreading putInTestThread;
367 putInTestThread.AllThreadPut(timeT);
368
369 TestThreading getOutTestThread;
370 getOutTestThread.AllThreadGet(timeT);
371
372 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_THREE_SECONDS));
373 AssertTrue((DemoThreadData::shareQueue.Size() == THREAD_NUM),
374 "DemoThreadData::shareQueue.Size() == THREAD_NUM did not equal true as expected.", state);
375
376 unsigned int getedOut = 0;
377 unsigned int ungetedOut = 0;
378 unsigned int pushedIn = 0;
379 unsigned int unpushedIn = 0;
380 getOutTestThread.GetThreadDateGetedStatus(getedOut, ungetedOut);
381 putInTestThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
382 AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
383 AssertEqual(getedOut, THREAD_NUM, "getedOut did not equal THREAD_NUM as expected.", state);
384
385 // 3. reset status
386 putInTestThread.ResetStatus();
387 getOutTestThread.ResetStatus();
388 }
389 BENCHMARK_LOGD("SafeQueue testMutilthreadConcurrentGetAndPopInNotEmptyQueue end.");
390 }
391
392 /*
393 * Feature: SafeBlockQueue
394 * Function:erase empty
395 * SubFunction: NA
396 * FunctionPoints:
397 * EnvConditions: NA
398 * CaseDescription: Multi-threaded erase() and Multi-threaded empty() on the empty queue.
399 * When all threads are waiting to reach a certain
400 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
401 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadEraseAndEmptyConcurrently)402 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadEraseAndEmptyConcurrently)(benchmark::State& state)
403 {
404 BENCHMARK_LOGD("SafeQueue testMutilthreadEraseAndEmptyConcurrently start.");
405 while (state.KeepRunning()) {
406 using std::chrono::system_clock;
407 std::time_t timeT = system_clock::to_time_t(system_clock::now());
408 timeT += TIME_INCREMENT;
409
410 TestThreading putThread;
411 putThread.AllThreadPut(timeT);
412
413 TestThreading eraseThread;
414 eraseThread.AllThreadErase(timeT);
415
416 TestThreading emptyThread;
417 emptyThread.AllThreadEmpty(timeT);
418 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_FOUR_SECONDS));
419
420 unsigned int pushedIn = 0;
421 unsigned int unpushedIn = 0;
422 unsigned int erase = 0;
423 unsigned int unerase = 0;
424 unsigned int empty = 0;
425 unsigned int unempty = 0;
426 putThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
427 eraseThread.GetThreadDateEraseStatus(erase, unerase);
428 emptyThread.GetThreadDateEmptyStatus(empty, unempty);
429 AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
430 AssertEqual(erase, THREAD_NUM, "erase did not equal THREAD_NUM as expected.", state);
431 AssertEqual(empty, THREAD_NUM, "empty did not equal THREAD_NUM as expected.", state);
432
433 putThread.ResetStatus();
434 eraseThread.ResetStatus();
435 emptyThread.ResetStatus();
436 }
437 BENCHMARK_LOGD("SafeQueue testMutilthreadEraseAndEmptyConcurrently end.");
438 }
439 } // namespace
440 } // namespace OHOS
441 // Run the benchmark
442 BENCHMARK_MAIN();