• 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 #ifndef PANDA_RUNTIME_COMPILER_QUEUE_COUNTER_PRIORITY_H_
16 #define PANDA_RUNTIME_COMPILER_QUEUE_COUNTER_PRIORITY_H_
17 
18 #include <algorithm>
19 #include <cstring>
20 
21 #include "libpandabase/utils/time.h"
22 #include "runtime/compiler_queue_interface.h"
23 #include "runtime/include/mem/panda_containers.h"
24 #include "runtime/include/method-inl.h"
25 
26 namespace panda {
27 
28 class CompilationQueueElement {
29 public:
CompilationQueueElement(CompilerTask task)30     explicit CompilationQueueElement(CompilerTask task) : context_(task)
31     {
32         timestamp_ = time::GetCurrentTimeInMillis();
33         counter_ = task.GetMethod()->GetHotnessCounter();
34     }
35 
GetCounter()36     uint64_t GetCounter() const
37     {
38         // Do not return method->counter as it can changed during sorting
39         return counter_;
40     }
41 
GetContext()42     const CompilerTask &GetContext() const
43     {
44         return context_;
45     }
46 
GetContext()47     CompilerTask &GetContext()
48     {
49         return context_;
50     }
51 
GetTimestamp()52     uint64_t GetTimestamp() const
53     {
54         return timestamp_;
55     }
56 
UpdateCounter(uint64_t new_counter)57     void UpdateCounter(uint64_t new_counter)
58     {
59         counter_ = new_counter;
60     }
61 
62 private:
63     uint64_t timestamp_;
64     uint64_t counter_;
65     CompilerTask context_;
66 };
67 
68 /** The counter priority queue sorts all tasks (methods) by its hotness counters.
69  *  It extracts the most hot method for compilation (the greater counter).
70  *  It also has a limit of tasks for compilation. If the task is too old, it is expired and removed from the queue.
71  *  Note, in case of skip the method due to length limit, its hotness counter is reset.
72  *  Max length and life time is configured.
73  *  This queue is thread unsafe (should be used under lock).
74  */
75 class CompilerPriorityCounterQueue : public CompilerQueueInterface {
76 public:
CompilerPriorityCounterQueue(mem::InternalAllocatorPtr allocator,uint64_t max_length,uint64_t task_life_span)77     explicit CompilerPriorityCounterQueue(mem::InternalAllocatorPtr allocator, uint64_t max_length,
78                                           uint64_t task_life_span)
79         : allocator_(allocator), queue_(allocator->Adapter())
80     {
81         max_length_ = max_length;
82         task_life_span_ = task_life_span;
83         queue_name_ = "";
84         SetQueueName("priority counter compilation queue");
85     }
86 
GetTask()87     CompilerTask GetTask() override
88     {
89         UpdateQueue();
90         if (queue_.empty()) {
91             LOG(DEBUG, COMPILATION_QUEUE) << "Empty " << queue_name_ << ", return nothing";
92             return CompilerTask();
93         }
94         sort(queue_.begin(), queue_.end(), comparator_);
95         auto element = queue_.back();
96         auto task = element->GetContext();
97         queue_.pop_back();
98         allocator_->Delete(element);
99         LOG(DEBUG, COMPILATION_QUEUE) << "Extract a task from a " << queue_name_ << ": " << GetTaskDescription(task);
100         return task;
101     }
102 
103     // NOLINTNEXTLINE(google-default-arguments)
104     void AddTask(CompilerTask ctx, [[maybe_unused]] size_t priority = 0) override
105     {
106         UpdateQueue();
107         if (queue_.size() >= max_length_) {
108             // Not sure if it is possible to exceed the size more than one
109             // Reset the counter of the rejected method;
110             ctx.GetMethod()->ResetHotnessCounter();
111             ctx.GetMethod()->AtomicSetCompilationStatus(Method::WAITING,
112                                                         ctx.IsOsr() ? Method::COMPILED : Method::NOT_COMPILED);
113             // Maybe, replace the other method in queue?
114             LOG(DEBUG, COMPILATION_QUEUE) << "Skip adding the task " << GetTaskDescription(ctx)
115                                           << " due to limit of tasks (" << max_length_ << ") in a " << queue_name_;
116             return;
117         }
118         auto element = allocator_->New<CompilationQueueElement>(ctx);
119         // Sorting will be in Get function
120         queue_.push_back(element);
121         LOG(DEBUG, COMPILATION_QUEUE) << "Add an element to a " << queue_name_ << ": " << GetTaskDescription(ctx);
122     }
123 
Finalize()124     void Finalize() override
125     {
126         for (auto e : queue_) {
127             allocator_->Delete(e);
128         }
129         queue_.clear();
130         LOG(DEBUG, COMPILATION_QUEUE) << "Clear a " << queue_name_;
131     }
132 
133 protected:
GetQueueSize()134     size_t GetQueueSize() override
135     {
136         return queue_.size();
137     }
138 
UpdateCounterAndCheck(CompilationQueueElement * element)139     virtual bool UpdateCounterAndCheck(CompilationQueueElement *element)
140     {
141         // The only way to update counter
142         element->UpdateCounter(element->GetContext().GetMethod()->GetHotnessCounter());
143         uint64_t cur_stamp = time::GetCurrentTimeInMillis();
144         return (cur_stamp - element->GetTimestamp() >= task_life_span_);
145     }
146 
SetQueueName(char const * name)147     void SetQueueName(char const *name)
148     {
149         queue_name_ = name;
150     }
151 
152 private:
153     class Comparator {
154     public:
operator()155         bool operator()(CompilationQueueElement *a, CompilationQueueElement *b) const
156         {
157             if (a->GetCounter() == b->GetCounter()) {
158                 // Use method name just in case?
159                 if (a->GetTimestamp() == b->GetTimestamp()) {
160                     // The only way is a name
161                     // Again, as we pull from the end return reversed compare
162                     return strcmp(reinterpret_cast<const char *>(a->GetContext().GetMethod()->GetName().data),
163                                   reinterpret_cast<const char *>(b->GetContext().GetMethod()->GetName().data)) > 0;
164                 }
165                 // Low time is high priority (pull from the end)
166                 return a->GetTimestamp() > b->GetTimestamp();
167             }
168             // First, handle a method with higher hotness counter
169             return (a->GetCounter() < b->GetCounter());
170         }
171     };
172 
UpdateQueue()173     void UpdateQueue()
174     {
175         // Remove expired tasks
176         for (auto it = queue_.begin(); it != queue_.end();) {
177             auto element = *it;
178             // We should update the counter inside as the queue choose the semantic of the counter by itself
179             if (UpdateCounterAndCheck(element)) {
180                 LOG(DEBUG, COMPILATION_QUEUE) << "Remove an expired element from a " << queue_name_ << ": "
181                                               << GetTaskDescription(element->GetContext());
182                 auto ctx = element->GetContext();
183                 ctx.GetMethod()->AtomicSetCompilationStatus(Method::WAITING,
184                                                             ctx.IsOsr() ? Method::COMPILED : Method::NOT_COMPILED);
185                 allocator_->Delete(element);
186                 it = queue_.erase(it);
187             } else {
188                 ++it;
189             }
190         }
191     }
192     mem::InternalAllocatorPtr allocator_;
193     PandaVector<CompilationQueueElement *> queue_;
194     Comparator comparator_;
195     uint64_t max_length_;
196     // In milliseconds
197     uint64_t task_life_span_;
198     const char *queue_name_;
199 };
200 
201 }  // namespace panda
202 
203 #endif  // PANDA_RUNTIME_COMPILER_QUEUE_COUNTER_PRIORITY_H_
204