diff options
Diffstat (limited to 'runtime/jit')
| -rw-r--r-- | runtime/jit/jit.cc | 10 | ||||
| -rw-r--r-- | runtime/jit/jit.h | 9 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 12 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.h | 3 | ||||
| -rw-r--r-- | runtime/jit/jit_instrumentation.cc | 79 | ||||
| -rw-r--r-- | runtime/jit/jit_instrumentation.h | 53 | ||||
| -rw-r--r-- | runtime/jit/profiling_info.cc | 117 | ||||
| -rw-r--r-- | runtime/jit/profiling_info.h | 106 |
8 files changed, 312 insertions, 77 deletions
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 26a4fe49f1..683b2cfa89 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -39,6 +39,8 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheCapacity); jit_options->compile_threshold_ = options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold); + jit_options->warmup_threshold_ = + options.GetOrDefault(RuntimeArgumentMap::JITWarmupThreshold); jit_options->dump_info_on_shutdown_ = options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown); return jit_options; @@ -160,17 +162,19 @@ Jit::~Jit() { } } -void Jit::CreateInstrumentationCache(size_t compile_threshold) { +void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) { CHECK_GT(compile_threshold, 0U); Runtime* const runtime = Runtime::Current(); runtime->GetThreadList()->SuspendAll(__FUNCTION__); // Add Jit interpreter instrumentation, tells the interpreter when to notify the jit to compile // something. - instrumentation_cache_.reset(new jit::JitInstrumentationCache(compile_threshold)); + instrumentation_cache_.reset( + new jit::JitInstrumentationCache(compile_threshold, warmup_threshold)); runtime->GetInstrumentation()->AddListener( new jit::JitInstrumentationListener(instrumentation_cache_.get()), instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kBackwardBranch); + instrumentation::Instrumentation::kBackwardBranch | + instrumentation::Instrumentation::kInvokeVirtualOrInterface); runtime->GetThreadList()->ResumeAll(); } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index ca6e7ea1f8..643bc23da3 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -43,13 +43,14 @@ class JitOptions; class Jit { public: static constexpr bool kStressMode = kIsDebugBuild; - static constexpr size_t kDefaultCompileThreshold = kStressMode ? 1 : 1000; + static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 1000; + static constexpr size_t kDefaultWarmupThreshold = kDefaultCompileThreshold / 2; virtual ~Jit(); static Jit* Create(JitOptions* options, std::string* error_msg); bool CompileMethod(ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateInstrumentationCache(size_t compile_threshold); + void CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold); void CreateThreadPool(); CompilerCallbacks* GetCompilerCallbacks() { return compiler_callbacks_; @@ -95,6 +96,9 @@ class JitOptions { size_t GetCompileThreshold() const { return compile_threshold_; } + size_t GetWarmupThreshold() const { + return warmup_threshold_; + } size_t GetCodeCacheCapacity() const { return code_cache_capacity_; } @@ -112,6 +116,7 @@ class JitOptions { bool use_jit_; size_t code_cache_capacity_; size_t compile_threshold_; + size_t warmup_threshold_; bool dump_info_on_shutdown_; JitOptions() : use_jit_(false), code_cache_capacity_(0), compile_threshold_(0), diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index cd5f4cb529..4c5316227c 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -82,9 +82,19 @@ uint8_t* JitCodeCache::ReserveCode(Thread* self, size_t size) { return code_cache_ptr_ - size; } +uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { + MutexLock mu(self, lock_); + size = RoundUp(size, sizeof(void*)); + if (size > DataCacheRemain()) { + return nullptr; + } + data_cache_ptr_ += size; + return data_cache_ptr_ - size; +} + uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) { MutexLock mu(self, lock_); - const size_t size = end - begin; + const size_t size = RoundUp(end - begin, sizeof(void*)); if (size > DataCacheRemain()) { return nullptr; // Out of space in the data cache. } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 9707f6f29d..f485e4aded 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -86,6 +86,9 @@ class JitCodeCache { // Reserve a region of code of size at least "size". Returns null if there is no more room. uint8_t* ReserveCode(Thread* self, size_t size) REQUIRES(!lock_); + // Reserve a region of data of size at least "size". Returns null if there is no more room. + uint8_t* ReserveData(Thread* self, size_t size) REQUIRES(!lock_); + // Add a data array of size (end - begin) with the associated contents, returns null if there // is no more room. uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index 258c29dd20..f48568271d 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -26,16 +26,12 @@ namespace jit { class JitCompileTask : public Task { public: - JitCompileTask(ArtMethod* method, JitInstrumentationCache* cache) - : method_(method), cache_(cache) { - } + explicit JitCompileTask(ArtMethod* method) : method_(method) {} virtual void Run(Thread* self) OVERRIDE { ScopedObjectAccess soa(self); VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); - if (Runtime::Current()->GetJit()->CompileMethod(method_, self)) { - cache_->SignalCompiled(self, method_); - } else { + if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) { VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); } } @@ -46,13 +42,14 @@ class JitCompileTask : public Task { private: ArtMethod* const method_; - JitInstrumentationCache* const cache_; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); }; -JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold) - : lock_("jit instrumentation lock"), hot_method_threshold_(hot_method_threshold) { +JitInstrumentationCache::JitInstrumentationCache(size_t hot_method_threshold, + size_t warm_method_threshold) + : hot_method_threshold_(hot_method_threshold), + warm_method_threshold_(warm_method_threshold) { } void JitInstrumentationCache::CreateThreadPool() { @@ -60,20 +57,11 @@ void JitInstrumentationCache::CreateThreadPool() { } void JitInstrumentationCache::DeleteThreadPool() { + DCHECK(Runtime::Current()->IsShuttingDown(Thread::Current())); thread_pool_.reset(); } -void JitInstrumentationCache::SignalCompiled(Thread* self, ArtMethod* method) { - ScopedObjectAccessUnchecked soa(self); - jmethodID method_id = soa.EncodeMethod(method); - MutexLock mu(self, lock_); - auto it = samples_.find(method_id); - if (it != samples_.end()) { - samples_.erase(it); - } -} - -void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t count) { +void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t) { ScopedObjectAccessUnchecked soa(self); // Since we don't have on-stack replacement, some methods can remain in the interpreter longer // than we want resulting in samples even after the method is compiled. @@ -81,35 +69,22 @@ void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) { return; } - jmethodID method_id = soa.EncodeMethod(method); - bool is_hot = false; - { - MutexLock mu(self, lock_); - size_t sample_count = 0; - auto it = samples_.find(method_id); - if (it != samples_.end()) { - it->second += count; - sample_count = it->second; - } else { - sample_count = count; - samples_.insert(std::make_pair(method_id, count)); - } - // If we have enough samples, mark as hot and request Jit compilation. - if (sample_count >= hot_method_threshold_ && sample_count - count < hot_method_threshold_) { - is_hot = true; - } + if (thread_pool_.get() == nullptr) { + DCHECK(Runtime::Current()->IsShuttingDown(self)); + return; } - if (is_hot) { - if (thread_pool_.get() != nullptr) { - thread_pool_->AddTask(self, new JitCompileTask( - method->GetInterfaceMethodIfProxy(sizeof(void*)), this)); - thread_pool_->StartWorkers(self); - } else { - VLOG(jit) << "Compiling hot method " << PrettyMethod(method); - Runtime::Current()->GetJit()->CompileMethod( - method->GetInterfaceMethodIfProxy(sizeof(void*)), self); + uint16_t sample_count = method->IncrementCounter(); + if (sample_count == warm_method_threshold_) { + ProfilingInfo* info = method->CreateProfilingInfo(); + if (info != nullptr) { + VLOG(jit) << "Start profiling " << PrettyMethod(method); } } + if (sample_count == hot_method_threshold_) { + thread_pool_->AddTask(self, new JitCompileTask( + method->GetInterfaceMethodIfProxy(sizeof(void*)))); + thread_pool_->StartWorkers(self); + } } JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache) @@ -117,5 +92,17 @@ JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* CHECK(instrumentation_cache_ != nullptr); } +void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee ATTRIBUTE_UNUSED) { + DCHECK(this_object != nullptr); + ProfilingInfo* info = caller->GetProfilingInfo(); + if (info != nullptr) { + info->AddInvokeInfo(thread, dex_pc, this_object->GetClass()); + } +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h index 0deaf8ad02..6fdef6585d 100644 --- a/runtime/jit/jit_instrumentation.h +++ b/runtime/jit/jit_instrumentation.h @@ -45,18 +45,15 @@ namespace jit { // Keeps track of which methods are hot. class JitInstrumentationCache { public: - explicit JitInstrumentationCache(size_t hot_method_threshold); + JitInstrumentationCache(size_t hot_method_threshold, size_t warm_method_threshold); void AddSamples(Thread* self, ArtMethod* method, size_t samples) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); - void SignalCompiled(Thread* self, ArtMethod* method) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); + SHARED_REQUIRES(Locks::mutator_lock_); void CreateThreadPool(); void DeleteThreadPool(); private: - Mutex lock_; - std::unordered_map<jmethodID, size_t> samples_; size_t hot_method_threshold_; + size_t warm_method_threshold_; std::unique_ptr<ThreadPool> thread_pool_; DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache); @@ -66,37 +63,43 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen public: explicit JitInstrumentationListener(JitInstrumentationCache* cache); - virtual void MethodEntered(Thread* thread, mirror::Object* /*this_object*/, - ArtMethod* method, uint32_t /*dex_pc*/) + void MethodEntered(Thread* thread, mirror::Object* /*this_object*/, + ArtMethod* method, uint32_t /*dex_pc*/) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { instrumentation_cache_->AddSamples(thread, method, 1); } - virtual void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - const JValue& /*return_value*/) + void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/, + ArtMethod* /*method*/, uint32_t /*dex_pc*/, + const JValue& /*return_value*/) OVERRIDE { } - virtual void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { } - virtual void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/) OVERRIDE { } - virtual void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/, const JValue& /*field_value*/) + void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/, + ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { } + void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/, + ArtMethod* /*method*/, uint32_t /*dex_pc*/, + ArtField* /*field*/) OVERRIDE { } + void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/, + ArtMethod* /*method*/, uint32_t /*dex_pc*/, + ArtField* /*field*/, const JValue& /*field_value*/) OVERRIDE { } - virtual void ExceptionCaught(Thread* /*thread*/, - mirror::Throwable* /*exception_object*/) OVERRIDE { } + void ExceptionCaught(Thread* /*thread*/, + mirror::Throwable* /*exception_object*/) OVERRIDE { } - virtual void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { } + void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/, + ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { } - // We only care about how many dex instructions were executed in the Jit. - virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset) + void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { CHECK_LE(dex_pc_offset, 0); instrumentation_cache_->AddSamples(thread, method, 1); } + void InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee) + OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); + private: JitInstrumentationCache* const instrumentation_cache_; diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc new file mode 100644 index 0000000000..0c039f2bbd --- /dev/null +++ b/runtime/jit/profiling_info.cc @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "profiling_info.h" + +#include "art_method-inl.h" +#include "dex_instruction.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "scoped_thread_state_change.h" +#include "thread.h" + +namespace art { + +ProfilingInfo* ProfilingInfo::Create(ArtMethod* method) { + // Walk over the dex instructions of the method and keep track of + // instructions we are interested in profiling. + const uint16_t* code_ptr = nullptr; + const uint16_t* code_end = nullptr; + { + ScopedObjectAccess soa(Thread::Current()); + DCHECK(!method->IsNative()); + const DexFile::CodeItem& code_item = *method->GetCodeItem(); + code_ptr = code_item.insns_; + code_end = code_item.insns_ + code_item.insns_size_in_code_units_; + } + + uint32_t dex_pc = 0; + std::vector<uint32_t> entries; + while (code_ptr < code_end) { + const Instruction& instruction = *Instruction::At(code_ptr); + switch (instruction.Opcode()) { + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + case Instruction::INVOKE_VIRTUAL_QUICK: + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + entries.push_back(dex_pc); + break; + + default: + break; + } + dex_pc += instruction.SizeInCodeUnits(); + code_ptr += instruction.SizeInCodeUnits(); + } + + // If there is no instruction we are interested in, no need to create a `ProfilingInfo` + // object, it will never be filled. + if (entries.empty()) { + return nullptr; + } + + // Allocate the `ProfilingInfo` object int the JIT's data space. + jit::JitCodeCache* code_cache = Runtime::Current()->GetJit()->GetCodeCache(); + size_t profile_info_size = sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size(); + uint8_t* data = code_cache->ReserveData(Thread::Current(), profile_info_size); + + if (data == nullptr) { + VLOG(jit) << "Cannot allocate profiling info anymore"; + return nullptr; + } + + return new (data) ProfilingInfo(entries); +} + +void ProfilingInfo::AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls) { + InlineCache* cache = nullptr; + // TODO: binary search if array is too long. + for (size_t i = 0; i < number_of_inline_caches_; ++i) { + if (cache_[i].dex_pc == dex_pc) { + cache = &cache_[i]; + break; + } + } + DCHECK(cache != nullptr); + + ScopedObjectAccess soa(self); + for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { + mirror::Class* existing = cache->classes_[i].Read<kWithoutReadBarrier>(); + if (existing == cls) { + // Receiver type is already in the cache, nothing else to do. + return; + } else if (existing == nullptr) { + // Cache entry is empty, try to put `cls` in it. + GcRoot<mirror::Class> expected_root(nullptr); + GcRoot<mirror::Class> desired_root(cls); + if (!reinterpret_cast<Atomic<GcRoot<mirror::Class>>*>(&cache->classes_[i])-> + CompareExchangeStrongSequentiallyConsistent(expected_root, desired_root)) { + // Some other thread put a class in the cache, continue iteration starting at this + // entry in case the entry contains `cls`. + --i; + } else { + // We successfully set `cls`, just return. + return; + } + } + } + // Unsuccessfull - cache is full, making it megamorphic. + DCHECK(cache->IsMegamorphic()); +} + +} // namespace art diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h new file mode 100644 index 0000000000..73ca41a9a1 --- /dev/null +++ b/runtime/jit/profiling_info.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_JIT_PROFILING_INFO_H_ +#define ART_RUNTIME_JIT_PROFILING_INFO_H_ + +#include <vector> + +#include "base/macros.h" +#include "gc_root.h" + +namespace art { + +class ArtMethod; + +namespace mirror { +class Class; +} + +/** + * Profiling info for a method, created and filled by the interpreter once the + * method is warm, and used by the compiler to drive optimizations. + */ +class ProfilingInfo { + public: + static ProfilingInfo* Create(ArtMethod* method); + + // Add information from an executed INVOKE instruction to the profile. + void AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls); + + // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires. + template<typename RootVisitorType> + void VisitRoots(RootVisitorType& visitor) NO_THREAD_SAFETY_ANALYSIS { + for (size_t i = 0; i < number_of_inline_caches_; ++i) { + InlineCache* cache = &cache_[i]; + for (size_t j = 0; j < InlineCache::kIndividualCacheSize; ++j) { + visitor.VisitRootIfNonNull(cache->classes_[j].AddressWithoutBarrier()); + } + } + } + + private: + // Structure to store the classes seen at runtime for a specific instruction. + // Once the classes_ array is full, we consider the INVOKE to be megamorphic. + struct InlineCache { + bool IsMonomorphic() const { + DCHECK_GE(kIndividualCacheSize, 2); + return !classes_[0].IsNull() && classes_[1].IsNull(); + } + + bool IsMegamorphic() const { + for (size_t i = 0; i < kIndividualCacheSize; ++i) { + if (classes_[i].IsNull()) { + return false; + } + } + return true; + } + + bool IsUnitialized() const { + return classes_[0].IsNull(); + } + + bool IsPolymorphic() const { + DCHECK_GE(kIndividualCacheSize, 3); + return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull(); + } + + static constexpr uint16_t kIndividualCacheSize = 5; + uint32_t dex_pc; + GcRoot<mirror::Class> classes_[kIndividualCacheSize]; + }; + + explicit ProfilingInfo(const std::vector<uint32_t>& entries) + : number_of_inline_caches_(entries.size()) { + memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); + for (size_t i = 0; i < number_of_inline_caches_; ++i) { + cache_[i].dex_pc = entries[i]; + } + } + + // Number of instructions we are profiling in the ArtMethod. + const uint32_t number_of_inline_caches_; + + // Dynamically allocated array of size `number_of_inline_caches_`. + InlineCache cache_[0]; + + DISALLOW_COPY_AND_ASSIGN(ProfilingInfo); +}; + +} // namespace art + +#endif // ART_RUNTIME_JIT_PROFILING_INFO_H_ |