1 /* 2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "jfr/recorder/service/jfrMemorySizer.hpp" 27 #include "runtime/os.hpp" 28 29 const julong MAX_ADJUSTED_GLOBAL_BUFFER_SIZE = 1 * M; 30 const julong MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF = 512 * K; 31 const julong MIN_GLOBAL_BUFFER_SIZE = 64 * K; 32 // implies at least 2 * MIN_GLOBAL_BUFFER SIZE 33 const julong MIN_BUFFER_COUNT = 2; 34 // MAX global buffer count open ended 35 const julong DEFAULT_BUFFER_COUNT = 20; 36 // MAX thread local buffer size == size of a single global buffer (runtime determined) 37 // DEFAULT thread local buffer size = 2 * os page size (runtime determined) 38 const julong MIN_THREAD_BUFFER_SIZE = 4 * K; 39 const julong MIN_MEMORY_SIZE = 1 * M; 40 const julong DEFAULT_MEMORY_SIZE = 10 * M; 41 42 // 43 // In pages: 44 // 45 // units = total_pages / per_unit_pages 46 // 47 static julong div_pages(julong& total_pages, julong& per_unit_pages) { 48 assert(total_pages > 0, "invariant"); 49 assert(per_unit_pages > 0, "invariant"); 50 assert(total_pages >= per_unit_pages, "invariant"); 51 52 const julong units = total_pages / per_unit_pages; 53 const julong rem = total_pages % per_unit_pages; 54 55 assert(units > 0, "invariant"); 56 57 if (rem > 0) { 58 total_pages -= rem % units; 59 per_unit_pages += rem / units; 60 } 61 62 assert(per_unit_pages > 0, "invariant"); 63 assert(total_pages % units == 0, "invariant"); 64 assert(units * per_unit_pages == total_pages, "invariant"); 65 assert(units == total_pages / per_unit_pages, "invariant"); 66 67 return units; 68 } 69 70 static void page_size_align_up(julong& value) { 71 static const julong alignment = os::vm_page_size() - 1; 72 value = (value + alignment) & ~alignment; 73 } 74 75 // 76 // In bytes: 77 // units = total_bytes / per_unit_bytes 78 // 79 static julong div_total_by_per_unit(julong& total_bytes, julong& per_unit_bytes) { 80 assert(total_bytes > 0, "invariant"); 81 assert(per_unit_bytes > 0, "invariant"); 82 assert(total_bytes >= per_unit_bytes, "invariant"); 83 84 page_size_align_up(total_bytes); 85 assert(total_bytes % os::vm_page_size() == 0, "invariant"); 86 julong total_pages = total_bytes / os::vm_page_size(); 87 88 page_size_align_up(per_unit_bytes); 89 assert(per_unit_bytes % os::vm_page_size() == 0, "invariant"); 90 julong per_unit_pages = per_unit_bytes / os::vm_page_size(); 91 92 const julong units = div_pages(total_pages, per_unit_pages); 93 assert(units > 0, "invariant"); 94 95 total_bytes = total_pages * os::vm_page_size(); 96 per_unit_bytes = per_unit_pages * os::vm_page_size(); 97 98 assert(per_unit_bytes > 0, "invariant"); 99 assert(total_bytes / per_unit_bytes == units, "invariant"); 100 101 return units; 102 } 103 104 // 105 // per_unit_bytes = total_bytes / units 106 // 107 static julong div_total_by_units(julong& total_bytes, julong& units) { 108 page_size_align_up(total_bytes); 109 assert(total_bytes % os::vm_page_size() == 0, "invariant"); 110 julong total_pages = total_bytes / os::vm_page_size(); 111 assert(units > 0, "invariant"); 112 113 julong per_unit_pages = total_pages <= units ? 1 : total_pages / units; 114 units = div_pages(total_pages, per_unit_pages); 115 116 julong per_unit_bytes = per_unit_pages * os::vm_page_size(); 117 assert(per_unit_bytes % os::vm_page_size() == 0, "invariant"); 118 119 total_bytes = total_pages * os::vm_page_size(); 120 assert(total_bytes % os::vm_page_size() == 0, "invariant"); 121 122 assert(total_bytes % units == 0, "invariant"); 123 assert(total_bytes / units == per_unit_bytes, "invariant"); 124 assert(units * per_unit_bytes == total_bytes, "invariant"); 125 126 return per_unit_bytes; 127 } 128 129 // 130 // total_bytes = per_unit_bytes * units; 131 // 132 static julong multiply(julong& per_unit_bytes, julong& units) { 133 page_size_align_up(per_unit_bytes); 134 assert(per_unit_bytes % os::vm_page_size() == 0, "invariant"); 135 assert(units > 0, "invariant"); 136 137 julong total_bytes = per_unit_bytes * units; 138 assert(total_bytes % os::vm_page_size() == 0, "invariant"); 139 140 assert(total_bytes % units == 0, "invariant"); 141 assert(total_bytes / units == per_unit_bytes, "invariant"); 142 assert(units * per_unit_bytes == total_bytes, "invariant"); 143 144 return total_bytes; 145 } 146 147 // Total_bytes is explicitly set. 148 // 149 // Deduce other parameters by delegating to a sizing policy 150 template <typename SizingPolicy> 151 static julong adjust(JfrMemoryOptions* options) { 152 page_size_align_up(options->memory_size); 153 assert(options->memory_size % os::vm_page_size() == 0, "invariant"); 154 julong total_pages = options->memory_size / os::vm_page_size(); 155 assert(options->buffer_count > 0, "invariant"); 156 julong per_unit_pages = total_pages / options->buffer_count; 157 page_size_align_up(options->thread_buffer_size); 158 assert(options->thread_buffer_size % os::vm_page_size() == 0, "invariant"); 159 julong thread_buffer_pages = options->thread_buffer_size / os::vm_page_size(); 160 161 SizingPolicy::adjust(total_pages, per_unit_pages, options->buffer_count, thread_buffer_pages, options->thread_buffer_size_configured); 162 assert(options->buffer_count * per_unit_pages == total_pages, "invariant"); 163 164 const julong per_unit_bytes = per_unit_pages * os::vm_page_size(); 165 options->memory_size = total_pages * os::vm_page_size(); 166 options->thread_buffer_size = thread_buffer_pages * os::vm_page_size(); 167 168 assert(options->memory_size % options->buffer_count == 0, "invariant"); 169 assert(options->memory_size / options->buffer_count == per_unit_bytes, "invariant"); 170 assert(options->buffer_count * per_unit_bytes == options->memory_size, "invariant"); 171 assert(per_unit_bytes >= options->thread_buffer_size, "invariant"); 172 return per_unit_bytes; 173 } 174 175 static void align_buffer_size(julong& buffer_size_in_pages, julong max_size_pages, julong min_size_pages, bool sizeup = false) { 176 buffer_size_in_pages = MIN2(buffer_size_in_pages, max_size_pages); 177 buffer_size_in_pages = MAX2(buffer_size_in_pages, min_size_pages); 178 size_t multiples = 0; 179 if (buffer_size_in_pages < max_size_pages) { 180 while (buffer_size_in_pages >= 181 (min_size_pages << (multiples + (sizeup ? 0 : 1)))) { 182 ++multiples; 183 } 184 buffer_size_in_pages = min_size_pages << multiples; 185 } 186 assert(buffer_size_in_pages >= min_size_pages && buffer_size_in_pages <= max_size_pages, "invariant"); 187 } 188 189 static void adjust_buffer_size_to_total_memory_size(julong& total_pages, julong& buffer_size_pages) { 190 static const julong max_buffer_size_pages = MAX_ADJUSTED_GLOBAL_BUFFER_SIZE / os::vm_page_size(); 191 // If memory size is less than DEFAULT_MEMORY_SIZE, 192 // the adjustment algorithm can decrease the size of the global buffer 193 // all the way down to the MIN_GLOBAL_BUFFER_SIZE (taking embedded use case in account). 194 // If memory size is larger than DEFAULT_MEMORY_SIZE, the lowest size of 195 // a global buffer will be the size of MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF 196 static const julong min_buffer_size_pages = 197 total_pages * os::vm_page_size() < DEFAULT_MEMORY_SIZE ? 198 MIN_GLOBAL_BUFFER_SIZE / os::vm_page_size() : 199 MIN_ADJUSTED_GLOBAL_BUFFER_SIZE_CUTOFF / os::vm_page_size(); 200 201 align_buffer_size(buffer_size_pages, max_buffer_size_pages, min_buffer_size_pages); 202 assert(buffer_size_pages % min_buffer_size_pages == 0, "invariant"); 203 204 julong remainder = total_pages % buffer_size_pages; 205 while (remainder >= (buffer_size_pages >> 1)) { 206 if (buffer_size_pages <= min_buffer_size_pages) { 207 break; 208 } 209 buffer_size_pages >>= 1; 210 remainder = total_pages % buffer_size_pages; 211 } 212 } 213 214 // Sizing policy class 215 class ScaleOutAdjuster : public AllStatic { 216 public: 217 static void adjust(julong& total_pages, 218 julong& buffer_size_pages, 219 julong& buffer_count, 220 julong& thread_buffer_size_pages, 221 bool is_thread_buffer_size_set) { 222 assert(buffer_count > 0, "invariant"); 223 adjust_buffer_size_to_total_memory_size(total_pages, buffer_size_pages); 224 assert(buffer_size_pages * os::vm_page_size() >= MIN_GLOBAL_BUFFER_SIZE, "invariant"); 225 assert((buffer_size_pages * os::vm_page_size()) % MIN_GLOBAL_BUFFER_SIZE == 0, "invariant"); 226 if (is_thread_buffer_size_set) { 227 if (thread_buffer_size_pages > buffer_size_pages) { 228 buffer_size_pages = thread_buffer_size_pages; 229 } 230 } 231 // and with this information, calculate what the new buffer count will be 232 buffer_count = div_pages(total_pages, buffer_size_pages); 233 } 234 }; 235 236 static void memory_and_thread_buffer_size(JfrMemoryOptions* options) { 237 assert(options->memory_size_configured, "invariant"); 238 assert(!options->buffer_count_configured, "invariant"); 239 assert(!options->global_buffer_size_configured, "invariant"); 240 // here the only thing specified is the overall total memory size 241 // we can and will apply some sizing heuristics to derive both 242 // the size of an individual global buffer and by implication the number of global 243 // buffers to use. Starting values for buffer count and global_buffer_size 244 // will be the defaults. 245 options->global_buffer_size = adjust<ScaleOutAdjuster>(options); 246 } 247 248 static void memory_size_and_buffer_count(JfrMemoryOptions* options) { 249 assert(options->memory_size_configured, "invariant"); 250 assert(!options->global_buffer_size_configured, "invariant"); 251 assert(!options->thread_buffer_size_configured, "invariant"); 252 assert(options->buffer_count_configured, "invariant"); 253 options->global_buffer_size = div_total_by_units(options->memory_size, options->buffer_count); 254 } 255 256 static void memory_size_and_global_buffer_size(JfrMemoryOptions* options) { 257 assert(options->memory_size_configured, "invariant"); 258 assert(options->global_buffer_size_configured, "invariant"); 259 assert(!options->buffer_count_configured, "invariant"); 260 page_size_align_up(options->thread_buffer_size); 261 options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size); 262 if (options->thread_buffer_size > options->global_buffer_size) { 263 options->global_buffer_size = options->thread_buffer_size; 264 options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size); 265 } 266 assert(options->global_buffer_size >= options->thread_buffer_size, "invariant"); 267 } 268 269 static bool is_ambiguous(const JfrMemoryOptions* options) { 270 assert(options->memory_size_configured, "invariant"); 271 assert(options->global_buffer_size_configured, "invariant"); 272 assert(options->buffer_count_configured, "invariant"); 273 assert(options->thread_buffer_size <= options->global_buffer_size, "invariant"); 274 // This can cause an ambiguous situation because all three parameters are explicitly set. 275 return options->global_buffer_size * options->buffer_count != options->memory_size; 276 } 277 278 static void all_options_set(JfrMemoryOptions* options) { 279 options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size); 280 page_size_align_up(options->thread_buffer_size); 281 if (options->thread_buffer_size > options->global_buffer_size) { 282 options->global_buffer_size = options->thread_buffer_size; 283 options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size); 284 } 285 assert(options->global_buffer_size >= options->thread_buffer_size, "invariant"); 286 assert(options->memory_size / options->global_buffer_size == options->buffer_count, "invariant"); 287 assert(options->memory_size % options->global_buffer_size == 0, "invariant"); 288 } 289 290 static void global_buffer_size(JfrMemoryOptions* options) { 291 assert(!options->memory_size_configured, "invariant"); 292 page_size_align_up(options->thread_buffer_size); 293 if (options->thread_buffer_size > options->global_buffer_size) { 294 options->global_buffer_size = options->thread_buffer_size; 295 } 296 options->memory_size = multiply(options->global_buffer_size, options->buffer_count); 297 assert(options->global_buffer_size >= options->thread_buffer_size, "invariant"); 298 } 299 300 static void thread_buffer_size(JfrMemoryOptions* options) { 301 assert(!options->global_buffer_size_configured, "invariant"); 302 assert(options->thread_buffer_size_configured, "invariant"); 303 page_size_align_up(options->thread_buffer_size); 304 options->global_buffer_size = div_total_by_units(options->memory_size, options->buffer_count); 305 if (options->thread_buffer_size > options->global_buffer_size) { 306 options->global_buffer_size = options->thread_buffer_size; 307 options->buffer_count = div_total_by_per_unit(options->memory_size, options->global_buffer_size); 308 } 309 assert(options->global_buffer_size >= options->thread_buffer_size, "invariant"); 310 } 311 312 static void default_size(const JfrMemoryOptions* options) { 313 // no memory options explicitly set 314 // default values already statically adjusted 315 assert(!options->thread_buffer_size_configured, "invariant"); 316 assert(!options->memory_size_configured, "invariant"); 317 assert(!options->buffer_count_configured, "invarinat"); 318 assert(!options->global_buffer_size_configured, "invariant"); 319 } 320 321 #ifdef ASSERT 322 static void assert_post_condition(const JfrMemoryOptions* options) { 323 assert(options->memory_size % os::vm_page_size() == 0, "invariant"); 324 assert(options->global_buffer_size % os::vm_page_size() == 0, "invariant"); 325 assert(options->thread_buffer_size % os::vm_page_size() == 0, "invariant"); 326 assert(options->buffer_count > 0, "invariant"); 327 } 328 #endif 329 330 // MEMORY SIZING ALGORITHM 331 332 bool JfrMemorySizer::adjust_options(JfrMemoryOptions* options) { 333 assert(options != NULL, "invariant"); 334 335 enum MemoryOptions { 336 MEMORY_SIZE = 1, 337 GLOBAL_BUFFER_SIZE = 2, 338 GLOBAL_BUFFER_COUNT = 4, 339 THREAD_BUFFER_SIZE = 8 340 }; 341 342 // LEGEND 343 // 344 // M = "memorysize" option 345 // G = "globalbuffersize" option 346 // C = "numglobalbuffers" option 347 // T = "threadbuffersize" option 348 // 349 // The memory options comprise an n-set (a 4-set) = { M, G, C, T } 350 // 351 // Number of r-subsets = 5 (0, 1, 2, 3, 4) (including null set) 352 // 353 // Unordered selection: 354 // 355 // C(4, 0) = {} = NULL set = 1 356 // C(4, 1) = { (M), (G), (C), (T) } = 4 357 // C(4, 2) = { (M, G), (M, C), (M, T), (G, C), (G, T), (C, T) } = 6 358 // C(4, 3) = { (M, G, C), (M, G, T), (M, C, T), (G, C, T) } = 4 359 // C(4, 4) = { (M, G, C, T) } = 1 360 // 361 // in shorter terms: P({ M, G, C, T}) = 16 362 // 363 #define MG (MEMORY_SIZE | GLOBAL_BUFFER_SIZE) 364 #define MC (MEMORY_SIZE | GLOBAL_BUFFER_COUNT) 365 #define MT (MEMORY_SIZE | THREAD_BUFFER_SIZE) 366 #define MGC (MG | GLOBAL_BUFFER_COUNT) 367 #define MGT (MG | THREAD_BUFFER_SIZE) 368 #define MCT (MC | THREAD_BUFFER_SIZE) 369 #define MGCT (MGC | THREAD_BUFFER_SIZE) 370 #define GC (GLOBAL_BUFFER_SIZE | GLOBAL_BUFFER_COUNT) 371 #define GT (GLOBAL_BUFFER_SIZE | THREAD_BUFFER_SIZE) 372 #define GCT (GC | THREAD_BUFFER_SIZE) 373 #define CT (GLOBAL_BUFFER_COUNT | THREAD_BUFFER_SIZE) 374 375 int set_of_options = 0; 376 377 if (options->memory_size_configured) { 378 set_of_options |= MEMORY_SIZE; 379 } 380 if (options->global_buffer_size_configured) { 381 set_of_options |= GLOBAL_BUFFER_SIZE; 382 } 383 if (options->buffer_count_configured) { 384 set_of_options |= GLOBAL_BUFFER_COUNT; 385 } 386 if (options->thread_buffer_size_configured) { 387 set_of_options |= THREAD_BUFFER_SIZE; 388 } 389 390 switch (set_of_options) { 391 case MT: 392 case MEMORY_SIZE: 393 memory_and_thread_buffer_size(options); 394 break; 395 case MC: 396 memory_size_and_buffer_count(options); 397 break; 398 case MGT: 399 assert(options->thread_buffer_size_configured, "invariant"); 400 case MG: 401 memory_size_and_global_buffer_size(options); 402 break; 403 case MGC: 404 case MGCT: 405 if (is_ambiguous(options)) { 406 // Let the user resolve the ambiguity by bailing. 407 return false; 408 } 409 all_options_set(options); 410 break; 411 case GCT: 412 assert(options->buffer_count_configured, "invariant"); 413 assert(options->thread_buffer_size_configured, "invariant"); 414 case GC: 415 assert(options->global_buffer_size_configured, "invariant"); 416 case GT: 417 case GLOBAL_BUFFER_COUNT: 418 case GLOBAL_BUFFER_SIZE: 419 global_buffer_size(options); 420 break; 421 case MCT: 422 assert(options->memory_size_configured, "invariant"); 423 case CT: 424 assert(options->buffer_count_configured, "invariant"); 425 case THREAD_BUFFER_SIZE: 426 thread_buffer_size(options); 427 break; 428 default: 429 default_size(options); 430 } 431 DEBUG_ONLY(assert_post_condition(options);) 432 return true; 433 }