Replace the nterp threshold with the warmup threshold.
Replace a hardcoded value with one that can be changed at the command
line.
Test: test.py
Change-Id: I638da5b5cc2e56aa0857d2bf0862a2f8c2020949
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 10c651b..38c37ec 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -479,7 +479,7 @@
MemoryKiB(16 * MB), "-Xjitmaxsize:16M", M::JITCodeCacheMaxCapacity);
}
{
- EXPECT_SINGLE_PARSE_VALUE(12345u, "-Xjitthreshold:12345", M::JITCompileThreshold);
+ EXPECT_SINGLE_PARSE_VALUE(12345u, "-Xjitthreshold:12345", M::JITOptimizeThreshold);
}
} // TEST_F
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 69eaef9..29886ab 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -33,6 +33,7 @@
#include "gc_root-inl.h"
#include "imtable-inl.h"
#include "intrinsics_enum.h"
+#include "jit/jit.h"
#include "jit/profiling_info.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -423,16 +424,20 @@
return CodeItemDebugInfoAccessor(*GetDexFile(), GetCodeItem(), GetDexMethodIndex());
}
-inline bool ArtMethod::CounterHasChanged() {
+inline bool ArtMethod::CounterHasChanged(uint16_t threshold) {
DCHECK(!IsAbstract());
- return hotness_count_ != interpreter::kNterpHotnessMask;
+ DCHECK_EQ(threshold, Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
+ return hotness_count_ != threshold;
}
-inline void ArtMethod::ResetCounter() {
- DCHECK(!IsAbstract());
+inline void ArtMethod::ResetCounter(uint16_t new_value) {
+ if (IsAbstract()) {
+ return;
+ }
+ DCHECK_EQ(new_value, Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
// Avoid dirtying the value if possible.
- if (hotness_count_ != interpreter::kNterpHotnessMask) {
- hotness_count_ = interpreter::kNterpHotnessMask;
+ if (hotness_count_ != new_value) {
+ hotness_count_ = new_value;
}
}
@@ -461,10 +466,11 @@
return hotness_count_ == 0;
}
-inline bool ArtMethod::CounterHasReached(uint16_t samples) {
+inline bool ArtMethod::CounterHasReached(uint16_t samples, uint16_t threshold) {
DCHECK(!IsAbstract());
- DCHECK_LE(samples, interpreter::kNterpHotnessMask);
- return hotness_count_ <= (interpreter::kNterpHotnessMask - samples);
+ DCHECK_EQ(threshold, Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
+ DCHECK_LE(samples, threshold);
+ return hotness_count_ <= (threshold - samples);
}
inline uint16_t ArtMethod::GetCounter() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 2f8085c..9dda50c 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -752,9 +752,7 @@
SetDataPtrSize(nullptr, image_pointer_size);
}
// Clear hotness to let the JIT properly decide when to compile this method.
- if (!IsAbstract()) {
- ResetCounter();
- }
+ ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold());
}
bool ArtMethod::IsImagePointerSize(PointerSize pointer_size) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index a4bb78e..b501484 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -76,7 +76,7 @@
static constexpr uint32_t kRuntimeMethodDexMethodIndex = 0xFFFFFFFF;
ArtMethod() : access_flags_(0), dex_method_index_(0),
- method_index_(0), hotness_count_(interpreter::kNterpHotnessMask) { }
+ method_index_(0), hotness_count_(0) { }
ArtMethod(ArtMethod* src, PointerSize image_pointer_size) {
CopyFrom(src, image_pointer_size);
@@ -684,13 +684,13 @@
void CopyFrom(ArtMethod* src, PointerSize image_pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE void ResetCounter();
+ ALWAYS_INLINE void ResetCounter(uint16_t new_value);
ALWAYS_INLINE void UpdateCounter(int32_t new_samples);
ALWAYS_INLINE void SetHotCounter();
ALWAYS_INLINE bool CounterIsHot();
- ALWAYS_INLINE bool CounterHasReached(uint16_t samples);
+ ALWAYS_INLINE bool CounterHasReached(uint16_t samples, uint16_t threshold);
ALWAYS_INLINE uint16_t GetCounter();
- ALWAYS_INLINE bool CounterHasChanged();
+ ALWAYS_INLINE bool CounterHasChanged(uint16_t threshold);
ALWAYS_INLINE static constexpr uint16_t MaxCounter() {
return std::numeric_limits<decltype(hotness_count_)>::max();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5de7adf..45562c4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1949,6 +1949,7 @@
if (!runtime->IsAotCompiler()) {
ScopedTrace trace("AppImage:UpdateCodeItemAndNterp");
bool can_use_nterp = interpreter::CanRuntimeUseNterp();
+ uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) {
// In the image, the `data` pointer field of the ArtMethod contains the code
// item offset. Change this to the actual pointer to the code item.
@@ -1958,7 +1959,7 @@
method.SetCodeItem(code_item);
// The hotness counter may have changed since we compiled the image, so
// reset it with the runtime value.
- method.ResetCounter();
+ method.ResetCounter(hotness_threshold);
}
// Set image methods' entry point that point to the interpreter bridge to the
// nterp entry point.
@@ -3687,6 +3688,7 @@
uint32_t last_dex_method_index = dex::kDexNoIndex;
size_t last_class_def_method_index = 0;
+ uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
// Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
// methods needs to decode all of the fields.
accessor.VisitFieldsAndMethods([&](
@@ -3720,11 +3722,13 @@
last_dex_method_index = it_method_index;
last_class_def_method_index = class_def_method_index;
}
+ art_method->ResetCounter(hotness_threshold);
++class_def_method_index;
}, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* art_method = klass->GetVirtualMethodUnchecked(
class_def_method_index - accessor.NumDirectMethods(),
image_pointer_size_);
+ art_method->ResetCounter(hotness_threshold);
LoadMethod(dex_file, method, klass, art_method);
LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
++class_def_method_index;
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index e339dcf..4fe4e15 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -81,7 +81,6 @@
LOG(FATAL) << "ERROR: unexpected asm interp size " << interp_size
<< "(did an instruction handler exceed " << width << " bytes?)";
}
- static_assert(IsPowerOfTwo(kNterpHotnessMask + 1), "Hotness mask must be a (power of 2) - 1");
static_assert(IsPowerOfTwo(kTieredHotnessMask + 1),
"Tiered hotness mask must be a (power of 2) - 1");
}
@@ -727,8 +726,9 @@
extern "C" jit::OsrData* NterpHotMethod(ArtMethod* method, uint16_t* dex_pc_ptr, uint32_t* vregs)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension sants("In nterp");
- method->ResetCounter();
- jit::Jit* jit = Runtime::Current()->GetJit();
+ Runtime* runtime = Runtime::Current();
+ method->ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold());
+ jit::Jit* jit = runtime->GetJit();
if (jit != nullptr && jit->UseJitCompilation()) {
// Nterp passes null on entry where we don't want to OSR.
if (dex_pc_ptr != nullptr) {
diff --git a/runtime/interpreter/mterp/nterp.h b/runtime/interpreter/mterp/nterp.h
index 94e5b8a..b7cbbf7 100644
--- a/runtime/interpreter/mterp/nterp.h
+++ b/runtime/interpreter/mterp/nterp.h
@@ -33,8 +33,6 @@
bool CanRuntimeUseNterp();
const void* GetNterpEntryPoint();
-// The hotness threshold where we trigger JIT compilation or OSR.
-constexpr uint16_t kNterpHotnessMask = 0xffff;
constexpr uint16_t kNterpHotnessValue = 0;
// The hotness threshold for the baseline compiler to trigger optimized
diff --git a/runtime/jit/jit-inl.h b/runtime/jit/jit-inl.h
index 683fc38..32465f2 100644
--- a/runtime/jit/jit-inl.h
+++ b/runtime/jit/jit-inl.h
@@ -32,7 +32,7 @@
return;
}
if (method->CounterIsHot()) {
- method->ResetCounter();
+ method->ResetCounter(Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
EnqueueCompilation(method, self);
} else {
method->UpdateCounter(1);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index f48ed7d..d070339 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -64,21 +64,19 @@
// Maximum permitted threshold value.
static constexpr uint32_t kJitMaxThreshold = std::numeric_limits<uint16_t>::max();
-// Different compilation threshold constants. These can be overridden on the command line.
+static constexpr uint32_t kJitDefaultOptimizeThreshold = 0xffff;
+// Different optimization threshold constants. These default to the equivalent optimization
+// thresholds divided by 2, but can be overridden at the command-line.
+static constexpr uint32_t kJitStressDefaultOptimizeThreshold = kJitDefaultOptimizeThreshold / 2;
+static constexpr uint32_t kJitSlowStressDefaultOptimizeThreshold =
+ kJitStressDefaultOptimizeThreshold / 2;
-// Non-debug default
-static constexpr uint32_t kJitDefaultCompileThreshold = 20 * kJitSamplesBatchSize;
-// Fast-debug build.
-static constexpr uint32_t kJitStressDefaultCompileThreshold = 2 * kJitSamplesBatchSize;
-// Slow-debug build.
-static constexpr uint32_t kJitSlowStressDefaultCompileThreshold = 2;
-
-// Different warm-up threshold constants. These default to the equivalent compile thresholds divided
+static constexpr uint32_t kJitDefaultWarmupThreshold = 0xffff;
+// Different warm-up threshold constants. These default to the equivalent warmup thresholds divided
// by 2, but can be overridden at the command-line.
-static constexpr uint32_t kJitDefaultWarmUpThreshold = kJitDefaultCompileThreshold / 2;
-static constexpr uint32_t kJitStressDefaultWarmUpThreshold = kJitStressDefaultCompileThreshold / 2;
-static constexpr uint32_t kJitSlowStressDefaultWarmUpThreshold =
- kJitSlowStressDefaultCompileThreshold / 2;
+static constexpr uint32_t kJitStressDefaultWarmupThreshold = kJitDefaultWarmupThreshold / 2;
+static constexpr uint32_t kJitSlowStressDefaultWarmupThreshold =
+ kJitStressDefaultWarmupThreshold / 2;
DEFINE_RUNTIME_DEBUG_FLAG(Jit, kSlowMode);
@@ -106,66 +104,37 @@
jit_options->zygote_thread_pool_pthread_priority_ =
options.GetOrDefault(RuntimeArgumentMap::JITZygotePoolThreadPthreadPriority);
- // Set default compile threshold to aid with checking defaults.
- jit_options->compile_threshold_ =
+ // Set default optimize threshold to aid with checking defaults.
+ jit_options->optimize_threshold_ =
kIsDebugBuild
? (Jit::kSlowMode
- ? kJitSlowStressDefaultCompileThreshold
- : kJitStressDefaultCompileThreshold)
- : kJitDefaultCompileThreshold;
-
- // When not running in slow-mode, thresholds are quantized to kJitSamplesbatchsize.
- const uint32_t kJitThresholdStep = Jit::kSlowMode ? 1u : kJitSamplesBatchSize;
+ ? kJitSlowStressDefaultOptimizeThreshold
+ : kJitStressDefaultOptimizeThreshold)
+ : kJitDefaultOptimizeThreshold;
// Set default warm-up threshold to aid with checking defaults.
jit_options->warmup_threshold_ =
kIsDebugBuild ? (Jit::kSlowMode
- ? kJitSlowStressDefaultWarmUpThreshold
- : kJitStressDefaultWarmUpThreshold)
- : kJitDefaultWarmUpThreshold;
+ ? kJitSlowStressDefaultWarmupThreshold
+ : kJitStressDefaultWarmupThreshold)
+ : kJitDefaultWarmupThreshold;
- // Warmup threshold should be less than compile threshold (so long as compile threshold is not
- // zero == JIT-on-first-use).
- DCHECK_LT(jit_options->warmup_threshold_, jit_options->compile_threshold_);
- DCHECK_EQ(RoundUp(jit_options->warmup_threshold_, kJitThresholdStep),
- jit_options->warmup_threshold_);
-
- if (options.Exists(RuntimeArgumentMap::JITCompileThreshold)) {
- jit_options->compile_threshold_ = *options.Get(RuntimeArgumentMap::JITCompileThreshold);
+ if (options.Exists(RuntimeArgumentMap::JITOptimizeThreshold)) {
+ jit_options->optimize_threshold_ = *options.Get(RuntimeArgumentMap::JITOptimizeThreshold);
}
- jit_options->compile_threshold_ = RoundUp(jit_options->compile_threshold_, kJitThresholdStep);
+ DCHECK_LE(jit_options->optimize_threshold_, kJitMaxThreshold);
if (options.Exists(RuntimeArgumentMap::JITWarmupThreshold)) {
jit_options->warmup_threshold_ = *options.Get(RuntimeArgumentMap::JITWarmupThreshold);
}
- jit_options->warmup_threshold_ = RoundUp(jit_options->warmup_threshold_, kJitThresholdStep);
+ DCHECK_LE(jit_options->warmup_threshold_, kJitMaxThreshold);
if (options.Exists(RuntimeArgumentMap::JITOsrThreshold)) {
jit_options->osr_threshold_ = *options.Get(RuntimeArgumentMap::JITOsrThreshold);
} else {
- jit_options->osr_threshold_ = jit_options->compile_threshold_ * 2;
- if (jit_options->osr_threshold_ > kJitMaxThreshold) {
- jit_options->osr_threshold_ =
- RoundDown(kJitMaxThreshold, kJitThresholdStep);
- }
+ jit_options->osr_threshold_ = jit_options->warmup_threshold_;
}
- jit_options->osr_threshold_ = RoundUp(jit_options->osr_threshold_, kJitThresholdStep);
-
- // Enforce ordering constraints between thresholds if not jit-on-first-use (when the compile
- // threshold is 0).
- if (jit_options->compile_threshold_ != 0) {
- // Clamp thresholds such that OSR > compile > warm-up (see Jit::MaybeCompileMethod).
- jit_options->osr_threshold_ = std::clamp(jit_options->osr_threshold_,
- 2u * kJitThresholdStep,
- RoundDown(kJitMaxThreshold, kJitThresholdStep));
- jit_options->compile_threshold_ = std::clamp(jit_options->compile_threshold_,
- kJitThresholdStep,
- jit_options->osr_threshold_ - kJitThresholdStep);
- jit_options->warmup_threshold_ =
- std::clamp(jit_options->warmup_threshold_,
- 0u,
- jit_options->compile_threshold_ - kJitThresholdStep);
- }
+ DCHECK_LE(jit_options->osr_threshold_, kJitMaxThreshold);
if (options.Exists(RuntimeArgumentMap::JITPriorityThreadWeight)) {
jit_options->priority_thread_weight_ =
@@ -253,7 +222,8 @@
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
<< ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
- << ", compile_threshold=" << options->GetCompileThreshold()
+ << ", warmup_threshold=" << options->GetWarmupThreshold()
+ << ", optimize_threshold=" << options->GetOptimizeThreshold()
<< ", profile_saver_options=" << options->GetProfileSaverOptions();
// We want to know whether the compiler is compiling baseline, as this
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e06a6e0..e6c93aa 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -65,16 +65,13 @@
// 19 is the lowest background priority on device.
// See android/os/Process.java.
static constexpr int kJitZygotePoolThreadPthreadDefaultPriority = 19;
-// We check whether to jit-compile the method every Nth invoke.
-// The tests often use threshold of 1000 (and thus 500 to start profiling).
-static constexpr uint32_t kJitSamplesBatchSize = 512; // Must be power of 2.
class JitOptions {
public:
static JitOptions* CreateFromRuntimeArguments(const RuntimeArgumentMap& options);
- uint16_t GetCompileThreshold() const {
- return compile_threshold_;
+ uint16_t GetOptimizeThreshold() const {
+ return optimize_threshold_;
}
uint16_t GetWarmupThreshold() const {
@@ -143,7 +140,7 @@
void SetJitAtFirstUse() {
use_jit_compilation_ = true;
- compile_threshold_ = 0;
+ optimize_threshold_ = 0;
}
void SetUseBaselineCompiler() {
@@ -164,7 +161,7 @@
bool use_baseline_compiler_;
size_t code_cache_initial_capacity_;
size_t code_cache_max_capacity_;
- uint32_t compile_threshold_;
+ uint32_t optimize_threshold_;
uint32_t warmup_threshold_;
uint32_t osr_threshold_;
uint16_t priority_thread_weight_;
@@ -180,7 +177,7 @@
use_baseline_compiler_(false),
code_cache_initial_capacity_(0),
code_cache_max_capacity_(0),
- compile_threshold_(0),
+ optimize_threshold_(0),
warmup_threshold_(0),
osr_threshold_(0),
priority_thread_weight_(0),
@@ -286,7 +283,7 @@
}
uint16_t HotMethodThreshold() const {
- return options_->GetCompileThreshold();
+ return options_->GetOptimizeThreshold();
}
uint16_t WarmMethodThreshold() const {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index e05ca11..3fcb10a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -615,7 +615,7 @@
if (was_warm) {
method->SetPreviouslyWarm();
}
- method->ResetCounter();
+ method->ResetCounter(Runtime::Current()->GetJITOptions()->GetWarmupThreshold());
// We add one sample so that the profile knows that the method was executed at least once.
// This is required for layout purposes.
method->UpdateCounter(/* new_samples= */ 1);
@@ -1289,6 +1289,7 @@
// hotness count is zero.
// Note that these methods may be in thread stack or concurrently revived
// between. That's OK, as the thread executing it will mark it.
+ uint16_t warmup_threshold = Runtime::Current()->GetJITOptions()->GetWarmupThreshold();
for (auto it : profiling_infos_) {
ProfilingInfo* info = it.second;
if (info->GetBaselineHotnessCount() == 0) {
@@ -1297,7 +1298,7 @@
OatQuickMethodHeader* method_header =
OatQuickMethodHeader::FromEntryPoint(entry_point);
if (CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
- info->GetMethod()->ResetCounter();
+ info->GetMethod()->ResetCounter(warmup_threshold);
info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
}
}
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index fce779f..a5cb81b 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -634,13 +634,15 @@
size_t number_of_hot_methods = 0u;
size_t number_of_sampled_methods = 0u;
+ uint16_t initial_value = Runtime::Current()->GetJITOptions()->GetWarmupThreshold();
auto get_method_flags = [&](ArtMethod& method) {
// Mark methods as hot if they have more than hot_method_sample_threshold
// samples. This means they will get compiled by the compiler driver.
- if (method.PreviouslyWarm() || method.CounterHasReached(hot_method_sample_threshold)) {
+ if (method.PreviouslyWarm() ||
+ method.CounterHasReached(hot_method_sample_threshold, initial_value)) {
++number_of_hot_methods;
return enum_cast<ProfileCompilationInfo::MethodHotness::Flag>(base_flags | Hotness::kFlagHot);
- } else if (method.CounterHasChanged()) {
+ } else if (method.CounterHasChanged(initial_value)) {
++number_of_sampled_methods;
return enum_cast<ProfileCompilationInfo::MethodHotness::Flag>(base_flags);
} else {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 2e67b1a..c7996b5 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -477,9 +477,10 @@
klass->IsErroneousResolved()) {
return true;
}
+ uint16_t threshold = Runtime::Current()->GetJITOptions()->GetWarmupThreshold();
for (ArtMethod& m : klass->GetMethods(kRuntimePointerSize)) {
if (!m.IsAbstract()) {
- m.ResetCounter();
+ m.ResetCounter(threshold);
}
}
return true;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index f973ebd..a999951 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -176,7 +176,7 @@
.IntoKey(M::ProfileClock)
.Define("-Xjitthreshold:_")
.WithType<unsigned int>()
- .IntoKey(M::JITCompileThreshold)
+ .IntoKey(M::JITOptimizeThreshold)
.SetCategory("ART")
.Define("-Ximage:_")
.WithType<ParseStringList<':'>>()
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 209c3d8..b386d87 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -90,7 +90,7 @@
RUNTIME_OPTIONS_KEY (unsigned int, MadviseWillNeedArtFileSize, 0)
RUNTIME_OPTIONS_KEY (JniIdType, OpaqueJniIds, JniIdType::kDefault) // -Xopaque-jni-ids:{true, false, swapable}
RUNTIME_OPTIONS_KEY (bool, AutoPromoteOpaqueJniIds, true) // testing use only. -Xauto-promote-opaque-jni-ids:{true, false}
-RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold)
+RUNTIME_OPTIONS_KEY (unsigned int, JITOptimizeThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight)