src/share/vm/utilities/taskqueue.cpp

Wed, 03 Jul 2019 20:42:37 +0800

author
aoqi
date
Wed, 03 Jul 2019 20:42:37 +0800
changeset 9637
eef07cd490d4
parent 6876
710a3c8b516e
permissions
-rw-r--r--

Merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation.
aoqi@0 8 *
aoqi@0 9 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 12 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 13 * accompanied this code).
aoqi@0 14 *
aoqi@0 15 * You should have received a copy of the GNU General Public License version
aoqi@0 16 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 18 *
aoqi@0 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 20 * or visit www.oracle.com if you need additional information or have any
aoqi@0 21 * questions.
aoqi@0 22 *
aoqi@0 23 */
aoqi@0 24
aoqi@0 25 #include "precompiled.hpp"
aoqi@0 26 #include "oops/oop.inline.hpp"
aoqi@0 27 #include "runtime/os.hpp"
aoqi@0 28 #include "runtime/thread.inline.hpp"
aoqi@0 29 #include "utilities/debug.hpp"
aoqi@0 30 #include "utilities/stack.inline.hpp"
aoqi@0 31 #include "utilities/taskqueue.hpp"
aoqi@0 32
aoqi@0 33 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
aoqi@0 34
aoqi@0 35 #ifdef TRACESPINNING
aoqi@0 36 uint ParallelTaskTerminator::_total_yields = 0;
aoqi@0 37 uint ParallelTaskTerminator::_total_spins = 0;
aoqi@0 38 uint ParallelTaskTerminator::_total_peeks = 0;
aoqi@0 39 #endif
aoqi@0 40
aoqi@0 41 #if TASKQUEUE_STATS
aoqi@0 42 const char * const TaskQueueStats::_names[last_stat_id] = {
aoqi@0 43 "qpush", "qpop", "qpop-s", "qattempt", "qsteal", "opush", "omax"
aoqi@0 44 };
aoqi@0 45
aoqi@0 46 TaskQueueStats & TaskQueueStats::operator +=(const TaskQueueStats & addend)
aoqi@0 47 {
aoqi@0 48 for (unsigned int i = 0; i < last_stat_id; ++i) {
aoqi@0 49 _stats[i] += addend._stats[i];
aoqi@0 50 }
aoqi@0 51 return *this;
aoqi@0 52 }
aoqi@0 53
aoqi@0 54 void TaskQueueStats::print_header(unsigned int line, outputStream* const stream,
aoqi@0 55 unsigned int width)
aoqi@0 56 {
aoqi@0 57 // Use a width w: 1 <= w <= max_width
aoqi@0 58 const unsigned int max_width = 40;
aoqi@0 59 const unsigned int w = MAX2(MIN2(width, max_width), 1U);
aoqi@0 60
aoqi@0 61 if (line == 0) { // spaces equal in width to the header
aoqi@0 62 const unsigned int hdr_width = w * last_stat_id + last_stat_id - 1;
aoqi@0 63 stream->print("%*s", hdr_width, " ");
aoqi@0 64 } else if (line == 1) { // labels
aoqi@0 65 stream->print("%*s", w, _names[0]);
aoqi@0 66 for (unsigned int i = 1; i < last_stat_id; ++i) {
aoqi@0 67 stream->print(" %*s", w, _names[i]);
aoqi@0 68 }
aoqi@0 69 } else if (line == 2) { // dashed lines
aoqi@0 70 char dashes[max_width + 1];
aoqi@0 71 memset(dashes, '-', w);
aoqi@0 72 dashes[w] = '\0';
aoqi@0 73 stream->print("%s", dashes);
aoqi@0 74 for (unsigned int i = 1; i < last_stat_id; ++i) {
aoqi@0 75 stream->print(" %s", dashes);
aoqi@0 76 }
aoqi@0 77 }
aoqi@0 78 }
aoqi@0 79
aoqi@0 80 void TaskQueueStats::print(outputStream* stream, unsigned int width) const
aoqi@0 81 {
aoqi@0 82 #define FMT SIZE_FORMAT_W(*)
aoqi@0 83 stream->print(FMT, width, _stats[0]);
aoqi@0 84 for (unsigned int i = 1; i < last_stat_id; ++i) {
aoqi@0 85 stream->print(" " FMT, width, _stats[i]);
aoqi@0 86 }
aoqi@0 87 #undef FMT
aoqi@0 88 }
aoqi@0 89
aoqi@0 90 #ifdef ASSERT
aoqi@0 91 // Invariants which should hold after a TaskQueue has been emptied and is
aoqi@0 92 // quiescent; they do not hold at arbitrary times.
aoqi@0 93 void TaskQueueStats::verify() const
aoqi@0 94 {
aoqi@0 95 assert(get(push) == get(pop) + get(steal),
aoqi@0 96 err_msg("push=" SIZE_FORMAT " pop=" SIZE_FORMAT " steal=" SIZE_FORMAT,
aoqi@0 97 get(push), get(pop), get(steal)));
aoqi@0 98 assert(get(pop_slow) <= get(pop),
aoqi@0 99 err_msg("pop_slow=" SIZE_FORMAT " pop=" SIZE_FORMAT,
aoqi@0 100 get(pop_slow), get(pop)));
aoqi@0 101 assert(get(steal) <= get(steal_attempt),
aoqi@0 102 err_msg("steal=" SIZE_FORMAT " steal_attempt=" SIZE_FORMAT,
aoqi@0 103 get(steal), get(steal_attempt)));
aoqi@0 104 assert(get(overflow) == 0 || get(push) != 0,
aoqi@0 105 err_msg("overflow=" SIZE_FORMAT " push=" SIZE_FORMAT,
aoqi@0 106 get(overflow), get(push)));
aoqi@0 107 assert(get(overflow_max_len) == 0 || get(overflow) != 0,
aoqi@0 108 err_msg("overflow_max_len=" SIZE_FORMAT " overflow=" SIZE_FORMAT,
aoqi@0 109 get(overflow_max_len), get(overflow)));
aoqi@0 110 }
aoqi@0 111 #endif // ASSERT
aoqi@0 112 #endif // TASKQUEUE_STATS
aoqi@0 113
aoqi@0 114 int TaskQueueSetSuper::randomParkAndMiller(int *seed0) {
aoqi@0 115 const int a = 16807;
aoqi@0 116 const int m = 2147483647;
aoqi@0 117 const int q = 127773; /* m div a */
aoqi@0 118 const int r = 2836; /* m mod a */
aoqi@0 119 assert(sizeof(int) == 4, "I think this relies on that");
aoqi@0 120 int seed = *seed0;
aoqi@0 121 int hi = seed / q;
aoqi@0 122 int lo = seed % q;
aoqi@0 123 int test = a * lo - r * hi;
aoqi@0 124 if (test > 0)
aoqi@0 125 seed = test;
aoqi@0 126 else
aoqi@0 127 seed = test + m;
aoqi@0 128 *seed0 = seed;
aoqi@0 129 return seed;
aoqi@0 130 }
aoqi@0 131
aoqi@0 132 ParallelTaskTerminator::
aoqi@0 133 ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set) :
aoqi@0 134 _n_threads(n_threads),
aoqi@0 135 _queue_set(queue_set),
aoqi@0 136 _offered_termination(0) {}
aoqi@0 137
aoqi@0 138 bool ParallelTaskTerminator::peek_in_queue_set() {
aoqi@0 139 return _queue_set->peek();
aoqi@0 140 }
aoqi@0 141
aoqi@0 142 void ParallelTaskTerminator::yield() {
aoqi@0 143 assert(_offered_termination <= _n_threads, "Invariant");
aoqi@0 144 os::yield();
aoqi@0 145 }
aoqi@0 146
aoqi@0 147 void ParallelTaskTerminator::sleep(uint millis) {
aoqi@0 148 assert(_offered_termination <= _n_threads, "Invariant");
aoqi@0 149 os::sleep(Thread::current(), millis, false);
aoqi@0 150 }
aoqi@0 151
aoqi@0 152 bool
aoqi@0 153 ParallelTaskTerminator::offer_termination(TerminatorTerminator* terminator) {
aoqi@0 154 assert(_n_threads > 0, "Initialization is incorrect");
aoqi@0 155 assert(_offered_termination < _n_threads, "Invariant");
aoqi@0 156 Atomic::inc(&_offered_termination);
aoqi@0 157
aoqi@0 158 uint yield_count = 0;
aoqi@0 159 // Number of hard spin loops done since last yield
aoqi@0 160 uint hard_spin_count = 0;
aoqi@0 161 // Number of iterations in the hard spin loop.
aoqi@0 162 uint hard_spin_limit = WorkStealingHardSpins;
aoqi@0 163
aoqi@0 164 // If WorkStealingSpinToYieldRatio is 0, no hard spinning is done.
aoqi@0 165 // If it is greater than 0, then start with a small number
aoqi@0 166 // of spins and increase number with each turn at spinning until
aoqi@0 167 // the count of hard spins exceeds WorkStealingSpinToYieldRatio.
aoqi@0 168 // Then do a yield() call and start spinning afresh.
aoqi@0 169 if (WorkStealingSpinToYieldRatio > 0) {
aoqi@0 170 hard_spin_limit = WorkStealingHardSpins >> WorkStealingSpinToYieldRatio;
aoqi@0 171 hard_spin_limit = MAX2(hard_spin_limit, 1U);
aoqi@0 172 }
aoqi@0 173 // Remember the initial spin limit.
aoqi@0 174 uint hard_spin_start = hard_spin_limit;
aoqi@0 175
aoqi@0 176 // Loop waiting for all threads to offer termination or
aoqi@0 177 // more work.
aoqi@0 178 while (true) {
aoqi@0 179 assert(_offered_termination <= _n_threads, "Invariant");
aoqi@0 180 // Are all threads offering termination?
aoqi@0 181 if (_offered_termination == _n_threads) {
aoqi@0 182 return true;
aoqi@0 183 } else {
aoqi@0 184 // Look for more work.
aoqi@0 185 // Periodically sleep() instead of yield() to give threads
aoqi@0 186 // waiting on the cores the chance to grab this code
aoqi@0 187 if (yield_count <= WorkStealingYieldsBeforeSleep) {
aoqi@0 188 // Do a yield or hardspin. For purposes of deciding whether
aoqi@0 189 // to sleep, count this as a yield.
aoqi@0 190 yield_count++;
aoqi@0 191
aoqi@0 192 // Periodically call yield() instead spinning
aoqi@0 193 // After WorkStealingSpinToYieldRatio spins, do a yield() call
aoqi@0 194 // and reset the counts and starting limit.
aoqi@0 195 if (hard_spin_count > WorkStealingSpinToYieldRatio) {
aoqi@0 196 yield();
aoqi@0 197 hard_spin_count = 0;
aoqi@0 198 hard_spin_limit = hard_spin_start;
aoqi@0 199 #ifdef TRACESPINNING
aoqi@0 200 _total_yields++;
aoqi@0 201 #endif
aoqi@0 202 } else {
aoqi@0 203 // Hard spin this time
aoqi@0 204 // Increase the hard spinning period but only up to a limit.
aoqi@0 205 hard_spin_limit = MIN2(2*hard_spin_limit,
aoqi@0 206 (uint) WorkStealingHardSpins);
aoqi@0 207 for (uint j = 0; j < hard_spin_limit; j++) {
aoqi@0 208 SpinPause();
aoqi@0 209 }
aoqi@0 210 hard_spin_count++;
aoqi@0 211 #ifdef TRACESPINNING
aoqi@0 212 _total_spins++;
aoqi@0 213 #endif
aoqi@0 214 }
aoqi@0 215 } else {
aoqi@0 216 if (PrintGCDetails && Verbose) {
aoqi@0 217 gclog_or_tty->print_cr("ParallelTaskTerminator::offer_termination() "
aoqi@0 218 "thread %d sleeps after %d yields",
aoqi@0 219 Thread::current(), yield_count);
aoqi@0 220 }
aoqi@0 221 yield_count = 0;
aoqi@0 222 // A sleep will cause this processor to seek work on another processor's
aoqi@0 223 // runqueue, if it has nothing else to run (as opposed to the yield
aoqi@0 224 // which may only move the thread to the end of the this processor's
aoqi@0 225 // runqueue).
aoqi@0 226 sleep(WorkStealingSleepMillis);
aoqi@0 227 }
aoqi@0 228
aoqi@0 229 #ifdef TRACESPINNING
aoqi@0 230 _total_peeks++;
aoqi@0 231 #endif
aoqi@0 232 if (peek_in_queue_set() ||
aoqi@0 233 (terminator != NULL && terminator->should_exit_termination())) {
aoqi@0 234 Atomic::dec(&_offered_termination);
aoqi@0 235 assert(_offered_termination < _n_threads, "Invariant");
aoqi@0 236 return false;
aoqi@0 237 }
aoqi@0 238 }
aoqi@0 239 }
aoqi@0 240 }
aoqi@0 241
aoqi@0 242 #ifdef TRACESPINNING
aoqi@0 243 void ParallelTaskTerminator::print_termination_counts() {
aoqi@0 244 gclog_or_tty->print_cr("ParallelTaskTerminator Total yields: " UINT32_FORMAT
aoqi@0 245 " Total spins: " UINT32_FORMAT " Total peeks: " UINT32_FORMAT,
aoqi@0 246 total_yields(),
aoqi@0 247 total_spins(),
aoqi@0 248 total_peeks());
aoqi@0 249 }
aoqi@0 250 #endif
aoqi@0 251
aoqi@0 252 void ParallelTaskTerminator::reset_for_reuse() {
aoqi@0 253 if (_offered_termination != 0) {
aoqi@0 254 assert(_offered_termination == _n_threads,
aoqi@0 255 "Terminator may still be in use");
aoqi@0 256 _offered_termination = 0;
aoqi@0 257 }
aoqi@0 258 }
aoqi@0 259
aoqi@0 260 #ifdef ASSERT
aoqi@0 261 bool ObjArrayTask::is_valid() const {
aoqi@0 262 return _obj != NULL && _obj->is_objArray() && _index > 0 &&
aoqi@0 263 _index < objArrayOop(_obj)->length();
aoqi@0 264 }
aoqi@0 265 #endif // ASSERT
aoqi@0 266
aoqi@0 267 void ParallelTaskTerminator::reset_for_reuse(int n_threads) {
aoqi@0 268 reset_for_reuse();
aoqi@0 269 _n_threads = n_threads;
aoqi@0 270 }

mercurial