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