Skip to content

Commit 53e4de6

Browse files
committed
8239786: Shenandoah: print per-cycle statistics
Reviewed-by: rkennke
1 parent 76e5da4 commit 53e4de6

File tree

4 files changed

+204
-63
lines changed

4 files changed

+204
-63
lines changed

src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2013, 2020, Red Hat, Inc. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -242,6 +242,22 @@ void ShenandoahControlThread::run_service() {
242242
heuristics->clear_metaspace_oom();
243243
}
244244

245+
// Commit worker statistics to cycle data
246+
heap->phase_timings()->flush_par_workers_to_cycle();
247+
248+
// Print GC stats for current cycle
249+
{
250+
LogTarget(Info, gc, stats) lt;
251+
if (lt.is_enabled()) {
252+
ResourceMark rm;
253+
LogStream ls(lt);
254+
heap->phase_timings()->print_cycle_on(&ls);
255+
}
256+
}
257+
258+
// Commit statistics to globals
259+
heap->phase_timings()->flush_cycle_to_global();
260+
245261
// Print Metaspace change following GC (if logging is enabled).
246262
MetaspaceUtils::print_metaspace_change(meta_sizes);
247263

@@ -265,6 +281,7 @@ void ShenandoahControlThread::run_service() {
265281
current :
266282
current - (ShenandoahUncommitDelay / 1000.0);
267283
service_uncommit(shrink_before);
284+
heap->phase_timings()->flush_cycle_to_global();
268285
last_shrink_time = current;
269286
}
270287

