1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#![allow(dead_code, unused_variables, unused_assignments)]
use std::sync::atomic::{AtomicBool, AtomicUsize};
use crate::prelude::HeapOptions;
use super::heap::Heap;
const GC_WORKERS_PER_MUTATOR: usize = 2;
const PARALLEL_WORKER_THREADS: AtomicUsize = AtomicUsize::new(0);
const PARALLEL_WORKER_THREADS_INITIALIZED: AtomicBool = AtomicBool::new(false);
pub struct WorkerPolicy;
impl WorkerPolicy {
#[allow(unused_mut)]
fn nof_parallel_worker_threads(
num: usize,
den: usize,
switch_pt: usize,
args: &HeapOptions,
) -> usize {
if args.parallel_gc_threads == 0 {
let mut threads;
let ncpus = std::thread::available_parallelism()
.map(|x| x.get())
.unwrap_or(1);
// For very large machines, there are diminishing returns
// for large numbers of worker threads. Instead of
// hogging the whole system, use a fraction of the workers for every
// processor after the first 8. For example, on a 72 cpu machine
// and a chosen fraction of 5/8
// use 8 + (72 - 8) * (5/8) == 48 worker threads.
threads = if ncpus <= switch_pt {
ncpus
} else {
switch_pt + ((ncpus - switch_pt) * num) / den
};
#[cfg(target_pointer_width = "32")]
{
// On 32-bit binaries the virtual address space available to the JVM
// is usually limited to 2-3 GB (depends on the platform).
// Do not use up address space with too many threads (stacks and per-thread
// data). Note that x86 apps running on Win64 have 2 stacks per thread.
// GC may more generally scale down threads by max heap size (etc), but the
// consequences of over-provisioning threads are higher on 32-bit JVMS,
// so add hard limit here:
threads = threads.min(2 * switch_pt);
}
threads
} else {
args.parallel_gc_threads
}
}
/// Calculates and returns the number of parallel GC threads. May
/// be CPU-architecture-specific.
fn calc_parallel_worker_threads(args: &HeapOptions) -> usize {
let den = 8; // apparently default denominator value in hotspot atm
Self::nof_parallel_worker_threads(5, den, 8, args)
}
pub fn parallel_worker_threads(args: &HeapOptions) -> usize {
if !PARALLEL_WORKER_THREADS_INITIALIZED.load(atomic::Ordering::Relaxed) {
if args.parallel_gc_threads == 0 {
let threads = Self::calc_parallel_worker_threads(args);
PARALLEL_WORKER_THREADS.store(threads, atomic::Ordering::Relaxed);
} else {
PARALLEL_WORKER_THREADS.store(args.parallel_gc_threads, atomic::Ordering::Relaxed);
}
PARALLEL_WORKER_THREADS_INITIALIZED.store(true, atomic::Ordering::Relaxed);
}
PARALLEL_WORKER_THREADS.load(atomic::Ordering::Relaxed)
}
/// If the number of GC threads was set on the command line, use it.
/// Else
/// Calculate the number of GC threads based on the number of Java threads.
/// Calculate the number of GC threads based on the size of the heap.
/// Use the larger.
pub fn calc_default_active_workers(
total_workers: usize,
min_workers: usize,
active_workers: usize,
application_workers: usize,
) -> usize {
// If the user has specifically set the number of GC threads, use them.
// If the user has turned off using a dynamic number of GC threads
// or the users has requested a specific number, set the active
// number of workers to all the workers.
let new_active_workers = total_workers;
let prev_active_workers = active_workers;
let mut active_workers_by_mt = 0;
let mut active_workers_by_heap_size = 0;
active_workers_by_mt = (GC_WORKERS_PER_MUTATOR * application_workers).max(min_workers);
let heap = super::heap::heap();
active_workers_by_heap_size = 2.max(heap.max_capacity() / heap.options().heap_size_per_gc_thread);
let max_active_workers = active_workers_by_mt * active_workers_by_heap_size;
let mut new_active_workers = max_active_workers.min(total_workers);
if new_active_workers < prev_active_workers {
new_active_workers = min_workers.min((prev_active_workers + new_active_workers) / 2);
}
new_active_workers
}
/// If the user has specifically set the number of GC threads, use them.
/// If the user has turned off using a dynamic number of GC threads
/// or the users has requested a specific number, set the active
/// number of workers to all the workers.
pub fn calc_active_workers(total_workers: usize, active_workers: usize, application_workers: usize) -> usize {
todo!()
}
}