diff options
-rw-r--r-- | runtime/interpreter/interpreter.cc | 6 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 8 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.h | 11 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_goto_table_impl.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_switch_impl.cc | 2 | ||||
-rw-r--r-- | runtime/interpreter/mterp/mterp.cc | 6 | ||||
-rw-r--r-- | runtime/jit/jit.cc | 42 | ||||
-rw-r--r-- | runtime/jit/jit.h | 15 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.cc | 10 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.h | 2 |
10 files changed, 73 insertions, 31 deletions
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index a43278228c..97dbe5d219 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -292,7 +292,7 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, // Pop the shadow frame before calling into compiled code. self->PopShadowFrame(); - ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); + ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result); // Push the shadow frame back as the caller will expect it. self->PushShadowFrame(&shadow_frame); @@ -535,6 +535,10 @@ JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* cod return JValue(); } + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod()); + } return Execute(self, code_item, *shadow_frame, JValue()); } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 3453abcd64..12d70c5244 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -503,6 +503,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, uint32_t vregC) ALWAYS_INLINE; void ArtInterpreterToCompiledCodeBridge(Thread* self, + ArtMethod* caller, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result) @@ -530,6 +531,10 @@ void ArtInterpreterToCompiledCodeBridge(Thread* self, uint16_t arg_offset = (code_item == nullptr) ? 0 : code_item->registers_size_ - code_item->ins_size_; + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr && caller != nullptr) { + jit->NotifyInterpreterToCompiledCodeTransition(self, caller); + } method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset), (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t), result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty()); @@ -726,7 +731,8 @@ static inline bool DoCallCommon(ArtMethod* called_method, target->GetEntryPointFromQuickCompiledCode())) { ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result); } else { - ArtInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result); + ArtInterpreterToCompiledCodeBridge( + self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result); } } else { UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg); diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index fb9817514c..e5b89e2f98 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -635,7 +635,7 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr jit->InvokeVirtualOrInterface( self, receiver, sf_method, shadow_frame.GetDexPC(), called_method); } - jit->AddSamples(self, sf_method, 1); + jit->AddSamples(self, sf_method, 1, /*with_backedges*/false); } // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. if (type == kVirtual || type == kInterface) { @@ -681,7 +681,7 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, if (jit != nullptr) { jit->InvokeVirtualOrInterface( self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method); - jit->AddSamples(self, shadow_frame.GetMethod(), 1); + jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges*/false); } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. @@ -1001,8 +1001,11 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { return branch_offset <= 0; } -void ArtInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result); +void ArtInterpreterToCompiledCodeBridge(Thread* self, + ArtMethod* caller, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, + JValue* result); // Explicitly instantiate all DoInvoke functions. #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index c95af6f0f1..13cfb9877d 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -78,7 +78,7 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ if (jit != nullptr) { \ - jit->AddSamples(self, method, 1); \ + jit->AddSamples(self, method, 1, /*with_backedges*/ true); \ } \ } while (false) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index ca1d635d51..4323d4f425 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -89,7 +89,7 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ if (jit != nullptr) { \ - jit->AddSamples(self, method, 1); \ + jit->AddSamples(self, method, 1, /*with_backedges*/ true); \ } \ } while (false) diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index f80068354f..4b3c03ebb4 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -689,7 +689,7 @@ extern "C" int16_t MterpAddHotnessBatch(ArtMethod* method, jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown(); - jit->AddSamples(self, method, count); + jit->AddSamples(self, method, count, /*with_backedges*/ true); } return MterpSetUpHotnessCountdown(method, shadow_frame); } @@ -702,7 +702,7 @@ extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int uint32_t dex_pc = shadow_frame->GetDexPC(); jit::Jit* jit = Runtime::Current()->GetJit(); if ((jit != nullptr) && (offset <= 0)) { - jit->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1, /*with_backedges*/ true); } int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame); if (countdown_value == jit::kJitCheckForOSR) { @@ -722,7 +722,7 @@ extern "C" bool MterpMaybeDoOnStackReplacement(Thread* self, jit::Jit* jit = Runtime::Current()->GetJit(); if (offset <= 0) { // Keep updating hotness in case a compilation request was dropped. Eventually it will retry. - jit->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1, /*with_backedges*/ true); } // Assumes caller has already determined that an OSR check is appropriate. return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 558e4435f0..0a6da2cbe3 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -97,8 +97,9 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt LOG(FATAL) << "Priority thread weight cannot be 0."; } } else { - jit_options->priority_thread_weight_ = - std::max(jit_options->compile_threshold_ / 2000, static_cast<size_t>(1)); + jit_options->priority_thread_weight_ = std::max( + jit_options->warmup_threshold_ / Jit::kDefaultPriorityThreadWeightRatio, + static_cast<size_t>(1)); } return jit_options; @@ -154,6 +155,8 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { jit->warm_method_threshold_ = options->GetWarmupThreshold(); jit->osr_method_threshold_ = options->GetOsrThreshold(); jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); + jit->transition_weight_ = std::max( + jit->warm_method_threshold_ / kDefaultTransitionRatio, static_cast<size_t>(1)); jit->CreateThreadPool(); @@ -240,8 +243,17 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) { return false; } + + VLOG(jit) << "Compiling method " + << PrettyMethod(method_to_compile) + << " osr=" << std::boolalpha << osr; bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr); code_cache_->DoneCompiling(method_to_compile, self, osr); + if (!success) { + VLOG(jit) << "Failed to compile method " + << PrettyMethod(method_to_compile) + << " osr=" << std::boolalpha << osr; + } return success; } @@ -520,15 +532,9 @@ class JitCompileTask FINAL : public Task { void Run(Thread* self) OVERRIDE { ScopedObjectAccess soa(self); if (kind_ == kCompile) { - VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) { - VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); - } + Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false); } else if (kind_ == kCompileOsr) { - VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) { - VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_); - } + Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true); } else { DCHECK(kind_ == kAllocateProfile); if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) { @@ -549,7 +555,7 @@ class JitCompileTask FINAL : public Task { DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); }; -void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { +void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) { if (thread_pool_ == nullptr) { // Should only see this when shutting down. DCHECK(Runtime::Current()->IsShuttingDown(self)); @@ -573,7 +579,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; if (starting_count < warm_method_threshold_) { - if (new_count >= warm_method_threshold_) { + if ((new_count >= warm_method_threshold_) && + (method->GetProfilingInfo(sizeof(void*)) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); if (success) { VLOG(jit) << "Start profiling " << PrettyMethod(method); @@ -595,14 +602,19 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { // Avoid jumping more than one state at a time. new_count = std::min(new_count, hot_method_threshold_ - 1); } else if (starting_count < hot_method_threshold_) { - if (new_count >= hot_method_threshold_) { + if ((new_count >= hot_method_threshold_) && + !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); } // Avoid jumping more than one state at a time. new_count = std::min(new_count, osr_method_threshold_ - 1); } else if (starting_count < osr_method_threshold_) { - if (new_count >= osr_method_threshold_) { + if (!with_backedges) { + // If the samples don't contain any back edge, we don't increment the hotness. + return; + } + if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); } @@ -630,7 +642,7 @@ void Jit::MethodEntered(Thread* thread, ArtMethod* method) { !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); } else { - AddSamples(thread, method, 1); + AddSamples(thread, method, 1, /* with_backedges */false); } } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 96f9608a94..ff3acf62c8 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -43,6 +43,8 @@ class Jit { public: static constexpr bool kStressMode = kIsDebugBuild; static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000; + static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000; + static constexpr size_t kDefaultTransitionRatio = 100; virtual ~Jit(); static Jit* Create(JitOptions* options, std::string* error_msg); @@ -92,7 +94,7 @@ class Jit { void MethodEntered(Thread* thread, ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); - void AddSamples(Thread* self, ArtMethod* method, uint16_t samples) + void AddSamples(Thread* self, ArtMethod* method, uint16_t samples, bool with_backedges) SHARED_REQUIRES(Locks::mutator_lock_); void InvokeVirtualOrInterface(Thread* thread, @@ -102,6 +104,16 @@ class Jit { ArtMethod* callee) SHARED_REQUIRES(Locks::mutator_lock_); + void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller) + SHARED_REQUIRES(Locks::mutator_lock_) { + AddSamples(self, caller, transition_weight_, false); + } + + void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee) + SHARED_REQUIRES(Locks::mutator_lock_) { + AddSamples(self, callee, transition_weight_, false); + } + // Starts the profile saver if the config options allow profile recording. // The profile will be stored in the specified `filename` and will contain // information collected from the given `code_paths` (a set of dex locations). @@ -175,6 +187,7 @@ class Jit { uint16_t warm_method_threshold_; uint16_t osr_method_threshold_; uint16_t priority_thread_weight_; + uint16_t transition_weight_; std::unique_ptr<ThreadPool> thread_pool_; DISALLOW_COPY_AND_ASSIGN(Jit); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 820ae6acab..1f3e08b39c 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -366,7 +366,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, } last_update_time_ns_.StoreRelease(NanoTime()); VLOG(jit) - << "JIT added (osr = " << std::boolalpha << osr << std::noboolalpha << ") " + << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") " << PrettyMethod(method) << "@" << method << " ccache_size=" << PrettySize(CodeCacheSizeLocked()) << ": " << " dcache_size=" << PrettySize(DataCacheSizeLocked()) << ": " @@ -905,15 +905,18 @@ uint64_t JitCodeCache::GetLastUpdateTimeNs() const { return last_update_time_ns_.LoadAcquire(); } +bool JitCodeCache::IsOsrCompiled(ArtMethod* method) { + MutexLock mu(Thread::Current(), lock_); + return osr_code_map_.find(method) != osr_code_map_.end(); +} + bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) { if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { - VLOG(jit) << PrettyMethod(method) << " is already compiled"; return false; } MutexLock mu(self, lock_); if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) { - VLOG(jit) << PrettyMethod(method) << " is already osr compiled"; return false; } @@ -928,7 +931,6 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr } if (info->IsMethodBeingCompiled(osr)) { - VLOG(jit) << PrettyMethod(method) << " is already being compiled"; return false; } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 9f18c700d4..f31cc51d51 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -186,6 +186,8 @@ class JitCodeCache { void Dump(std::ostream& os) REQUIRES(!lock_); + bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, |