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