37
37
38
38
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
39
39
40
+ class IOPolicy : public CHeapObj <mtGC> {
41
+ class IOWaitRecord {
42
+ public:
43
+ IOWaitRecord (bool *active) : _active(active), _last_cpu_total(0 ), _last_cpu_iowait(0 ) {}
44
+ void start () {
45
+ fill_value (&_last_cpu_total, &_last_cpu_iowait);
46
+ }
47
+ double stop () {
48
+ size_t total, iowait;
49
+ fill_value (&total, &iowait);
50
+
51
+ size_t total_diff = total - _last_cpu_total;
52
+ size_t iowait_diff = iowait - _last_cpu_iowait;
53
+ if (total_diff == 0 ) {
54
+ if (PrintAdaptiveSizePolicy) {
55
+ gclog_or_tty->print_cr (" fail to record, cpu total diff is 0" );
56
+ }
57
+ return 0 ;
58
+ } else {
59
+ return (double )iowait_diff / (double )total_diff;
60
+ }
61
+ }
62
+ private:
63
+ // if anything unexpected happened during record, we will deactivate the policy
64
+ bool *_active;
65
+ size_t _last_cpu_total;
66
+ size_t _last_cpu_iowait;
67
+ void fill_value_fail (FILE *file) {
68
+ if (file != NULL ) {
69
+ fclose (file);
70
+ }
71
+ warning (" Deactivate UseIOPrioritySizePolicy due to failed to parse cpu stat" );
72
+ *_active = false ;
73
+ }
74
+ void fill_value (size_t *total, size_t *iowait) {
75
+ FILE *file = fopen (" /proc/stat" , " r" );
76
+ if (file == NULL ) {
77
+ fill_value_fail (file);
78
+ return ;
79
+ }
80
+
81
+ char line[256 ];
82
+ char *read_line = fgets (line, sizeof (line), file);
83
+ if (read_line == NULL ) {
84
+ fill_value_fail (file);
85
+ return ;
86
+ }
87
+
88
+ /*
89
+ * Expected stdout of the first line of /proc/stat should be like:
90
+ * cat /proc/stat
91
+ * cpu 417487649 75106 102895030 23107566512 152075 65480092 6013218 0 0 0
92
+ */
93
+ size_t user, nice , system , idle, iowait_time, irq, softirq, steal, guest, guest_nice;
94
+ int parse_line = sscanf (line, " cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu" , &user,
95
+ &nice , &system , &idle, &iowait_time, &irq, &softirq, &steal, &guest, &guest_nice);
96
+ if (parse_line != 10 ) {
97
+ fill_value_fail (file);
98
+ return ;
99
+ }
100
+
101
+ *total = user + nice + system + idle + iowait_time + irq + softirq + steal + guest + guest_nice;
102
+ *iowait = iowait_time;
103
+ fclose (file);
104
+ }
105
+ };
106
+
107
+ class UserTimeRecord {
108
+ public:
109
+ UserTimeRecord (bool *active) : _active(active), _starting_user_time(0 ), _starting_system_time(0 ), _starting_real_time(0 ) {}
110
+ void start () {
111
+ if (!os::getTimesSecs (&_starting_real_time, &_starting_user_time, &_starting_system_time)) {
112
+ warning (" Deactivate UseIOPrioritySizePolicy due to failed to get cpu times" );
113
+ *_active = false ;
114
+ }
115
+ }
116
+ double stop () {
117
+ const static double INVALID = 99999 ;
118
+ double real_time, user_time, system_time;
119
+ if (!os::getTimesSecs (&real_time, &user_time, &system_time)) {
120
+ warning (" Deactivate UseIOPrioritySizePolicy due to failed to get cpu times" );
121
+ *_active = false ;
122
+ return INVALID;
123
+ }
124
+ double user_diff = user_time - _starting_user_time;
125
+ double real_diff = real_time - _starting_real_time;
126
+ // too short interval to calculate a meaningful user time percent, thus we
127
+ // return a very large number to avoid trigger memory reduction.
128
+ if (real_diff < 0.00001 ) {
129
+ if (PrintAdaptiveSizePolicy) {
130
+ gclog_or_tty->print_cr (" fail to record, real_duration too small: %f" ,
131
+ real_diff);
132
+ }
133
+ return INVALID;
134
+ }
135
+ return user_diff / real_diff;
136
+ }
137
+ private:
138
+ // if anything unexpected happened during record, we will deactivate the policy
139
+ bool *_active;
140
+ double _starting_user_time;
141
+ double _starting_system_time;
142
+ double _starting_real_time;
143
+ };
144
+
145
+ double _default_throughput_goal;
146
+
147
+ double _mutator_iowait_percent;
148
+ double _mutator_user_percent;
149
+ elapsedTimer _io_triggerred_major_gc_timer;
150
+
151
+ IOWaitRecord _io_wait_record;
152
+ UserTimeRecord _user_time_record;
153
+
154
+ bool _active;
155
+ bool _should_reduce_heap;
156
+
157
+ public:
158
+ IOPolicy (double default_throughput_goal) :
159
+ _default_throughput_goal (default_throughput_goal),
160
+ _mutator_iowait_percent (0.0 ),
161
+ _mutator_user_percent (0.0 ),
162
+ _io_triggerred_major_gc_timer (),
163
+ _io_wait_record (&_active),
164
+ _user_time_record (&_active),
165
+ _active (true ),
166
+ _should_reduce_heap (false ) {
167
+ _io_triggerred_major_gc_timer.start ();
168
+ start_mutator_record ();
169
+ if (FLAG_IS_CMDLINE (NewSize)) {
170
+ if (PrintAdaptiveSizePolicy) {
171
+ gclog_or_tty->print_cr (" NewSize or Xmn is set, which may introduce a large size for min young size" );
172
+ }
173
+ }
174
+ if (MaxHeapSize == InitialHeapSize) {
175
+ if (PrintAdaptiveSizePolicy) {
176
+ gclog_or_tty->print_cr (" Xmx is equal to Xms, which may introduce a large size for min young size" );
177
+ }
178
+ }
179
+ if (PrintAdaptiveSizePolicy) {
180
+ gclog_or_tty->print_cr (
181
+ " min size: young " SIZE_FORMAT " M, old " SIZE_FORMAT
182
+ " M. IOPrioritySizePolicy can't decrease heap below these sizes" ,
183
+ ParallelScavengeHeap::young_gen ()->min_gen_size () / M,
184
+ ParallelScavengeHeap::old_gen ()->min_gen_size () / M);
185
+ }
186
+ }
187
+
188
+ void start_mutator_record () {
189
+ if (!_active) {
190
+ return ;
191
+ }
192
+ _io_wait_record.start ();
193
+ _user_time_record.start ();
194
+ }
195
+
196
+ void stop_mutator_record () {
197
+ if (!_active) {
198
+ return ;
199
+ }
200
+ _mutator_iowait_percent = _io_wait_record.stop ();
201
+ _mutator_user_percent = _user_time_record.stop ();
202
+ }
203
+
204
+ void print (double mutator_cost) const {
205
+ if (!_active) {
206
+ return ;
207
+ }
208
+ if (PrintAdaptiveSizePolicy) {
209
+ gclog_or_tty->print_cr (" mutator cost: %f, iowait : %f, user: %f" ,
210
+ mutator_cost, _mutator_iowait_percent,
211
+ _mutator_user_percent);
212
+ }
213
+ }
214
+
215
+ bool should_full_GC () {
216
+ if (!_active) {
217
+ return false ;
218
+ }
219
+
220
+ // These thresholds are tuned by spark on TPC-DS workload.
221
+ const static double IOTriggerredFullGCUserThreshold = 0.75 ;
222
+ const static double IOTriggerredFullGCIOWaitThreshold = 0.4 ;
223
+ const static double IOTriggerredFullGCMinInterval =
224
+ 60 ; // can be set longer if io heavy workload lasts long.
225
+
226
+ if (_mutator_user_percent < IOTriggerredFullGCUserThreshold &&
227
+ _mutator_iowait_percent > IOTriggerredFullGCIOWaitThreshold) {
228
+ _io_triggerred_major_gc_timer.stop ();
229
+ if (_io_triggerred_major_gc_timer.seconds () >
230
+ IOTriggerredFullGCMinInterval) {
231
+ _io_triggerred_major_gc_timer.reset ();
232
+ _io_triggerred_major_gc_timer.start ();
233
+ if (PrintAdaptiveSizePolicy) {
234
+ gclog_or_tty->print_cr (" decrease old gen by full gc" );
235
+ }
236
+ return true ;
237
+ } else {
238
+ if (PrintAdaptiveSizePolicy) {
239
+ gclog_or_tty->print_cr (
240
+ " decrease old gen FAILED because interval is %f < %f" ,
241
+ _io_triggerred_major_gc_timer.seconds (),
242
+ IOTriggerredFullGCMinInterval);
243
+ }
244
+ _io_triggerred_major_gc_timer.start ();
245
+ return false ;
246
+ }
247
+ }
248
+ return false ;
249
+ }
250
+
251
+ double calculate_reduced_throughput_goal () {
252
+ if (!_active) {
253
+ return _default_throughput_goal;
254
+ }
255
+
256
+ const static double UserThreshold = 1.0 ;
257
+ const static double IOWaitThreshold = 0.1 ;
258
+
259
+ if (_mutator_user_percent < UserThreshold &&
260
+ _mutator_iowait_percent > IOWaitThreshold) {
261
+ double reduced_throughput_goal = _default_throughput_goal - (1 - _mutator_user_percent);
262
+ _should_reduce_heap = true ;
263
+ if (PrintAdaptiveSizePolicy) {
264
+ gclog_or_tty->print_cr (" decrease throughput goal to %.3f" ,
265
+ reduced_throughput_goal);
266
+ }
267
+ return reduced_throughput_goal;
268
+ } else {
269
+ _should_reduce_heap = false ;
270
+ return _default_throughput_goal;
271
+ }
272
+ }
273
+
274
+ size_t calculate_reduced_eden_size (size_t eden_size, float avg_survivor, size_t current_eden_size) const {
275
+ if (!_active || !_should_reduce_heap) {
276
+ return eden_size;
277
+ }
278
+ size_t reduced_size;
279
+ reduced_size = MIN (eden_size, avg_survivor * IOPrioritySizePolicyEdenScale);
280
+ reduced_size = MAX (reduced_size, ParallelScavengeHeap::heap ()->young_gen ()->max_size () / 10 );
281
+ if (PrintAdaptiveSizePolicy) {
282
+ gclog_or_tty->print_cr (
283
+ " decrease eden from " SIZE_FORMAT " M to " SIZE_FORMAT
284
+ " M , survivor avg: %fM, min threshold: " SIZE_FORMAT " M" ,
285
+ current_eden_size / M, reduced_size / M, avg_survivor / M,
286
+ ParallelScavengeHeap::heap ()->young_gen ()->max_size () / 10 / M);
287
+ }
288
+ return reduced_size;
289
+ }
290
+
291
+ size_t calculate_reduced_promo_size (size_t promo_size, float avg_promo, size_t current_promo_size) const {
292
+ if (!_active || !_should_reduce_heap) {
293
+ return promo_size;
294
+ }
295
+ const static float PromoScale = 5 ;
296
+ size_t reduced_size;
297
+ reduced_size = MIN (reduced_size, avg_promo * PromoScale);
298
+ reduced_size = MAX (reduced_size,
299
+ ParallelScavengeHeap::heap ()->old_gen ()->max_gen_size () / 10 );
300
+ if (PrintAdaptiveSizePolicy) {
301
+ gclog_or_tty->print_cr (
302
+ " decrease promotion from " SIZE_FORMAT " M to " SIZE_FORMAT
303
+ " M , promo avg: %fM, min threshold: " SIZE_FORMAT " M" ,
304
+ current_promo_size / M, reduced_size / M, avg_promo / M,
305
+ ParallelScavengeHeap::heap ()->old_gen ()->max_gen_size () / 10 / M);
306
+ }
307
+ return reduced_size;
308
+ }
309
+ };
310
+
311
+
40
312
PSAdaptiveSizePolicy::PSAdaptiveSizePolicy (size_t init_eden_size,
41
313
size_t init_promo_size,
42
314
size_t init_survivor_size,
@@ -54,6 +326,7 @@ PSAdaptiveSizePolicy::PSAdaptiveSizePolicy(size_t init_eden_size,
54
326
_live_at_last_full_gc(init_promo_size),
55
327
_gc_minor_pause_goal_sec(gc_minor_pause_goal_sec),
56
328
_latest_major_mutator_interval_seconds(0 ),
329
+ _throughput_goal(AdaptiveSizePolicy::_throughput_goal),
57
330
_young_gen_change_for_major_pause_count(0 )
58
331
{
59
332
// Sizing policy statistics
@@ -77,6 +350,9 @@ PSAdaptiveSizePolicy::PSAdaptiveSizePolicy(size_t init_eden_size,
77
350
_major_timer.start ();
78
351
79
352
_old_gen_policy_is_ready = false ;
353
+ if (UseIOPrioritySizePolicy) {
354
+ _io_policy = new IOPolicy (_throughput_goal);
355
+ }
80
356
}
81
357
82
358
size_t PSAdaptiveSizePolicy::calculate_free_based_on_live (size_t live, uintx ratio_as_percentage) {
@@ -111,6 +387,22 @@ size_t PSAdaptiveSizePolicy::calculated_old_free_size_in_bytes() const {
111
387
return free_size;
112
388
}
113
389
390
+ void PSAdaptiveSizePolicy::minor_collection_begin () {
391
+ AdaptiveSizePolicy::minor_collection_begin ();
392
+ if (UseIOPrioritySizePolicy) {
393
+ _io_policy->stop_mutator_record ();
394
+ }
395
+ }
396
+
397
+ void PSAdaptiveSizePolicy::minor_collection_end (GCCause::Cause gc_cause) {
398
+ AdaptiveSizePolicy::minor_collection_end (gc_cause);
399
+ if (UseIOPrioritySizePolicy) {
400
+ _io_policy->start_mutator_record ();
401
+ _io_policy->print (adjusted_mutator_cost ());
402
+ }
403
+ }
404
+
405
+
114
406
void PSAdaptiveSizePolicy::major_collection_begin () {
115
407
// Update the interval time
116
408
_major_timer.stop ();
@@ -131,6 +423,9 @@ void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live,
131
423
GCCause::Cause gc_cause) {
132
424
// Update the pause time.
133
425
_major_timer.stop ();
426
+ if (UseIOPrioritySizePolicy) {
427
+ _io_policy->start_mutator_record ();
428
+ }
134
429
135
430
if (gc_cause != GCCause::_java_lang_system_gc ||
136
431
UseAdaptiveSizePolicyWithSystemGC) {
@@ -175,6 +470,10 @@ void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live,
175
470
assert (collection_cost >= 0.0 , " Expected to be non-negative" );
176
471
_major_collection_estimator->update (promo_size_in_mbytes,
177
472
collection_cost);
473
+
474
+ if (UseIOPrioritySizePolicy) {
475
+ _io_policy->print (adjusted_mutator_cost ());
476
+ }
178
477
}
179
478
180
479
// Update the amount live at the end of a full GC
@@ -196,6 +495,11 @@ void PSAdaptiveSizePolicy::major_collection_end(size_t amount_live,
196
495
// that expected to be needed by the next collection, do a full
197
496
// collection now.
198
497
bool PSAdaptiveSizePolicy::should_full_GC (size_t old_free_in_bytes) {
498
+ if (UseIOPrioritySizePolicy) {
499
+ if (_io_policy->should_full_GC ()) {
500
+ return true ;
501
+ }
502
+ }
199
503
200
504
// A similar test is done in the scavenge's should_attempt_scavenge(). If
201
505
// this is changed, decide if that test should also be changed.
@@ -253,6 +557,9 @@ void PSAdaptiveSizePolicy::compute_eden_space_size(
253
557
size_t cur_eden,
254
558
size_t max_eden_size,
255
559
bool is_full_gc) {
560
+ if (UseIOPrioritySizePolicy) {
561
+ _throughput_goal = _io_policy->calculate_reduced_throughput_goal ();
562
+ }
256
563
257
564
// Update statistics
258
565
// Time statistics are updated as we go, update footprint stats here
@@ -1030,6 +1337,11 @@ size_t PSAdaptiveSizePolicy::adjust_promo_for_footprint(
1030
1337
1031
1338
size_t reduced_size = desired_promo_size - change;
1032
1339
1340
+ if (UseIOPrioritySizePolicy) {
1341
+ reduced_size = _io_policy->calculate_reduced_promo_size (reduced_size, avg_promoted ()->average (), desired_promo_size);
1342
+ change = desired_promo_size - reduced_size;
1343
+ }
1344
+
1033
1345
if (PrintAdaptiveSizePolicy && Verbose) {
1034
1346
gclog_or_tty->print_cr (
1035
1347
" AdaptiveSizePolicy::adjust_promo_for_footprint "
@@ -1054,6 +1366,11 @@ size_t PSAdaptiveSizePolicy::adjust_eden_for_footprint(
1054
1366
1055
1367
size_t reduced_size = desired_eden_size - change;
1056
1368
1369
+ if (UseIOPrioritySizePolicy) {
1370
+ reduced_size = _io_policy->calculate_reduced_eden_size (reduced_size, avg_survived ()->average (), desired_eden_size);
1371
+ change = desired_eden_size - reduced_size;
1372
+ }
1373
+
1057
1374
if (PrintAdaptiveSizePolicy && Verbose) {
1058
1375
gclog_or_tty->print_cr (
1059
1376
" AdaptiveSizePolicy::adjust_eden_for_footprint "
0 commit comments