duke@435: /* jcoomes@1993: * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. duke@435: * duke@435: */ duke@435: duke@435: # include "incls/_precompiled.incl" duke@435: # include "incls/_taskqueue.cpp.incl" duke@435: jmasa@981: #ifdef TRACESPINNING jmasa@981: uint ParallelTaskTerminator::_total_yields = 0; jmasa@981: uint ParallelTaskTerminator::_total_spins = 0; jmasa@981: uint ParallelTaskTerminator::_total_peeks = 0; jmasa@981: #endif jmasa@981: jcoomes@2020: #if TASKQUEUE_STATS jcoomes@2020: const char * const TaskQueueStats::_names[last_stat_id] = { jcoomes@2020: "qpush", "qpop", "qpop-s", "qattempt", "qsteal", "opush", "omax" jcoomes@2020: }; jcoomes@2020: jcoomes@2064: TaskQueueStats & TaskQueueStats::operator +=(const TaskQueueStats & addend) jcoomes@2064: { jcoomes@2064: for (unsigned int i = 0; i < last_stat_id; ++i) { jcoomes@2064: _stats[i] += addend._stats[i]; jcoomes@2064: } jcoomes@2064: return *this; jcoomes@2064: } jcoomes@2064: jcoomes@2020: void TaskQueueStats::print_header(unsigned int line, outputStream* const stream, jcoomes@2020: unsigned int width) jcoomes@2020: { jcoomes@2020: // Use a width w: 1 <= w <= max_width jcoomes@2020: const unsigned int max_width = 40; jcoomes@2020: const unsigned int w = MAX2(MIN2(width, max_width), 1U); jcoomes@2020: jcoomes@2020: if (line == 0) { // spaces equal in width to the header jcoomes@2020: const unsigned int hdr_width = w * last_stat_id + last_stat_id - 1; jcoomes@2020: stream->print("%*s", hdr_width, " "); jcoomes@2020: } else if (line == 1) { // labels jcoomes@2020: stream->print("%*s", w, _names[0]); jcoomes@2020: for (unsigned int i = 1; i < last_stat_id; ++i) { jcoomes@2020: stream->print(" %*s", w, _names[i]); jcoomes@2020: } jcoomes@2020: } else if (line == 2) { // dashed lines jcoomes@2020: char dashes[max_width + 1]; jcoomes@2020: memset(dashes, '-', w); jcoomes@2020: dashes[w] = '\0'; jcoomes@2020: stream->print("%s", dashes); jcoomes@2020: for (unsigned int i = 1; i < last_stat_id; ++i) { jcoomes@2020: stream->print(" %s", dashes); jcoomes@2020: } jcoomes@2020: } jcoomes@2020: } jcoomes@2020: jcoomes@2020: void TaskQueueStats::print(outputStream* stream, unsigned int width) const jcoomes@2020: { jcoomes@2020: #define FMT SIZE_FORMAT_W(*) jcoomes@2020: stream->print(FMT, width, _stats[0]); jcoomes@2020: for (unsigned int i = 1; i < last_stat_id; ++i) { jcoomes@2020: stream->print(" " FMT, width, _stats[i]); jcoomes@2020: } jcoomes@2020: #undef FMT jcoomes@2020: } jcoomes@2064: jcoomes@2064: #ifdef ASSERT jcoomes@2064: // Invariants which should hold after a TaskQueue has been emptied and is jcoomes@2064: // quiescent; they do not hold at arbitrary times. jcoomes@2064: void TaskQueueStats::verify() const jcoomes@2064: { jcoomes@2064: assert(get(push) == get(pop) + get(steal), jcoomes@2064: err_msg("push=" SIZE_FORMAT " pop=" SIZE_FORMAT " steal=" SIZE_FORMAT, jcoomes@2064: get(push), get(pop), get(steal))); jcoomes@2064: assert(get(pop_slow) <= get(pop), jcoomes@2064: err_msg("pop_slow=" SIZE_FORMAT " pop=" SIZE_FORMAT, jcoomes@2064: get(pop_slow), get(pop))); jcoomes@2064: assert(get(steal) <= get(steal_attempt), jcoomes@2064: err_msg("steal=" SIZE_FORMAT " steal_attempt=" SIZE_FORMAT, jcoomes@2064: get(steal), get(steal_attempt))); jcoomes@2064: assert(get(overflow) == 0 || get(push) != 0, jcoomes@2064: err_msg("overflow=" SIZE_FORMAT " push=" SIZE_FORMAT, jcoomes@2064: get(overflow), get(push))); jcoomes@2064: assert(get(overflow_max_len) == 0 || get(overflow) != 0, jcoomes@2064: err_msg("overflow_max_len=" SIZE_FORMAT " overflow=" SIZE_FORMAT, jcoomes@2064: get(overflow_max_len), get(overflow))); jcoomes@2064: } jcoomes@2064: #endif // ASSERT jcoomes@2020: #endif // TASKQUEUE_STATS jcoomes@2020: duke@435: int TaskQueueSetSuper::randomParkAndMiller(int *seed0) { duke@435: const int a = 16807; duke@435: const int m = 2147483647; duke@435: const int q = 127773; /* m div a */ duke@435: const int r = 2836; /* m mod a */ duke@435: assert(sizeof(int) == 4, "I think this relies on that"); duke@435: int seed = *seed0; duke@435: int hi = seed / q; duke@435: int lo = seed % q; duke@435: int test = a * lo - r * hi; duke@435: if (test > 0) duke@435: seed = test; duke@435: else duke@435: seed = test + m; duke@435: *seed0 = seed; duke@435: return seed; duke@435: } duke@435: duke@435: ParallelTaskTerminator:: duke@435: ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set) : duke@435: _n_threads(n_threads), duke@435: _queue_set(queue_set), duke@435: _offered_termination(0) {} duke@435: duke@435: bool ParallelTaskTerminator::peek_in_queue_set() { duke@435: return _queue_set->peek(); duke@435: } duke@435: duke@435: void ParallelTaskTerminator::yield() { ysr@1280: assert(_offered_termination <= _n_threads, "Invariant"); duke@435: os::yield(); duke@435: } duke@435: duke@435: void ParallelTaskTerminator::sleep(uint millis) { ysr@1280: assert(_offered_termination <= _n_threads, "Invariant"); duke@435: os::sleep(Thread::current(), millis, false); duke@435: } duke@435: ysr@777: bool ysr@777: ParallelTaskTerminator::offer_termination(TerminatorTerminator* terminator) { ysr@1280: assert(_offered_termination < _n_threads, "Invariant"); duke@435: Atomic::inc(&_offered_termination); duke@435: ysr@976: uint yield_count = 0; jmasa@981: // Number of hard spin loops done since last yield jmasa@981: uint hard_spin_count = 0; jmasa@981: // Number of iterations in the hard spin loop. jmasa@981: uint hard_spin_limit = WorkStealingHardSpins; jmasa@981: jmasa@981: // If WorkStealingSpinToYieldRatio is 0, no hard spinning is done. jmasa@981: // If it is greater than 0, then start with a small number jmasa@981: // of spins and increase number with each turn at spinning until jmasa@981: // the count of hard spins exceeds WorkStealingSpinToYieldRatio. jmasa@981: // Then do a yield() call and start spinning afresh. jmasa@981: if (WorkStealingSpinToYieldRatio > 0) { jmasa@981: hard_spin_limit = WorkStealingHardSpins >> WorkStealingSpinToYieldRatio; jmasa@981: hard_spin_limit = MAX2(hard_spin_limit, 1U); jmasa@981: } jmasa@981: // Remember the initial spin limit. jmasa@981: uint hard_spin_start = hard_spin_limit; jmasa@981: jmasa@981: // Loop waiting for all threads to offer termination or jmasa@981: // more work. duke@435: while (true) { ysr@1280: assert(_offered_termination <= _n_threads, "Invariant"); jmasa@981: // Are all threads offering termination? duke@435: if (_offered_termination == _n_threads) { duke@435: return true; duke@435: } else { jmasa@981: // Look for more work. jmasa@981: // Periodically sleep() instead of yield() to give threads jmasa@981: // waiting on the cores the chance to grab this code duke@435: if (yield_count <= WorkStealingYieldsBeforeSleep) { jmasa@981: // Do a yield or hardspin. For purposes of deciding whether jmasa@981: // to sleep, count this as a yield. duke@435: yield_count++; jmasa@981: jmasa@981: // Periodically call yield() instead spinning jmasa@981: // After WorkStealingSpinToYieldRatio spins, do a yield() call jmasa@981: // and reset the counts and starting limit. jmasa@981: if (hard_spin_count > WorkStealingSpinToYieldRatio) { jmasa@981: yield(); jmasa@981: hard_spin_count = 0; jmasa@981: hard_spin_limit = hard_spin_start; jmasa@981: #ifdef TRACESPINNING jmasa@981: _total_yields++; jmasa@981: #endif jmasa@981: } else { jmasa@981: // Hard spin this time jmasa@981: // Increase the hard spinning period but only up to a limit. jmasa@981: hard_spin_limit = MIN2(2*hard_spin_limit, jmasa@981: (uint) WorkStealingHardSpins); jmasa@981: for (uint j = 0; j < hard_spin_limit; j++) { jmasa@981: SpinPause(); jmasa@981: } jmasa@981: hard_spin_count++; jmasa@981: #ifdef TRACESPINNING jmasa@981: _total_spins++; jmasa@981: #endif jmasa@981: } duke@435: } else { duke@435: if (PrintGCDetails && Verbose) { duke@435: gclog_or_tty->print_cr("ParallelTaskTerminator::offer_termination() " duke@435: "thread %d sleeps after %d yields", duke@435: Thread::current(), yield_count); duke@435: } duke@435: yield_count = 0; duke@435: // A sleep will cause this processor to seek work on another processor's duke@435: // runqueue, if it has nothing else to run (as opposed to the yield duke@435: // which may only move the thread to the end of the this processor's duke@435: // runqueue). duke@435: sleep(WorkStealingSleepMillis); duke@435: } duke@435: jmasa@981: #ifdef TRACESPINNING jmasa@981: _total_peeks++; jmasa@981: #endif ysr@777: if (peek_in_queue_set() || ysr@777: (terminator != NULL && terminator->should_exit_termination())) { duke@435: Atomic::dec(&_offered_termination); ysr@1280: assert(_offered_termination < _n_threads, "Invariant"); duke@435: return false; duke@435: } duke@435: } duke@435: } duke@435: } duke@435: jmasa@981: #ifdef TRACESPINNING jmasa@981: void ParallelTaskTerminator::print_termination_counts() { jmasa@981: gclog_or_tty->print_cr("ParallelTaskTerminator Total yields: %lld " jmasa@981: "Total spins: %lld Total peeks: %lld", jmasa@981: total_yields(), jmasa@981: total_spins(), jmasa@981: total_peeks()); jmasa@981: } jmasa@981: #endif jmasa@981: duke@435: void ParallelTaskTerminator::reset_for_reuse() { duke@435: if (_offered_termination != 0) { duke@435: assert(_offered_termination == _n_threads, duke@435: "Terminator may still be in use"); duke@435: _offered_termination = 0; duke@435: } duke@435: } duke@435: jcoomes@1746: #ifdef ASSERT jcoomes@1746: bool ObjArrayTask::is_valid() const { jcoomes@1746: return _obj != NULL && _obj->is_objArray() && _index > 0 && jcoomes@1746: _index < objArrayOop(_obj)->length(); jcoomes@1746: } jcoomes@1746: #endif // ASSERT