OSDN Git Service

Dex-wide ArenaPool scoping for AOT compilation
authorJean-Philippe Halimi <jean-philippe.halimi@intel.com>
Tue, 2 Feb 2016 18:48:52 +0000 (19:48 +0100)
committerJean-Philippe Halimi <jean-philippe.halimi@intel.com>
Fri, 19 Feb 2016 12:43:24 +0000 (13:43 +0100)
The Arena Pool is a structure that holds memory allocated by the Arena
Allocator, preventing system allocations and deallocations to occur too often
during AOT compilation. Currently, the Arena Pool holds memory it allocates
during the whole AOT compilation process. Unfortunately, such a behavior
generates memory usage overhead, which is exacerbated by bigger applications
such as Facebook.

In this particular app, method size imbalance provokes unnecessary memory
pressure, as one method might require a lot of arena allocations that won't be
used in the remaining compilation. Because the compiler memory footprint keeps
increasing during AOT compilation, the memory pressure becomes very high.

The proposed patch is an attempt to find a tradeoff between allocations /
deallocations time overhead, and the aforementioned memory pressure overhead
resulting of the allocations being held by the Arena Pool. The patch adds a
feature freeing up all memory allocated in the Arena Pool after each dex file
is done compiling.

We have measured no significant AOT compile-time overhead with the patch
(<0.3%), because the selected dex-file granularity is coarse enough. In the
meantime, it provides significant memory footprint improvements. The impact is
especially big with Facebook, because this app has a few methods generating
huge memory footprint peaks (peak native heap footprint goes down 34%).

Change-Id: I27e867e6a20b8a6c28a82cb83140941a8c2b5847
Signed-off-by: Jean-Philippe Halimi <jean-philippe.halimi@intel.com>
compiler/driver/compiler_driver.cc
runtime/base/arena_allocator.cc
runtime/base/arena_allocator.h
runtime/runtime.cc
runtime/runtime.h

index 670fe94..f5d7019 100644 (file)
@@ -2493,6 +2493,7 @@ void CompilerDriver::Compile(jobject class_loader,
                    parallel_thread_pool_.get(),
                    parallel_thread_count_,
                    timings);
+    Runtime::Current()->ReclaimArenaPoolMemory();
   }
   VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
 }
index 771b2d0..a4b38ea 100644 (file)
@@ -222,6 +222,10 @@ ArenaPool::ArenaPool(bool use_malloc, bool low_4gb)
 }
 
 ArenaPool::~ArenaPool() {
+  ReclaimMemory();
+}
+
+void ArenaPool::ReclaimMemory() {
   while (free_arenas_ != nullptr) {
     auto* arena = free_arenas_;
     free_arenas_ = free_arenas_->next_;
@@ -229,6 +233,11 @@ ArenaPool::~ArenaPool() {
   }
 }
 
+void ArenaPool::LockReclaimMemory() {
+  MutexLock lock(Thread::Current(), lock_);
+  ReclaimMemory();
+}
+
 Arena* ArenaPool::AllocArena(size_t size) {
   Thread* self = Thread::Current();
   Arena* ret = nullptr;
index 36334c4..8a96571 100644 (file)
@@ -276,6 +276,8 @@ class ArenaPool {
   Arena* AllocArena(size_t size) REQUIRES(!lock_);
   void FreeArenaChain(Arena* first) REQUIRES(!lock_);
   size_t GetBytesAllocated() const REQUIRES(!lock_);
+  void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS;
+  void LockReclaimMemory() REQUIRES(!lock_);
   // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works
   // use_malloc is false.
   void TrimMaps() REQUIRES(!lock_);
index 2aeb792..6c30eaa 100644 (file)
@@ -1300,6 +1300,10 @@ void Runtime::InitNativeMethods() {
   VLOG(startup) << "Runtime::InitNativeMethods exiting";
 }
 
+void Runtime::ReclaimArenaPoolMemory() {
+  arena_pool_->LockReclaimMemory();
+}
+
 void Runtime::InitThreadGroups(Thread* self) {
   JNIEnvExt* env = self->GetJniEnv();
   ScopedJniEnvLocalRefState env_state(env);
index cbb3e89..8aac4ce 100644 (file)
@@ -564,6 +564,9 @@ class Runtime {
   const ArenaPool* GetArenaPool() const {
     return arena_pool_.get();
   }
+
+  void ReclaimArenaPoolMemory();
+
   LinearAlloc* GetLinearAlloc() {
     return linear_alloc_.get();
   }