Add option for changing number of GC threads.

The number of threads was previously set to 1 by an accidential
checkin. This hurts on all devices which aren't dual core. We now
properly use sysconf to determine how many threads we should create.

Also added a -XX:HeapGCThreads heap option which lets us change
how many GC threads we create. The default value is equal to the
number of processors on the device minus one.

Change-Id: If65065ef09174a3813b8741efdd5ea7bbe82a4e2
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index dd76f3e..ae843cb 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -66,10 +66,11 @@
 
 Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
            double target_utilization, size_t capacity,
-           const std::string& original_image_file_name, bool concurrent_gc)
+           const std::string& original_image_file_name, bool concurrent_gc, size_t num_gc_threads)
     : alloc_space_(NULL),
       card_table_(NULL),
       concurrent_gc_(concurrent_gc),
+      num_gc_threads_(num_gc_threads),
       have_zygote_space_(false),
       reference_queue_lock_(NULL),
       is_gc_running_(false),
@@ -196,7 +197,7 @@
   gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
                                                 *gc_complete_lock_));
 
-  // Create the reference queue lock, this is required so for parrallel object scanning in the GC.
+  // Create the reference queue lock, this is required so for parallel object scanning in the GC.
   reference_queue_lock_ = new Mutex("reference queue lock");
 
   last_gc_time_ns_ = NanoTime();
@@ -217,10 +218,7 @@
 }
 
 void Heap::CreateThreadPool() {
-  // TODO: Make sysconf(_SC_NPROCESSORS_CONF) be a helper function?
-  // Use the number of processors - 1 since the thread doing the GC does work while its waiting for
-  // workers to complete.
-  thread_pool_.reset(new ThreadPool(1)); // new ThreadPool(sysconf(_SC_NPROCESSORS_CONF) - 1));
+  thread_pool_.reset(new ThreadPool(num_gc_threads_));
 }
 
 void Heap::DeleteThreadPool() {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 33b7601..b64fdc0 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -136,7 +136,8 @@
   // ImageWriter output.
   explicit Heap(size_t initial_size, size_t growth_limit, size_t min_free,
                 size_t max_free, double target_utilization, size_t capacity,
-                const std::string& original_image_file_name, bool concurrent_gc);
+                const std::string& original_image_file_name, bool concurrent_gc,
+                size_t num_gc_threads);
 
   ~Heap();
 
@@ -489,6 +490,9 @@
   // false for stop-the-world mark sweep.
   const bool concurrent_gc_;
 
+  // How many GC threads we may use for garbage collection.
+  const bool num_gc_threads_;
+
   // If we have a zygote space.
   bool have_zygote_space_;
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index ba18311..cf6e537 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -338,6 +338,8 @@
   parsed->heap_max_free_ = gc::Heap::kDefaultMaxFree;
   parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
   parsed->heap_growth_limit_ = 0;  // 0 means no growth limit.
+  // Default to number of processors minus one since the main GC thread also does work.
+  parsed->heap_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
   parsed->stack_size_ = 0; // 0 means default.
 
   parsed->is_compiler_ = false;
@@ -472,6 +474,9 @@
         return NULL;
       }
       parsed->heap_target_utilization_ = value;
+    } else if (StartsWith(option, "-XX:HeapGCThreads=")) {
+      parsed->heap_gc_threads_ =
+          ParseMemoryOption(option.substr(strlen("-XX:HeapGCThreads=")).c_str(), 1024);
     } else if (StartsWith(option, "-Xss")) {
       size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).c_str(), 1);
       if (size == 0) {
@@ -829,7 +834,8 @@
                        options->heap_target_utilization_,
                        options->heap_maximum_size_,
                        options->image_,
-                       options->is_concurrent_gc_enabled_);
+                       options->is_concurrent_gc_enabled_,
+                       options->heap_gc_threads_);
 
   BlockSignals();
   InitPlatformSignalHandlers();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 58f985f..d480e36 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -92,6 +92,7 @@
     size_t heap_initial_size_;
     size_t heap_maximum_size_;
     size_t heap_growth_limit_;
+    size_t heap_gc_threads_;
     size_t heap_min_free_;
     size_t heap_max_free_;
     double heap_target_utilization_;