src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ void ShenandoahHeap::print_tracing_info() const {
11901190
ResourceMark rm;
11911191
LogStream ls(lt);
11921192

1193-
phase_timings()->print_on(&ls);
1193+
phase_timings()->print_global_on(&ls);
11941194

11951195
ls.cr();
11961196
ls.cr();

src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.cpp

Lines changed: 158 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,14 @@
3030
#include "gc/shenandoah/shenandoahHeap.hpp"
3131
#include "gc/shenandoah/shenandoahHeuristics.hpp"
3232
#include "gc/shenandoah/shenandoahUtils.hpp"
33+
#include "runtime/orderAccess.hpp"
3334
#include "utilities/ostream.hpp"
3435

36+
#define SHENANDOAH_PHASE_NAME_FORMAT "%-28s"
37+
#define SHENANDOAH_S_TIME_FORMAT "%8.3lf"
38+
#define SHENANDOAH_US_TIME_FORMAT "%8.0lf"
39+
#define SHENANDOAH_US_WORKER_TIME_FORMAT "%3.0lf"
40+
3541
#define GC_PHASE_DECLARE_NAME(type, title) \
3642
title,
3743

@@ -42,62 +48,155 @@ const char* ShenandoahPhaseTimings::_phase_names[] = {
4248
#undef GC_PHASE_DECLARE_NAME
4349

4450
ShenandoahPhaseTimings::ShenandoahPhaseTimings() {
45-
uint max_workers = MAX2(ConcGCThreads, ParallelGCThreads);
46-
assert(max_workers > 0, "Must have some GC threads");
51+
_max_workers = MAX2(ConcGCThreads, ParallelGCThreads);
52+
assert(_max_workers > 0, "Must have some GC threads");
4753

48-
#define GC_PAR_PHASE_DECLARE_WORKER_DATA(type, title) \
49-
_gc_par_phases[ShenandoahPhaseTimings::type] = new WorkerDataArray<double>(title, max_workers);
50-
// Root scanning phases
51-
SHENANDOAH_GC_PAR_PHASE_DO(,, GC_PAR_PHASE_DECLARE_WORKER_DATA)
52-
#undef GC_PAR_PHASE_DECLARE_WORKER_DATA
54+
// Initialize everything to sane defaults
55+
for (uint i = 0; i < _num_phases; i++) {
56+
#define SHENANDOAH_WORKER_DATA_NULL(type, title) \
57+
_worker_data[i] = NULL;
58+
SHENANDOAH_GC_PAR_PHASE_DO(,, SHENANDOAH_WORKER_DATA_NULL)
59+
#undef SHENANDOAH_WORKER_DATA_NULL
60+
_cycle_data[i] = 0;
61+
}
62+
63+
// Then punch in the worker-related data.
64+
// Every worker phase get a bunch of internal objects, except
65+
// the very first slot, which is "<total>" and is not populated.
66+
for (uint i = 0; i < _num_phases; i++) {
67+
if (is_worker_phase(Phase(i))) {
68+
int c = 0;
69+
#define SHENANDOAH_WORKER_DATA_INIT(type, title) \
70+
if (c++ != 0) _worker_data[i + c] = new ShenandoahWorkerData(title, _max_workers);
71+
SHENANDOAH_GC_PAR_PHASE_DO(,, SHENANDOAH_WORKER_DATA_INIT)
72+
#undef SHENANDOAH_WORKER_DATA_INIT
73+
}
74+
}
5375

5476
_policy = ShenandoahHeap::heap()->shenandoah_policy();
5577
assert(_policy != NULL, "Can not be NULL");
78+
79+
_current_worker_phase = _invalid_phase;
80+
}
81+
82+
ShenandoahPhaseTimings::Phase ShenandoahPhaseTimings::worker_par_phase(Phase phase, GCParPhases par_phase) {
83+
assert(is_worker_phase(phase), "Phase should accept worker phase times: %s", phase_name(phase));
84+
Phase p = Phase(phase + 1 + par_phase);
85+
assert(p >= 0 && p < _num_phases, "Out of bound for: %s", phase_name(phase));
86+
return p;
87+
}
88+
89+
ShenandoahWorkerData* ShenandoahPhaseTimings::worker_data(Phase phase, GCParPhases par_phase) {
90+
Phase p = worker_par_phase(phase, par_phase);
91+
ShenandoahWorkerData* wd = _worker_data[p];
92+
assert(wd != NULL, "Counter initialized: %s", phase_name(p));
93+
return wd;
94+
}
95+
96+
bool ShenandoahPhaseTimings::is_worker_phase(Phase phase) {
97+
assert(phase >= 0 && phase < _num_phases, "Out of bounds");
98+
switch (phase) {
99+
case init_evac:
100+
case scan_roots:
101+
case update_roots:
102+
case final_update_refs_roots:
103+
case full_gc_scan_roots:
104+
case full_gc_update_roots:
105+
case full_gc_adjust_roots:
106+
case degen_gc_update_roots:
107+
case full_gc_purge_class_unload:
108+
case full_gc_purge_weak_par:
109+
case purge_class_unload:
110+
case purge_weak_par:
111+
case heap_iteration_roots:
112+
return true;
113+
default:
114+
return false;
115+
}
116+
}
117+
118+
void ShenandoahPhaseTimings::set_cycle_data(Phase phase, double time) {
119+
#ifdef ASSERT
120+
double d = _cycle_data[phase];
121+
assert(d == 0, "Should not be set yet: %s, current value: %lf", phase_name(phase), d);
122+
#endif
123+
_cycle_data[phase] = time;
56124
}
57125

58126
void ShenandoahPhaseTimings::record_phase_time(Phase phase, double time) {
59127
if (!_policy->is_at_shutdown()) {
60-
_timing_data[phase].add(time);
128+
set_cycle_data(phase, time);
61129
}
62130
}
63131

64132
void ShenandoahPhaseTimings::record_workers_start(Phase phase) {
65-
for (uint i = 0; i < GCParPhasesSentinel; i++) {
66-
_gc_par_phases[i]->reset();
133+
assert(is_worker_phase(phase), "Phase should accept worker phase times: %s", phase_name(phase));
134+
135+
assert(_current_worker_phase == _invalid_phase, "Should not be set yet: requested %s, existing %s",
136+
phase_name(phase), phase_name(_current_worker_phase));
137+
_current_worker_phase = phase;
138+
139+
for (uint i = 1; i < GCParPhasesSentinel; i++) {
140+
worker_data(phase, GCParPhases(i))->reset();
67141
}
68142
}
69143

70144
void ShenandoahPhaseTimings::record_workers_end(Phase phase) {
71-
if (_policy->is_at_shutdown()) {
72-
// Do not record the past-shutdown events
73-
return;
145+
assert(is_worker_phase(phase), "Phase should accept worker phase times: %s", phase_name(phase));
146+
_current_worker_phase = _invalid_phase;
147+
}
148+
149+
void ShenandoahPhaseTimings::flush_par_workers_to_cycle() {
150+
for (uint pi = 0; pi < _num_phases; pi++) {
151+
Phase phase = Phase(pi);
152+
if (is_worker_phase(phase)) {
153+
double s = 0;
154+
for (uint i = 1; i < GCParPhasesSentinel; i++) {
155+
double t = worker_data(phase, GCParPhases(i))->sum();
156+
// add to each line in phase
157+
set_cycle_data(Phase(phase + i + 1), t);
158+
s += t;
159+
}
160+
// add to total for phase
161+
set_cycle_data(Phase(phase + 1), s);
162+
}
74163
}
164+
}
75165

76-
assert(phase == init_evac ||
77-
phase == scan_roots ||
78-
phase == update_roots ||
79-
phase == final_update_refs_roots ||
80-
phase == full_gc_scan_roots ||
81-
phase == full_gc_update_roots ||
82-
phase == full_gc_adjust_roots ||
83-
phase == degen_gc_update_roots ||
84-
phase == full_gc_purge_class_unload ||
85-
phase == full_gc_purge_weak_par ||
86-
phase == purge_class_unload ||
87-
phase == purge_weak_par ||
88-
phase == heap_iteration_roots,
89-
"Phase should accept accept per-thread phase times: %s", phase_name(phase));
90-
91-
double s = 0;
92-
for (uint i = 1; i < GCParPhasesSentinel; i++) {
93-
double t = _gc_par_phases[i]->sum();
94-
_timing_data[phase + i + 1].add(t); // add to each line in phase
95-
s += t;
166+
void ShenandoahPhaseTimings::flush_cycle_to_global() {
167+
for (uint i = 0; i < _num_phases; i++) {
168+
_global_data[i].add(_cycle_data[i]);
169+
_cycle_data[i] = 0;
96170
}
97-
_timing_data[phase + 1].add(s); // add to total for phase
171+
OrderAccess::fence();
98172
}
99173

100-
void ShenandoahPhaseTimings::print_on(outputStream* out) const {
174+
void ShenandoahPhaseTimings::print_cycle_on(outputStream* out) const {
175+
out->cr();
176+
out->print_cr("All times are wall-clock times, except per-root-class counters, that are sum over");
177+
out->print_cr("all workers. Dividing the <total> over the root stage time estimates parallelism.");
178+
out->cr();
179+
for (uint i = 0; i < _num_phases; i++) {
180+
double v = _cycle_data[i] * 1000000.0;
181+
if (v > 0) {
182+
out->print(SHENANDOAH_PHASE_NAME_FORMAT " " SHENANDOAH_US_TIME_FORMAT " us", _phase_names[i], v);
183+
if (_worker_data[i] != NULL) {
184+
out->print(", workers (us): ");
185+
for (size_t c = 0; c < _max_workers; c++) {
186+
double tv = _worker_data[i]->get(c);
187+
if (tv != ShenandoahWorkerData::uninitialized()) {
188+
out->print(SHENANDOAH_US_WORKER_TIME_FORMAT ", ", tv * 1000000.0);
189+
} else {
190+
out->print("%3s, ", "---");
191+
}
192+
}
193+
}
194+
out->cr();
195+
}
196+
}
197+
}
198+
199+
void ShenandoahPhaseTimings::print_global_on(outputStream* out) const {
101200
out->cr();
102201
out->print_cr("GC STATISTICS:");
103202
out->print_cr(" \"(G)\" (gross) pauses include VM time: time to notify and block threads, do the pre-");
@@ -111,37 +210,43 @@ void ShenandoahPhaseTimings::print_on(outputStream* out) const {
111210
out->cr();
112211

113212
for (uint i = 0; i < _num_phases; i++) {
114-
if (_timing_data[i].maximum() != 0) {
115-
out->print_cr("%-27s = %8.2lf s (a = %8.0lf us) (n = " INT32_FORMAT_W(5) ") (lvls, us = %8.0lf, %8.0lf, %8.0lf, %8.0lf, %8.0lf)",
213+
if (_global_data[i].maximum() != 0) {
214+
out->print_cr(SHENANDOAH_PHASE_NAME_FORMAT " = " SHENANDOAH_S_TIME_FORMAT " s "
215+
"(a = " SHENANDOAH_US_TIME_FORMAT " us) "
216+
"(n = " INT32_FORMAT_W(5) ") (lvls, us = "
217+
SHENANDOAH_US_TIME_FORMAT ", "
218+
SHENANDOAH_US_TIME_FORMAT ", "
219+
SHENANDOAH_US_TIME_FORMAT ", "
220+
SHENANDOAH_US_TIME_FORMAT ", "
221+
SHENANDOAH_US_TIME_FORMAT ")",
116222
_phase_names[i],
117-
_timing_data[i].sum(),
118-
_timing_data[i].avg() * 1000000.0,
119-
_timing_data[i].num(),
120-
_timing_data[i].percentile(0) * 1000000.0,
121-
_timing_data[i].percentile(25) * 1000000.0,
122-
_timing_data[i].percentile(50) * 1000000.0,
123-
_timing_data[i].percentile(75) * 1000000.0,
124-
_timing_data[i].maximum() * 1000000.0
223+
_global_data[i].sum(),
224+
_global_data[i].avg() * 1000000.0,
225+
_global_data[i].num(),
226+
_global_data[i].percentile(0) * 1000000.0,
227+
_global_data[i].percentile(25) * 1000000.0,
228+
_global_data[i].percentile(50) * 1000000.0,
229+
_global_data[i].percentile(75) * 1000000.0,
230+
_global_data[i].maximum() * 1000000.0
125231
);
126232
}
127233
}
128234
}
129235

130-
void ShenandoahPhaseTimings::record_worker_time(ShenandoahPhaseTimings::GCParPhases phase, uint worker_id, double secs) {
131-
_gc_par_phases[phase]->set(worker_id, secs);
132-
}
133-
134-
ShenandoahWorkerTimingsTracker::ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::GCParPhases phase, uint worker_id) :
135-
_phase(phase), _timings(ShenandoahHeap::heap()->phase_timings()), _worker_id(worker_id) {
236+
ShenandoahWorkerTimingsTracker::ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::GCParPhases par_phase, uint worker_id) :
237+
_timings(ShenandoahHeap::heap()->phase_timings()), _phase(_timings->current_worker_phase()),
238+
_par_phase(par_phase), _worker_id(worker_id) {
239+
assert(_timings->worker_data(_phase, _par_phase)->get(_worker_id) == ShenandoahWorkerData::uninitialized(),
240+
"Should not be set yet: %s", ShenandoahPhaseTimings::phase_name(_timings->worker_par_phase(_phase, _par_phase)));
136241
_start_time = os::elapsedTime();
137242
}
138243

139244
ShenandoahWorkerTimingsTracker::~ShenandoahWorkerTimingsTracker() {
140-
_timings->record_worker_time(_phase, _worker_id, os::elapsedTime() - _start_time);
245+
_timings->worker_data(_phase, _par_phase)->set(_worker_id, os::elapsedTime() - _start_time);
141246

142247
if (ShenandoahGCPhase::is_root_work_phase()) {
143248
ShenandoahPhaseTimings::Phase root_phase = ShenandoahGCPhase::current_phase();
144-
ShenandoahPhaseTimings::Phase cur_phase = (ShenandoahPhaseTimings::Phase)((int)root_phase + (int)_phase + 1);
249+
ShenandoahPhaseTimings::Phase cur_phase = _timings->worker_par_phase(root_phase, _par_phase);
145250
_event.commit(GCId::current(), _worker_id, ShenandoahPhaseTimings::phase_name(cur_phase));
146251
}
147252
}

src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
2+
* Copyright (c) 2017, 2020, Red Hat, Inc. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -156,7 +156,11 @@ class outputStream;
156156
SHENANDOAH_GC_PAR_PHASE_DO(heap_iteration_roots_, " HI: ", f) \
157157
// end
158158

159+
typedef WorkerDataArray<double> ShenandoahWorkerData;
160+
159161
class ShenandoahPhaseTimings : public CHeapObj<mtGC> {
162+
friend class ShenandoahGCPhase;
163+
friend class ShenandoahWorkerTimingsTracker;
160164
public:
161165
#define GC_PHASE_DECLARE_ENUM(type, title) type,
162166

@@ -174,39 +178,54 @@ class ShenandoahPhaseTimings : public CHeapObj<mtGC> {
174178
#undef GC_PHASE_DECLARE_ENUM
175179

176180
private:
177-
HdrSeq _timing_data[_num_phases];
181+
size_t _max_workers;
182+
double _cycle_data[_num_phases];
183+
HdrSeq _global_data[_num_phases];
178184
static const char* _phase_names[_num_phases];
179185

180-
WorkerDataArray<double>* _gc_par_phases[ShenandoahPhaseTimings::GCParPhasesSentinel];
186+
Phase _current_worker_phase;
187+
ShenandoahWorkerData* _worker_data[_num_phases];
181188
ShenandoahCollectorPolicy* _policy;
182189

190+
static bool is_worker_phase(Phase phase);
191+
Phase current_worker_phase() { return _current_worker_phase; }
192+
193+
ShenandoahWorkerData* worker_data(Phase phase, GCParPhases par_phase);
194+
Phase worker_par_phase(Phase phase, GCParPhases par_phase);
195+
196+
void set_cycle_data(Phase phase, double time);
197+
183198
public:
184199
ShenandoahPhaseTimings();
185200

186201
void record_phase_time(Phase phase, double time);
187-
void record_worker_time(GCParPhases phase, uint worker_id, double time);
188202

189203
void record_workers_start(Phase phase);
190204
void record_workers_end(Phase phase);
191205

206+
void flush_par_workers_to_cycle();
207+
void flush_cycle_to_global();
208+
192209
static const char* phase_name(Phase phase) {
193210
assert(phase >= 0 && phase < _num_phases, "Out of bound");
194211
return _phase_names[phase];
195212
}
196213

197-
void print_on(outputStream* out) const;
214+
void print_cycle_on(outputStream* out) const;
215+
void print_global_on(outputStream* out) const;
198216
};
199217

200218
class ShenandoahWorkerTimingsTracker : public StackObj {
201219
private:
202-
ShenandoahPhaseTimings::GCParPhases const _phase;
203-
ShenandoahPhaseTimings* const _timings;
220+
ShenandoahPhaseTimings* const _timings;
221+
ShenandoahPhaseTimings::Phase const _phase;
222+
ShenandoahPhaseTimings::GCParPhases const _par_phase;
204223
uint const _worker_id;
205224

206225
double _start_time;
207226
EventGCPhaseParallel _event;
208227
public:
209-
ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::GCParPhases phase, uint worker_id);
228+
ShenandoahWorkerTimingsTracker(ShenandoahPhaseTimings::GCParPhases par_phase, uint worker_id);
210229
~ShenandoahWorkerTimingsTracker();
211230
};
212231

0 commit comments

Comments
 (0)