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