src/share/vm/gc_implementation/g1/g1MMUTracker.cpp

Mon, 03 Aug 2009 12:59:30 -0700

author
johnc
date
Mon, 03 Aug 2009 12:59:30 -0700
changeset 1324
15c5903cf9e1
parent 777
37f87013dfd8
child 1371
e1fdf4fd34dc
permissions
-rw-r--r--

6865703: G1: Parallelize hot card cache cleanup
Summary: Have the GC worker threads clear the hot card cache in parallel by having each worker thread claim a chunk of the card cache and process the cards in that chunk. The size of the chunks that each thread will claim is determined at VM initialization from the size of the card cache and the number of worker threads.
Reviewed-by: jmasa, tonyp

ysr@777 1 /*
ysr@777 2 * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
ysr@777 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ysr@777 4 *
ysr@777 5 * This code is free software; you can redistribute it and/or modify it
ysr@777 6 * under the terms of the GNU General Public License version 2 only, as
ysr@777 7 * published by the Free Software Foundation.
ysr@777 8 *
ysr@777 9 * This code is distributed in the hope that it will be useful, but WITHOUT
ysr@777 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ysr@777 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ysr@777 12 * version 2 for more details (a copy is included in the LICENSE file that
ysr@777 13 * accompanied this code).
ysr@777 14 *
ysr@777 15 * You should have received a copy of the GNU General Public License version
ysr@777 16 * 2 along with this work; if not, write to the Free Software Foundation,
ysr@777 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ysr@777 18 *
ysr@777 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
ysr@777 20 * CA 95054 USA or visit www.sun.com if you need additional information or
ysr@777 21 * have any questions.
ysr@777 22 *
ysr@777 23 */
ysr@777 24
ysr@777 25 #include "incls/_precompiled.incl"
ysr@777 26 #include "incls/_g1MMUTracker.cpp.incl"
ysr@777 27
ysr@777 28 #define _DISABLE_MMU 0
ysr@777 29
ysr@777 30 // can't rely on comparing doubles with tolerating a small margin for error
ysr@777 31 #define SMALL_MARGIN 0.0000001
ysr@777 32 #define is_double_leq_0(_value) ( (_value) < SMALL_MARGIN )
ysr@777 33 #define is_double_leq(_val1, _val2) is_double_leq_0((_val1) - (_val2))
ysr@777 34 #define is_double_geq(_val1, _val2) is_double_leq_0((_val2) - (_val1))
ysr@777 35
ysr@777 36 /***** ALL TIMES ARE IN SECS!!!!!!! *****/
ysr@777 37
ysr@777 38 G1MMUTracker::G1MMUTracker(double time_slice, double max_gc_time) :
ysr@777 39 _time_slice(time_slice),
ysr@777 40 _max_gc_time(max_gc_time),
ysr@777 41 _conc_overhead_time_sec(0.0) { }
ysr@777 42
ysr@777 43 void
ysr@777 44 G1MMUTracker::update_conc_overhead(double conc_overhead) {
ysr@777 45 double conc_overhead_time_sec = _time_slice * conc_overhead;
ysr@777 46 if (conc_overhead_time_sec > 0.9 * _max_gc_time) {
ysr@777 47 // We are screwed, as we only seem to have <10% of the soft
ysr@777 48 // real-time goal available for pauses. Let's admit defeat and
ysr@777 49 // allow something more generous as a pause target.
ysr@777 50 conc_overhead_time_sec = 0.75 * _max_gc_time;
ysr@777 51 }
ysr@777 52
ysr@777 53 _conc_overhead_time_sec = conc_overhead_time_sec;
ysr@777 54 }
ysr@777 55
ysr@777 56 G1MMUTrackerQueue::G1MMUTrackerQueue(double time_slice, double max_gc_time) :
ysr@777 57 G1MMUTracker(time_slice, max_gc_time),
ysr@777 58 _head_index(0),
ysr@777 59 _tail_index(trim_index(_head_index+1)),
ysr@777 60 _no_entries(0) { }
ysr@777 61
ysr@777 62 void G1MMUTrackerQueue::remove_expired_entries(double current_time) {
ysr@777 63 double limit = current_time - _time_slice;
ysr@777 64 while (_no_entries > 0) {
ysr@777 65 if (is_double_geq(limit, _array[_tail_index].end_time())) {
ysr@777 66 _tail_index = trim_index(_tail_index + 1);
ysr@777 67 --_no_entries;
ysr@777 68 } else
ysr@777 69 return;
ysr@777 70 }
ysr@777 71 guarantee(_no_entries == 0, "should have no entries in the array");
ysr@777 72 }
ysr@777 73
ysr@777 74 double G1MMUTrackerQueue::calculate_gc_time(double current_time) {
ysr@777 75 double gc_time = 0.0;
ysr@777 76 double limit = current_time - _time_slice;
ysr@777 77 for (int i = 0; i < _no_entries; ++i) {
ysr@777 78 int index = trim_index(_tail_index + i);
ysr@777 79 G1MMUTrackerQueueElem *elem = &_array[index];
ysr@777 80 if (elem->end_time() > limit) {
ysr@777 81 if (elem->start_time() > limit)
ysr@777 82 gc_time += elem->duration();
ysr@777 83 else
ysr@777 84 gc_time += elem->end_time() - limit;
ysr@777 85 }
ysr@777 86 }
ysr@777 87 return gc_time;
ysr@777 88 }
ysr@777 89
ysr@777 90 void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) {
ysr@777 91 double longest_allowed = longest_pause_internal(start);
ysr@777 92 if (longest_allowed < 0.0)
ysr@777 93 longest_allowed = 0.0;
ysr@777 94 double duration = end - start;
ysr@777 95
ysr@777 96 remove_expired_entries(end);
ysr@777 97 if (_no_entries == QueueLength) {
ysr@777 98 // OK, right now when we fill up we bomb out
ysr@777 99 // there are a few ways of dealing with this "gracefully"
ysr@777 100 // increase the array size (:-)
ysr@777 101 // remove the oldest entry (this might allow more GC time for
ysr@777 102 // the time slice than what's allowed)
ysr@777 103 // concolidate the two entries with the minimum gap between them
ysr@777 104 // (this mighte allow less GC time than what's allowed)
ysr@777 105 guarantee(0, "array full, currently we can't recover");
ysr@777 106 }
ysr@777 107 _head_index = trim_index(_head_index + 1);
ysr@777 108 ++_no_entries;
ysr@777 109 _array[_head_index] = G1MMUTrackerQueueElem(start, end);
ysr@777 110 }
ysr@777 111
ysr@777 112 // basically the _internal call does not remove expired entries
ysr@777 113 // this is for trying things out in the future and a couple
ysr@777 114 // of other places (debugging)
ysr@777 115
ysr@777 116 double G1MMUTrackerQueue::longest_pause(double current_time) {
ysr@777 117 if (_DISABLE_MMU)
ysr@777 118 return _max_gc_time;
ysr@777 119
ysr@777 120 MutexLockerEx x(MMUTracker_lock, Mutex::_no_safepoint_check_flag);
ysr@777 121 remove_expired_entries(current_time);
ysr@777 122
ysr@777 123 return longest_pause_internal(current_time);
ysr@777 124 }
ysr@777 125
ysr@777 126 double G1MMUTrackerQueue::longest_pause_internal(double current_time) {
ysr@777 127 double target_time = _max_gc_time;
ysr@777 128
ysr@777 129 while( 1 ) {
ysr@777 130 double gc_time =
ysr@777 131 calculate_gc_time(current_time + target_time) + _conc_overhead_time_sec;
ysr@777 132 double diff = target_time + gc_time - _max_gc_time;
ysr@777 133 if (!is_double_leq_0(diff)) {
ysr@777 134 target_time -= diff;
ysr@777 135 if (is_double_leq_0(target_time)) {
ysr@777 136 target_time = -1.0;
ysr@777 137 break;
ysr@777 138 }
ysr@777 139 } else {
ysr@777 140 break;
ysr@777 141 }
ysr@777 142 }
ysr@777 143
ysr@777 144 return target_time;
ysr@777 145 }
ysr@777 146
ysr@777 147 // basically the _internal call does not remove expired entries
ysr@777 148 // this is for trying things out in the future and a couple
ysr@777 149 // of other places (debugging)
ysr@777 150
ysr@777 151 double G1MMUTrackerQueue::when_sec(double current_time, double pause_time) {
ysr@777 152 if (_DISABLE_MMU)
ysr@777 153 return 0.0;
ysr@777 154
ysr@777 155 MutexLockerEx x(MMUTracker_lock, Mutex::_no_safepoint_check_flag);
ysr@777 156 remove_expired_entries(current_time);
ysr@777 157
ysr@777 158 return when_internal(current_time, pause_time);
ysr@777 159 }
ysr@777 160
ysr@777 161 double G1MMUTrackerQueue::when_internal(double current_time,
ysr@777 162 double pause_time) {
ysr@777 163 // if the pause is over the maximum, just assume that it's the maximum
ysr@777 164 double adjusted_pause_time =
ysr@777 165 (pause_time > max_gc_time()) ? max_gc_time() : pause_time;
ysr@777 166 double earliest_end = current_time + adjusted_pause_time;
ysr@777 167 double limit = earliest_end - _time_slice;
ysr@777 168 double gc_time = calculate_gc_time(earliest_end);
ysr@777 169 double diff = gc_time + adjusted_pause_time - max_gc_time();
ysr@777 170 if (is_double_leq_0(diff))
ysr@777 171 return 0.0;
ysr@777 172
ysr@777 173 int index = _tail_index;
ysr@777 174 while ( 1 ) {
ysr@777 175 G1MMUTrackerQueueElem *elem = &_array[index];
ysr@777 176 if (elem->end_time() > limit) {
ysr@777 177 if (elem->start_time() > limit)
ysr@777 178 diff -= elem->duration();
ysr@777 179 else
ysr@777 180 diff -= elem->end_time() - limit;
ysr@777 181 if (is_double_leq_0(diff))
ysr@777 182 return elem->end_time() + diff + _time_slice - adjusted_pause_time - current_time;
ysr@777 183 }
ysr@777 184 index = trim_index(index+1);
ysr@777 185 guarantee(index != trim_index(_head_index + 1), "should not go past head");
ysr@777 186 }
ysr@777 187 }

mercurial