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();