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 <chrono>
18 #include <cstdio>
19 #include <sys/prctl.h>
20 #include "thread_pool.h"
21 #include "benchmark_log.h"
22 #include "benchmark_assert.h"
23 #include <unistd.h>
24
25 namespace OHOS {
26 namespace {
27
28 int g_times = 0;
29 bool g_ready = false;
30 std::mutex g_mutex;
31 std::condition_variable g_cv;
32 const int SLEEP_FOR_ONE_SECOND = 1;
33 static bool g_flagTestFuncGetName = true;
34
35 class BenchmarkThreadPoolTest : public benchmark::Fixture {
36 public:
SetUp(const::benchmark::State & state)37 void SetUp(const ::benchmark::State& state) override
38 {
39 }
40
TearDown(const::benchmark::State & state)41 void TearDown(const ::benchmark::State& state) override
42 {
43 }
44
BenchmarkThreadPoolTest()45 BenchmarkThreadPoolTest()
46 {
47 Iterations(iterations);
48 Repetitions(repetitions);
49 ReportAggregatesOnly();
50 }
51
52 ~BenchmarkThreadPoolTest() override = default;
53
54 protected:
55 const int32_t repetitions = 3;
56 const int32_t iterations = 100;
57 };
58
BENCHMARK_F(BenchmarkThreadPoolTest,test_01)59 BENCHMARK_F(BenchmarkThreadPoolTest, test_01)(benchmark::State& state)
60 {
61 BENCHMARK_LOGD("ThreadPoolTest test_01 start.");
62 const int maxTaskNum = 0;
63 const int threadsNum = 0;
64 const int curTaskNum = 0;
65 while (state.KeepRunning()) {
66 ThreadPool pool;
67 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
68 AssertEqual((int)pool.GetMaxTaskNum(), maxTaskNum,
69 "(int)pool.GetMaxTaskNum() did not equal maxTaskNum as expected.", state);
70 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
71 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
72 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
73 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
74 }
75 BENCHMARK_LOGD("ThreadPoolTest test_01 end.");
76 }
77
BENCHMARK_F(BenchmarkThreadPoolTest,test_02)78 BENCHMARK_F(BenchmarkThreadPoolTest, test_02)(benchmark::State& state)
79 {
80 BENCHMARK_LOGD("ThreadPoolTest test_02 start.");
81 const int maxTaskNum = 0;
82 const int threadsNum = 0;
83 const int curTaskNum = 0;
84 while (state.KeepRunning()) {
85 ThreadPool pool("test_02_pool");
86 AssertEqual(pool.GetName(), "test_02_pool",
87 "pool.GetName() did not equal \"test_02_pool\" as expected.", state);
88 AssertEqual((int)pool.GetMaxTaskNum(), maxTaskNum,
89 "(int)pool.GetMaxTaskNum() did not equal maxTaskNum as expected.", state);
90 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
91 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
92 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
93 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
94 }
95 BENCHMARK_LOGD("ThreadPoolTest test_02 end.");
96 }
97
BENCHMARK_F(BenchmarkThreadPoolTest,test_03)98 BENCHMARK_F(BenchmarkThreadPoolTest, test_03)(benchmark::State& state)
99 {
100 BENCHMARK_LOGD("ThreadPoolTest test_03 start.");
101 const int maxTaskNum = 10;
102 const int threadsNum = 0;
103 const int curTaskNum = 0;
104 while (state.KeepRunning()) {
105 ThreadPool pool("test_02_pool");
106 pool.SetMaxTaskNum(maxTaskNum);
107 AssertEqual(pool.GetName(),
108 "test_02_pool", "pool.GetName() did not equal \"test_02_pool\" as expected.", state);
109 AssertEqual((int)pool.GetMaxTaskNum(), maxTaskNum,
110 "(int)pool.GetMaxTaskNum() did not equal maxTaskNum as expected.", state);
111 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
112 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
113 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
114 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
115 }
116 BENCHMARK_LOGD("ThreadPoolTest test_03 end.");
117 }
118
BENCHMARK_F(BenchmarkThreadPoolTest,test_04)119 BENCHMARK_F(BenchmarkThreadPoolTest, test_04)(benchmark::State& state)
120 {
121 BENCHMARK_LOGD("ThreadPoolTest test_04 start.");
122 const int startThreads = 4;
123 const int maxTaskNum = 0;
124 const int threadsNum = 4;
125 const int curTaskNum = 0;
126 while (state.KeepRunning()) {
127 ThreadPool pool;
128 pool.Start(startThreads);
129 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
130 AssertEqual((int)pool.GetMaxTaskNum(), maxTaskNum,
131 "(int)pool.GetMaxTaskNum() did not equal maxTaskNum as expected.", state);
132 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
133 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
134 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
135 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
136
137 // add no task, g_times has no change
138 AssertEqual(g_times, 0, "g_times did not equal 0 as expected.", state);
139 pool.Stop();
140 }
141 BENCHMARK_LOGD("ThreadPoolTest test_04 end.");
142 }
143
TestFuncAddOneTime(int & i)144 void TestFuncAddOneTime(int& i)
145 {
146 std::lock_guard<std::mutex> lock(g_mutex);
147 ++g_times;
148 BENCHMARK_LOGD("ThreadPoolTest TestFuncAddOneTime g_times:%{public}d", g_times);
149 }
150
TestFuncSubOneTime(int & i)151 void TestFuncSubOneTime(int& i)
152 {
153 std::lock_guard<std::mutex> lock(g_mutex);
154 --g_times;
155 BENCHMARK_LOGD("ThreadPoolTest TestFuncSubOneTime g_times:%{public}d", g_times);
156 }
157
AddPoolTaskByForLoop(ThreadPool & pool,const int loopIterations,void (* p)(int &))158 static void AddPoolTaskByForLoop(ThreadPool& pool, const int loopIterations, void(*p)(int&))
159 {
160 for (int i = 0; i < loopIterations; ++i) {
161 auto task = std::bind(p, i);
162 pool.AddTask(task);
163 }
164 }
165
166 // simple task, total task num less than the MaxTaskNum
BENCHMARK_F(BenchmarkThreadPoolTest,test_05)167 BENCHMARK_F(BenchmarkThreadPoolTest, test_05)(benchmark::State& state)
168 {
169 BENCHMARK_LOGD("ThreadPoolTest test_05 start.");
170 const int startThreads = 5;
171 const int threadsNum = 5;
172 const int curTaskNum = 0;
173 const int loopIterations1 = 3;
174 const int loopIterations2 = 2;
175 const int expectedTimesValue = 1;
176 while (state.KeepRunning()) {
177 BENCHMARK_LOGD("ThreadPoolTest test_05 in");
178 ThreadPool pool;
179 pool.Start(startThreads);
180 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
181 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
182 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
183 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
184 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
185
186 AddPoolTaskByForLoop(pool, loopIterations1, TestFuncAddOneTime);
187 AddPoolTaskByForLoop(pool, loopIterations2, TestFuncSubOneTime);
188
189 sleep(SLEEP_FOR_ONE_SECOND);
190 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
191 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
192 // add 5 tasks, add 3 times and sub 2 times
193 BENCHMARK_LOGD("ThreadPoolTest test_05 g_times:%{public}d", g_times);
194 AssertEqual(g_times, expectedTimesValue, "g_times did not equal expectedTimesValue as expected.", state);
195 pool.Stop();
196 g_times = 0;
197 BENCHMARK_LOGD("ThreadPoolTest test_05 off");
198 }
199 BENCHMARK_LOGD("ThreadPoolTest test_05 end.");
200 }
201
202 // simaple task, total task num exceed the MaxTaskNum and the threads num
BENCHMARK_F(BenchmarkThreadPoolTest,test_06)203 BENCHMARK_F(BenchmarkThreadPoolTest, test_06)(benchmark::State& state)
204 {
205 BENCHMARK_LOGD("ThreadPoolTest test_06 start.");
206 const int startThreads = 5;
207 const int threadsNum = 5;
208 const int curTaskNum = 0;
209 const int maxTaskNum = 10;
210 const int loopIterations1 = 8;
211 const int loopIterations2 = 7;
212 const int expectedTimesValue = 1;
213 while (state.KeepRunning()) {
214 ThreadPool pool;
215 pool.Start(startThreads);
216 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
217 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
218 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
219 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
220 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
221
222 pool.SetMaxTaskNum(maxTaskNum);
223
224 AddPoolTaskByForLoop(pool, loopIterations1, TestFuncAddOneTime);
225 AddPoolTaskByForLoop(pool, loopIterations2, TestFuncSubOneTime);
226
227 sleep(SLEEP_FOR_ONE_SECOND);
228 // 1 second should be enough to complete these tasks. if not, this case would be fail
229 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
230 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
231 // add 5 task, add 3 times and sub 2 times
232 AssertEqual(g_times, expectedTimesValue, "g_times did not equal expectedTimesValue as expected.", state);
233 pool.Stop();
234 g_times = 0;
235 }
236 BENCHMARK_LOGD("ThreadPoolTest test_06 end.");
237 }
238
TestFuncAddWait(int & i)239 void TestFuncAddWait(int& i)
240 {
241 BENCHMARK_LOGD("ThreadPoolTest void TestFuncAddWait is called.");
242 std::unique_lock<std::mutex> lk(g_mutex);
243 ++g_times;
244 BENCHMARK_LOGD("ThreadPoolTest TestFuncAddWait i:%{public}d g_times:%{public}d", i, g_times);
245 g_cv.wait(lk, [] {return g_ready;});
246 BENCHMARK_LOGD("ThreadPoolTest TestFuncAddWait received i:%{public}d g_ready:%{public}d", i, g_ready);
247 }
248
TestFuncSubWait(int & i)249 void TestFuncSubWait(int& i)
250 {
251 std::unique_lock<std::mutex> lk(g_mutex);
252 --g_times;
253 BENCHMARK_LOGD("ThreadPoolTest TestFuncSubWait i:%{public}d g_times:%{public}d", i, g_times);
254 g_cv.wait(lk, [] {return g_ready;});
255 BENCHMARK_LOGD("ThreadPoolTest TestFuncSubWait received i:%{public}d g_ready:%{public}d", i, g_ready);
256 }
257
258 // complex task, wait for notify by the main thread
259 // total task num less than the threads num and the MaxTaskNum
BENCHMARK_F(BenchmarkThreadPoolTest,test_07)260 BENCHMARK_F(BenchmarkThreadPoolTest, test_07)(benchmark::State& state)
261 {
262 BENCHMARK_LOGD("ThreadPoolTest test_07 start.");
263 const int startThreads = 5;
264 const int threadsNum = 5;
265 const int curTaskNum = 0;
266 const int loopIterations1 = 3;
267 const int loopIterations2 = 2;
268 const int expectedTimesValue = 1;
269 while (state.KeepRunning()) {
270 ThreadPool pool;
271 pool.Start(startThreads);
272 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
273 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
274 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
275 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
276 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
277
278 AddPoolTaskByForLoop(pool, loopIterations1, TestFuncAddWait);
279 AddPoolTaskByForLoop(pool, loopIterations2, TestFuncSubWait);
280
281 // release cpu proactively, let the task threads go into wait
282 std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_ONE_SECOND));
283 {
284 std::lock_guard<std::mutex> lk(g_mutex);
285 g_ready = true;
286 }
287
288 g_cv.notify_all();
289
290 // these tasks are endless Loop, 5 threads process 5 tasks, zero task remains in the task queue
291 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
292 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
293 // add 5 task, add 3 times and sub 2 times
294 BENCHMARK_LOGD("ThreadPoolTest test_07 g_times:%{public}d", g_times);
295 AssertEqual(g_times, expectedTimesValue, "g_times did not equal expectedTimesValue as expected.", state);
296 pool.Stop();
297 g_times = 0;
298 g_ready = false;
299 }
300 BENCHMARK_LOGD("ThreadPoolTest test_07 end.");
301 }
302
BENCHMARK_F(BenchmarkThreadPoolTest,test_08)303 BENCHMARK_F(BenchmarkThreadPoolTest, test_08)(benchmark::State& state)
304 {
305 BENCHMARK_LOGD("ThreadPoolTest test_08 start.");
306 const int startThreads = 5;
307 const int threadsNum = 5;
308 const int curTaskNum = 0;
309 const int maxTaskNum = 10;
310 const int loopIterations1 = 8;
311 const int loopIterations2 = 7;
312 const int curTaskNum2 = 10;
313 const int expectedTimesValue = 5;
314 const int expectedTimesValue2 = 1;
315 while (state.KeepRunning()) {
316 ThreadPool pool;
317 pool.Start(startThreads);
318 AssertEqual(pool.GetName(), "", "pool.GetName() did not equal \"\" as expected.", state);
319 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
320 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
321 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
322 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
323
324 pool.SetMaxTaskNum(maxTaskNum);
325
326 // ADD 15 tasks
327 AddPoolTaskByForLoop(pool, loopIterations1, TestFuncAddWait);
328 AddPoolTaskByForLoop(pool, loopIterations2, TestFuncSubWait);
329
330 sleep(SLEEP_FOR_ONE_SECOND);
331 // at this time, the first 5 tasks execute and wait for notify, the rest 10 tasks stay in the task queue.
332 BENCHMARK_LOGD("ThreadPoolTest test_08 g_times:%{tastNum}d", (int)pool.GetCurTaskNum());
333 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum2,
334 "(int)pool.GetCurTaskNum() did not equal curTaskNum2 as expected.", state);
335 // FIFO,
336 AssertEqual(g_times, expectedTimesValue, "g_times did not equal expectedTimesValue as expected.", state);
337
338 // notify_all
339 {
340 std::lock_guard<std::mutex> lk(g_mutex);
341 g_ready = true;
342 }
343 g_cv.notify_all();
344
345 // after noity, task thread wake up, and g_ready is true, new tasks didn't need to wait
346 sleep(SLEEP_FOR_ONE_SECOND);
347 // these tasks are endless Loop, and total num of task exceed the MaxTaskNum
348 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
349 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
350 AssertEqual(g_times, expectedTimesValue2, "g_times did not equal expectedTimesValue2 as expected.", state);
351 pool.Stop();
352 g_times = 0;
353 g_ready = false;
354 }
355 BENCHMARK_LOGD("ThreadPoolTest test_08 end.");
356 }
357
TestFuncGetName(const std::string & poolName)358 void TestFuncGetName(const std::string& poolName)
359 {
360 BENCHMARK_LOGD("ThreadPoolTest void TestFuncGetName is called.");
361 char name[16];
362 prctl(PR_GET_NAME, name);
363 std::string nameStr(name);
364 size_t found = nameStr.find(poolName);
365 if (found != 0) {
366 g_flagTestFuncGetName = false;
367 }
368 }
369
370 /*
371 * Test_09 is used to verify the name set to ThreadPool will be set as the real name of threads in pool.
372 */
BENCHMARK_F(BenchmarkThreadPoolTest,test_09)373 BENCHMARK_F(BenchmarkThreadPoolTest, test_09)(benchmark::State& state)
374 {
375 BENCHMARK_LOGD("ThreadPoolTest test_09 start.");
376 const int startThreads = 5;
377 const int threadsNum = 5;
378 const int curTaskNum = 0;
379 const int loopIterations = 5;
380 while (state.KeepRunning()) {
381 std::string poolName("test_09_pool");
382 ThreadPool pool(poolName);
383 pool.Start(startThreads);
384 AssertEqual(pool.GetName(), poolName, "pool.GetName() did not equal poolName as expected.", state);
385 AssertEqual((int)pool.GetThreadsNum(), threadsNum,
386 "(int)pool.GetThreadsNum() did not equal threadsNum as expected.", state);
387 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
388 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
389
390 for (int i = 0; i < loopIterations; ++i) {
391 auto task = std::bind(TestFuncGetName, poolName);
392 AssertTrue(g_flagTestFuncGetName, "found did not equal true as expected.", state);
393 pool.AddTask(task);
394 }
395
396 sleep(SLEEP_FOR_ONE_SECOND);
397 // these tasks are endless Loop, 5 threads process 5 tasks, zero task remains in the task queue
398 AssertEqual((int)pool.GetCurTaskNum(), curTaskNum,
399 "(int)pool.GetCurTaskNum() did not equal curTaskNum as expected.", state);
400 pool.Stop();
401 }
402 BENCHMARK_LOGD("ThreadPoolTest test_09 end.");
403 }
404 } // namespace
405 } // namespace OHOS
406 // Run the benchmark
407 BENCHMARK_MAIN();