Add weight to compiled/interpreter transitions.
Also:
- Cleanup logging.
- Check ArtMethod status before adding compilation requests.
- Don't request osr compilation if we know AddSamples does not come
from a back edge.
Bug: 27865109
Change-Id: I84512f7d957b61ce2458360ed430adb151830278
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a432782..97dbe5d 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -292,7 +292,7 @@
// 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 @@
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 3453abc..12d70c5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -503,6 +503,7 @@
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 @@
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 @@
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 fb98175..e5b89e2 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -635,7 +635,7 @@
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 @@
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 @@
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 c95af6f..13cfb98 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -78,7 +78,7 @@
#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 ca1d635..4323d4f 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -89,7 +89,7 @@
#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 f800683..4b3c03e 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -689,7 +689,7 @@
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 @@
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 @@
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 558e443..0a6da2c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -97,8 +97,9 @@
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->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 @@
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 @@
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 @@
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 @@
}
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 @@
// 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 @@
!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 96f9608..ff3acf6 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,6 +43,8 @@
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 @@
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 @@
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 @@
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 820ae6a..1f3e08b 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -366,7 +366,7 @@
}
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 @@
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 @@
}
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 9f18c70..f31cc51 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -186,6 +186,8 @@
void Dump(std::ostream& os) REQUIRES(!lock_);
+ bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_);
+
private:
// Take ownership of maps.
JitCodeCache(MemMap* code_map,