1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include "precompiled.hpp"
  25 #include "gc/shared/oopStorage.inline.hpp"
  26 #include "gc/shared/oopStorageParState.inline.hpp"
  27 #include "gc/shared/workgroup.hpp"
  28 #include "logging/log.hpp"
  29 #include "logging/logConfiguration.hpp"
  30 #include "memory/allocation.inline.hpp"
  31 #include "memory/iterator.inline.hpp"
  32 #include "runtime/interfaceSupport.inline.hpp"
  33 #include "runtime/os.hpp"
  34 #include "runtime/thread.hpp"
  35 #include "runtime/vm_operations.hpp"
  36 #include "runtime/vmThread.hpp"
  37 #include "utilities/debug.hpp"
  38 #include "utilities/ostream.hpp"
  39 #include "utilities/ticks.hpp"
  40 
  41 #include "unittest.hpp"
  42 
  43 // This "test" doesn't really verify much.  Rather, it's mostly a
  44 // microbenchmark for OopStorage parallel iteration.  It executes
  45 // parallel iteration with varying numbers of threads on an storage
  46 // object containing a large number of entries, and logs some stats
  47 // about the distribution and performance of the iteration.
  48 
  49 const uint _max_workers = 10;
  50 static uint _num_workers = 0;
  51 const size_t _storage_entries = 1000000;
  52 
  53 class OopStorageParIterPerf : public ::testing::Test {
  54 public:
  55   OopStorageParIterPerf();
  56   ~OopStorageParIterPerf();
  57 
  58   WorkGang* workers() const;
  59 
  60   class VM_ParStateTime;
  61   class Task;
  62   class Closure;
  63 
  64   Tickspan run_task(Task* task, uint nthreads);
  65   void show_task(const Task* task, Tickspan duration, uint nthreads);
  66   void run_test(uint nthreads);
  67 
  68   static WorkGang* _workers;
  69 
  70   static const int _active_rank = Mutex::leaf - 1;
  71   static const int _allocate_rank = Mutex::leaf;
  72 
  73   Mutex _allocate_mutex;
  74   Mutex _active_mutex;
  75   OopStorage _storage;
  76   oop* _entries[_storage_entries];
  77 };
  78 
  79 WorkGang* OopStorageParIterPerf::_workers = NULL;
  80 
  81 WorkGang* OopStorageParIterPerf::workers() const {
  82   if (_workers == NULL) {
  83     WorkGang* wg = new WorkGang("OopStorageParIterPerf workers",
  84                                 _num_workers,
  85                                 false,
  86                                 false);
  87     wg->initialize_workers();
  88     wg->update_active_workers(_num_workers);
  89     _workers = wg;
  90   }
  91   return _workers;
  92 }
  93 
  94 OopStorageParIterPerf::OopStorageParIterPerf() :
  95   _allocate_mutex(_allocate_rank,
  96                   "test_OopStorage_parperf_allocate",
  97                   false,
  98                   Mutex::_safepoint_check_never),
  99   _active_mutex(_active_rank,
 100                 "test_OopStorage_parperf_active",
 101                 false,
 102                 Mutex::_safepoint_check_never),
 103   _storage("Test Storage", &_allocate_mutex, &_active_mutex)
 104 {
 105   for (size_t i = 0; i < _storage_entries; ++i) {
 106     _entries[i] = _storage.allocate();
 107   }
 108   _num_workers = MIN2(_max_workers, (uint)os::processor_count());
 109 }
 110 
 111 OopStorageParIterPerf::~OopStorageParIterPerf() {
 112   _storage.release(_entries, ARRAY_SIZE(_entries));
 113 }
 114 
 115 class OopStorageParIterPerf::VM_ParStateTime : public VM_GTestExecuteAtSafepoint {
 116 public:
 117   VM_ParStateTime(WorkGang* workers, AbstractGangTask* task, uint nthreads) :
 118     _workers(workers), _task(task), _nthreads(nthreads)
 119   {}
 120 
 121   void doit() {
 122     _workers->run_task(_task, _nthreads);
 123   }
 124 
 125 private:
 126   WorkGang* _workers;
 127   AbstractGangTask* _task;
 128   uint _nthreads;
 129 };
 130 
 131 class OopStorageParIterPerf::Task : public AbstractGangTask {
 132   typedef OopStorage::ParState<false, false> StateType;
 133 
 134   Tickspan* _worker_times;
 135   StateType _state;
 136   OopClosure* _closure;
 137 
 138 public:
 139   Task(OopStorage* storage, OopClosure* closure, uint nthreads) :
 140     AbstractGangTask("OopStorageParIterPerf::Task"),
 141     _worker_times(NULL),
 142     _state(storage, nthreads),
 143     _closure(closure)
 144   {
 145     Tickspan* wtimes = NEW_C_HEAP_ARRAY(Tickspan, _num_workers, mtInternal);
 146     for (uint i = 0; i < _num_workers; ++i) {
 147       new (&wtimes[i]) Tickspan();
 148     }
 149     _worker_times = wtimes;
 150   }
 151 
 152   ~Task() {
 153     FREE_C_HEAP_ARRAY(Tickspan, _worker_times);
 154   }
 155 
 156   virtual void work(uint worker_id) {
 157     Ticks start_time = Ticks::now();
 158     _state.oops_do(_closure);
 159     _worker_times[worker_id] = Ticks::now() - start_time;
 160   }
 161 
 162   const Tickspan* worker_times() const { return _worker_times; }
 163 };
 164 
 165 class OopStorageParIterPerf::Closure : public OopClosure {
 166 public:
 167   virtual void do_oop(oop* p) { guarantee(*p == NULL, "expected NULL"); }
 168   virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
 169 };
 170 
 171 Tickspan OopStorageParIterPerf::run_task(Task* task, uint nthreads) {
 172   tty->print_cr("Running test with %u threads", nthreads);
 173   VM_ParStateTime op(workers(), task, nthreads);
 174   ThreadInVMfromNative invm(JavaThread::current());
 175   Ticks start_time = Ticks::now();
 176   VMThread::execute(&op);
 177   return Ticks::now() - start_time;
 178 }
 179 
 180 void OopStorageParIterPerf::show_task(const Task* task, Tickspan duration, uint nthreads) {
 181   tty->print_cr("Run test with %u threads: " JLONG_FORMAT, nthreads, duration.value());
 182   const Tickspan* wtimes = task->worker_times();
 183   for (uint i = 0; i < _num_workers; ++i) {
 184     if (wtimes[i] != Tickspan()) {
 185       tty->print_cr("  %u: " JLONG_FORMAT, i, wtimes[i].value());
 186     }
 187   }
 188   tty->cr();
 189 }
 190 
 191 void OopStorageParIterPerf::run_test(uint nthreads) {
 192   if (nthreads <= _num_workers) {
 193     SCOPED_TRACE(err_msg("Running test with %u threads", nthreads).buffer());
 194     Closure closure;
 195     Task task(&_storage, &closure, nthreads);
 196     Tickspan t = run_task(&task, nthreads);
 197     show_task(&task, t, nthreads);
 198   }
 199 }
 200 
 201 TEST_VM_F(OopStorageParIterPerf, test) {
 202   // Enable additional interesting logging.
 203 #define TEST_TAGS oopstorage, blocks, stats
 204   // There isn't an obvious way to capture the old log level so it
 205   // can be restored here, so just use Warning as the "default".
 206   LogLevelType old_level = LogLevel::Warning;
 207   if (log_is_enabled(Debug, TEST_TAGS)) {
 208     old_level = LogLevel::Debug;
 209   } else if (log_is_enabled(Info, TEST_TAGS)) {
 210     old_level = LogLevel::Info;
 211   }
 212   bool debug_enabled = old_level == LogLevel::Debug;
 213   if (!debug_enabled) {
 214     LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(TEST_TAGS));
 215   }
 216 
 217   run_test(1);
 218   run_test(2);
 219   run_test(3);
 220   run_test(4);
 221   run_test(6);
 222   run_test(8);
 223   run_test(10);
 224 
 225   if (!debug_enabled) {
 226     LogConfiguration::configure_stdout(old_level, true, LOG_TAGS(TEST_TAGS));
 227   }
 228 }