• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 <gtest/gtest.h>
17 
18 #include <chrono>
19 #include <thread>
20 
21 #include "assembly-parser.h"
22 #include "runtime/compiler_queue_aged_counter_priority.h"
23 #include "runtime/compiler_queue_counter_priority.h"
24 #include "runtime/include/class-inl.h"
25 #include "runtime/include/method.h"
26 #include "runtime/include/runtime.h"
27 
28 namespace panda::test {
29 
30 class CompilerQueueTest : public testing::Test {
31 public:
CompilerQueueTest()32     CompilerQueueTest()
33     {
34         RuntimeOptions options;
35         options.SetShouldLoadBootPandaFiles(false);
36         options.SetShouldInitializeIntrinsics(false);
37         Runtime::Create(options);
38         thread_ = panda::MTManagedThread::GetCurrent();
39         thread_->ManagedCodeBegin();
40     }
41 
~CompilerQueueTest()42     ~CompilerQueueTest()
43     {
44         thread_->ManagedCodeEnd();
45         Runtime::Destroy();
46     }
47 
48 protected:
49     panda::MTManagedThread *thread_;
50 };
51 
TestClassPrepare()52 static Class *TestClassPrepare()
53 {
54     auto source = R"(
55         .function i32 g() {
56             ldai 0
57             return
58         }
59 
60         .function i32 f() {
61             ldai 0
62             return
63         }
64 
65         .function void main() {
66             call f
67             return.void
68         }
69     )";
70     pandasm::Parser p;
71 
72     auto res = p.Parse(source);
73     auto pf = pandasm::AsmEmitter::Emit(res.Value());
74 
75     ClassLinker *class_linker = Runtime::GetCurrent()->GetClassLinker();
76     class_linker->AddPandaFile(std::move(pf));
77 
78     PandaString descriptor;
79 
80     Class *klass = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY)
81                        ->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), &descriptor));
82     return klass;
83 }
84 
GetAndCheckMethodIfExists(CompilerQueueInterface * queue,Method * target)85 static void GetAndCheckMethodIfExists(CompilerQueueInterface *queue, Method *target)
86 {
87     auto method = queue->GetTask().GetMethod();
88     // The element may expire and may be deleted
89     if (method != nullptr) {
90         ASSERT_EQ(method, target);
91     }
92 }
93 
WaitForExpire(uint millis)94 static void WaitForExpire(uint millis)
95 {
96     constexpr uint delta = 10;
97     uint64_t startTime = time::GetCurrentTimeInMillis();
98     std::this_thread::sleep_for(std::chrono::milliseconds(millis));
99     // sleep_for() works nondeterministically
100     // use an additional check for more confidence
101     // Note, the queue implementation uses GetCurrentTimeInMillis
102     // to update aged counter
103     while (time::GetCurrentTimeInMillis() < startTime + millis) {
104         std::this_thread::sleep_for(std::chrono::milliseconds(delta));
105     }
106 }
107 
108 // Testing of CounterQueue
109 
TEST_F(CompilerQueueTest,AddGet)110 TEST_F(CompilerQueueTest, AddGet)
111 {
112     Class *klass = TestClassPrepare();
113 
114     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
115     ASSERT_NE(main_method, nullptr);
116 
117     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
118     ASSERT_NE(f_method, nullptr);
119 
120     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
121     ASSERT_NE(g_method, nullptr);
122 
123     // Manual range
124     main_method->SetHotnessCounter(1);
125     f_method->SetHotnessCounter(2);
126     g_method->SetHotnessCounter(3);
127 
128     RuntimeOptions options;
129     CompilerPriorityCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
130                                        options.GetCompilerQueueMaxLength(), options.GetCompilerTaskLifeSpan());
131     queue.AddTask(CompilerTask {main_method, 0});
132     queue.AddTask(CompilerTask {f_method, 0});
133     queue.AddTask(CompilerTask {g_method, 0});
134 
135     GetAndCheckMethodIfExists(&queue, g_method);
136     GetAndCheckMethodIfExists(&queue, f_method);
137     GetAndCheckMethodIfExists(&queue, main_method);
138 }
139 
TEST_F(CompilerQueueTest,EqualCounters)140 TEST_F(CompilerQueueTest, EqualCounters)
141 {
142     Class *klass = TestClassPrepare();
143 
144     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
145     ASSERT_NE(main_method, nullptr);
146 
147     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
148     ASSERT_NE(f_method, nullptr);
149 
150     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
151     ASSERT_NE(g_method, nullptr);
152 
153     // Manual range
154     main_method->SetHotnessCounter(3);
155     f_method->SetHotnessCounter(3);
156     g_method->SetHotnessCounter(3);
157 
158     RuntimeOptions options;
159     CompilerPriorityCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
160                                        options.GetCompilerQueueMaxLength(), options.GetCompilerTaskLifeSpan());
161 
162     queue.AddTask(CompilerTask {f_method, 0});
163     queue.AddTask(CompilerTask {g_method, 0});
164     queue.AddTask(CompilerTask {main_method, 0});
165 
166     GetAndCheckMethodIfExists(&queue, f_method);
167     GetAndCheckMethodIfExists(&queue, g_method);
168     GetAndCheckMethodIfExists(&queue, main_method);
169 }
170 
TEST_F(CompilerQueueTest,Expire)171 TEST_F(CompilerQueueTest, Expire)
172 {
173     auto klass = TestClassPrepare();
174 
175     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
176     ASSERT_NE(main_method, nullptr);
177 
178     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
179     ASSERT_NE(f_method, nullptr);
180 
181     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
182     ASSERT_NE(g_method, nullptr);
183 
184     RuntimeOptions options;
185     constexpr int CompilerTaskLifeSpan1 = 500;
186     CompilerPriorityCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
187                                        options.GetCompilerQueueMaxLength(), CompilerTaskLifeSpan1);
188     queue.AddTask(CompilerTask {main_method, 0});
189     queue.AddTask(CompilerTask {f_method, 0});
190     queue.AddTask(CompilerTask {g_method, 0});
191 
192     WaitForExpire(1000);
193 
194     // All tasks should expire after sleep
195     auto method = queue.GetTask().GetMethod();
196     ASSERT_EQ(method, nullptr);
197 
198     constexpr int CompilerTaskLifeSpan2 = 0;
199     CompilerPriorityCounterQueue queue2(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
200                                         options.GetCompilerQueueMaxLength(), CompilerTaskLifeSpan2);
201     queue2.AddTask(CompilerTask {main_method, 0});
202     queue2.AddTask(CompilerTask {f_method, 0});
203     queue2.AddTask(CompilerTask {g_method, 0});
204 
205     // All tasks should expire without sleep
206     method = queue2.GetTask().GetMethod();
207     ASSERT_EQ(method, nullptr);
208 }
209 
TEST_F(CompilerQueueTest,Reorder)210 TEST_F(CompilerQueueTest, Reorder)
211 {
212     auto klass = TestClassPrepare();
213 
214     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
215     ASSERT_NE(main_method, nullptr);
216 
217     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
218     ASSERT_NE(f_method, nullptr);
219 
220     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
221     ASSERT_NE(g_method, nullptr);
222 
223     main_method->SetHotnessCounter(1);
224     f_method->SetHotnessCounter(2);
225     g_method->SetHotnessCounter(3);
226 
227     RuntimeOptions options;
228     CompilerPriorityCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
229                                        options.GetCompilerQueueMaxLength(), options.GetCompilerTaskLifeSpan());
230 
231     // It is possible, that the first added method is expired, and others are not
232     // So, add to queue in reversed order to be sure, that the first method is present anyway
233     queue.AddTask(CompilerTask {g_method, 0});
234     queue.AddTask(CompilerTask {f_method, 0});
235     queue.AddTask(CompilerTask {main_method, 0});
236 
237     // Change the order
238     main_method->SetHotnessCounter(6);
239     f_method->SetHotnessCounter(5);
240     g_method->SetHotnessCounter(4);
241 
242     GetAndCheckMethodIfExists(&queue, main_method);
243     GetAndCheckMethodIfExists(&queue, f_method);
244     GetAndCheckMethodIfExists(&queue, g_method);
245 }
246 
TEST_F(CompilerQueueTest,MaxLimit)247 TEST_F(CompilerQueueTest, MaxLimit)
248 {
249     auto klass = TestClassPrepare();
250 
251     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
252     ASSERT_NE(main_method, nullptr);
253 
254     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
255     ASSERT_NE(f_method, nullptr);
256 
257     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
258     ASSERT_NE(g_method, nullptr);
259 
260     main_method->SetHotnessCounter(1);
261     f_method->SetHotnessCounter(2);
262     g_method->SetHotnessCounter(3);
263 
264     RuntimeOptions options;
265     constexpr int CompilerQueueMaxLength1 = 100;
266     CompilerPriorityCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
267                                        CompilerQueueMaxLength1, options.GetCompilerTaskLifeSpan());
268 
269     for (int i = 0; i < 40; i++) {
270         queue.AddTask(CompilerTask {main_method, 0});
271         queue.AddTask(CompilerTask {f_method, 0});
272         queue.AddTask(CompilerTask {g_method, 0});
273     }
274 
275     // 100 as Max_Limit
276     for (int i = 0; i < 100; i++) {
277         queue.GetTask();
278         // Can not check as the task may expire on an overloaded machine
279     }
280 
281     auto method = queue.GetTask().GetMethod();
282     ASSERT_EQ(method, nullptr);
283 
284     // check an option
285     constexpr int CompilerQueueMaxLength2 = 1;
286     CompilerPriorityCounterQueue queue2(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
287                                         CompilerQueueMaxLength2, options.GetCompilerTaskLifeSpan());
288 
289     queue2.AddTask(CompilerTask {main_method, 0});
290     queue2.AddTask(CompilerTask {f_method, 0});
291     queue2.AddTask(CompilerTask {g_method, 0});
292 
293     method = queue2.GetTask().GetMethod();
294     method = queue2.GetTask().GetMethod();
295     ASSERT_EQ(method, nullptr);
296 }
297 
298 // Testing of AgedCounterQueue
299 
TEST_F(CompilerQueueTest,AgedAddGet)300 TEST_F(CompilerQueueTest, AgedAddGet)
301 {
302     Class *klass = TestClassPrepare();
303 
304     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
305     ASSERT_NE(main_method, nullptr);
306 
307     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
308     ASSERT_NE(f_method, nullptr);
309 
310     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
311     ASSERT_NE(g_method, nullptr);
312 
313     // Manual range
314     main_method->SetHotnessCounter(1000);
315     f_method->SetHotnessCounter(1200);
316     g_method->SetHotnessCounter(1300);
317 
318     RuntimeOptions options;
319     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
320                                            options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
321                                            options.GetCompilerEpochDuration());
322     queue.AddTask(CompilerTask {main_method, 0});
323     queue.AddTask(CompilerTask {f_method, 0});
324     queue.AddTask(CompilerTask {g_method, 0});
325 
326     GetAndCheckMethodIfExists(&queue, g_method);
327     GetAndCheckMethodIfExists(&queue, f_method);
328     GetAndCheckMethodIfExists(&queue, main_method);
329 }
330 
TEST_F(CompilerQueueTest,AgedEqualCounters)331 TEST_F(CompilerQueueTest, AgedEqualCounters)
332 {
333     Class *klass = TestClassPrepare();
334 
335     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
336     ASSERT_NE(main_method, nullptr);
337 
338     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
339     ASSERT_NE(g_method, nullptr);
340 
341     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
342     ASSERT_NE(f_method, nullptr);
343 
344     // Manual range
345     main_method->SetHotnessCounter(3000);
346     g_method->SetHotnessCounter(3000);
347     f_method->SetHotnessCounter(3000);
348 
349     RuntimeOptions options;
350     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
351                                            options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
352                                            options.GetCompilerEpochDuration());
353     // Add in reversed order, as methods with equal counters will be ordered by timestamp
354     queue.AddTask(CompilerTask {f_method, 0});
355     queue.AddTask(CompilerTask {g_method, 0});
356     queue.AddTask(CompilerTask {main_method, 0});
357 
358     GetAndCheckMethodIfExists(&queue, f_method);
359     GetAndCheckMethodIfExists(&queue, g_method);
360     GetAndCheckMethodIfExists(&queue, main_method);
361 }
362 
TEST_F(CompilerQueueTest,AgedExpire)363 TEST_F(CompilerQueueTest, AgedExpire)
364 {
365     auto klass = TestClassPrepare();
366 
367     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
368     ASSERT_NE(main_method, nullptr);
369 
370     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
371     ASSERT_NE(f_method, nullptr);
372 
373     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
374     ASSERT_NE(g_method, nullptr);
375 
376     main_method->SetHotnessCounter(1000);
377     f_method->SetHotnessCounter(1000);
378     g_method->SetHotnessCounter(1000);
379 
380     RuntimeOptions options;
381     constexpr int CompilerEpochDuration1 = 500;
382     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
383                                            options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
384                                            CompilerEpochDuration1);
385     queue.AddTask(CompilerTask {main_method, 0});
386     queue.AddTask(CompilerTask {f_method, 0});
387     queue.AddTask(CompilerTask {g_method, 0});
388 
389     WaitForExpire(1600);
390 
391     // All tasks should expire after sleep
392     auto method = queue.GetTask().GetMethod();
393     ASSERT_EQ(method, nullptr);
394 
395     constexpr int CompilerEpochDuration2 = 1;
396     CompilerPriorityAgedCounterQueue queue2(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
397                                             options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
398                                             CompilerEpochDuration2);
399 
400     queue2.AddTask(CompilerTask {main_method, 0});
401     queue2.AddTask(CompilerTask {f_method, 0});
402     queue2.AddTask(CompilerTask {g_method, 0});
403 
404     WaitForExpire(5);
405 
406     method = queue2.GetTask().GetMethod();
407     ASSERT_EQ(method, nullptr);
408 }
409 
TEST_F(CompilerQueueTest,AgedReorder)410 TEST_F(CompilerQueueTest, AgedReorder)
411 {
412     auto klass = TestClassPrepare();
413 
414     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
415     ASSERT_NE(main_method, nullptr);
416 
417     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
418     ASSERT_NE(f_method, nullptr);
419 
420     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
421     ASSERT_NE(g_method, nullptr);
422 
423     main_method->SetHotnessCounter(1500);
424     f_method->SetHotnessCounter(2000);
425     g_method->SetHotnessCounter(3000);
426 
427     RuntimeOptions options;
428     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
429                                            options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
430                                            options.GetCompilerEpochDuration());
431     // It is possible, that the first added method is expired, and others are not
432     // So, add to queue in reversed order to be sure, that the first method is present anyway
433     queue.AddTask(CompilerTask {g_method, 0});
434     queue.AddTask(CompilerTask {f_method, 0});
435     queue.AddTask(CompilerTask {main_method, 0});
436 
437     // Change the order
438     main_method->SetHotnessCounter(6000);
439     f_method->SetHotnessCounter(5000);
440     g_method->SetHotnessCounter(4000);
441 
442     GetAndCheckMethodIfExists(&queue, main_method);
443     GetAndCheckMethodIfExists(&queue, f_method);
444     GetAndCheckMethodIfExists(&queue, g_method);
445 }
446 
TEST_F(CompilerQueueTest,AgedMaxLimit)447 TEST_F(CompilerQueueTest, AgedMaxLimit)
448 {
449     auto klass = TestClassPrepare();
450 
451     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
452     ASSERT_NE(main_method, nullptr);
453 
454     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
455     ASSERT_NE(f_method, nullptr);
456 
457     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
458     ASSERT_NE(g_method, nullptr);
459 
460     main_method->SetHotnessCounter(1000);
461     f_method->SetHotnessCounter(2000);
462     g_method->SetHotnessCounter(3000);
463 
464     RuntimeOptions options;
465     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
466                                            options.GetCompilerQueueMaxLength(), options.GetCompilerDeathCounterValue(),
467                                            options.GetCompilerEpochDuration());
468 
469     for (int i = 0; i < 40; i++) {
470         queue.AddTask(CompilerTask {main_method, 0});
471         queue.AddTask(CompilerTask {f_method, 0});
472         queue.AddTask(CompilerTask {g_method, 0});
473     }
474 
475     // 100 as Max_Limit
476     for (int i = 0; i < 100; i++) {
477         queue.GetTask();
478         // Can not check as the task may expire on an overloaded machine
479     }
480 
481     auto method = queue.GetTask().GetMethod();
482     ASSERT_EQ(method, nullptr);
483 
484     // check an option
485     constexpr int CompilerQueueMaxLength = 1;
486     CompilerPriorityAgedCounterQueue queue2(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
487                                             CompilerQueueMaxLength, options.GetCompilerDeathCounterValue(),
488                                             options.GetCompilerEpochDuration());
489 
490     queue2.AddTask(CompilerTask {main_method, 0});
491     queue2.AddTask(CompilerTask {f_method, 0});
492     queue2.AddTask(CompilerTask {g_method, 0});
493 
494     method = queue2.GetTask().GetMethod();
495     method = queue2.GetTask().GetMethod();
496     ASSERT_EQ(method, nullptr);
497 }
498 
TEST_F(CompilerQueueTest,AgedDeathCounter)499 TEST_F(CompilerQueueTest, AgedDeathCounter)
500 {
501     auto klass = TestClassPrepare();
502 
503     Method *main_method = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
504     ASSERT_NE(main_method, nullptr);
505 
506     Method *f_method = klass->GetDirectMethod(utf::CStringAsMutf8("f"));
507     ASSERT_NE(f_method, nullptr);
508 
509     Method *g_method = klass->GetDirectMethod(utf::CStringAsMutf8("g"));
510     ASSERT_NE(g_method, nullptr);
511 
512     main_method->SetHotnessCounter(10);
513     f_method->SetHotnessCounter(20);
514     g_method->SetHotnessCounter(30000);
515 
516     RuntimeOptions options;
517     constexpr int CompilerDeathCounterValue = 50;
518     CompilerPriorityAgedCounterQueue queue(thread_->GetVM()->GetHeapManager()->GetInternalAllocator(),
519                                            options.GetCompilerQueueMaxLength(), CompilerDeathCounterValue,
520                                            options.GetCompilerEpochDuration());
521 
522     queue.AddTask(CompilerTask {main_method, 0});
523     queue.AddTask(CompilerTask {f_method, 0});
524     queue.AddTask(CompilerTask {g_method, 0});
525 
526     auto method = queue.GetTask().GetMethod();
527     ASSERT_EQ(method, g_method);
528     method = queue.GetTask().GetMethod();
529     ASSERT_EQ(method, nullptr);
530 }
531 }  // namespace panda::test
532