#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SchedulingStrategy {
Static,
Dynamic,
Guided,
Adaptive,
WorkStealing,
}
pub fn optimize_scheduling(
array_size: usize,
element_cost: f64,
strategy: SchedulingStrategy,
num_threads: usize,
) -> usize {
match strategy {
SchedulingStrategy::Static => static_scheduling(array_size, element_cost, num_threads),
SchedulingStrategy::Dynamic => dynamic_scheduling(array_size, element_cost, num_threads),
SchedulingStrategy::Guided => guided_scheduling(array_size, element_cost, num_threads),
SchedulingStrategy::WorkStealing => {
work_stealing_scheduler(array_size, element_cost, num_threads)
}
SchedulingStrategy::Adaptive => {
if array_size < 10_000 || element_cost < 0.5 {
static_scheduling(array_size, element_cost, num_threads)
} else if element_cost > 5.0 || array_size > 1_000_000 {
work_stealing_scheduler(array_size, element_cost, num_threads)
} else if element_cost_varies(element_cost) {
dynamic_scheduling(array_size, element_cost, num_threads)
} else {
guided_scheduling(array_size, element_cost, num_threads)
}
}
}
}
fn static_scheduling(array_size: usize, element_cost: f64, num_threads: usize) -> usize {
let min_elements_per_thread = 1000;
let optimal_threads = (array_size / min_elements_per_thread).max(1);
let cost_factor = (element_cost / 2.0).clamp(0.5, 2.0);
let scaled_threads = (optimal_threads as f64 * cost_factor) as usize;
scaled_threads.min(num_threads)
}
fn dynamic_scheduling(array_size: usize, element_cost: f64, num_threads: usize) -> usize {
let min_elements_per_thread = 500;
let optimal_threads = (array_size / min_elements_per_thread).max(1);
let cost_factor = (element_cost / 1.5).clamp(0.75, 3.0);
let scaled_threads = (optimal_threads as f64 * cost_factor) as usize;
scaled_threads.min(num_threads)
}
fn guided_scheduling(array_size: usize, element_cost: f64, num_threads: usize) -> usize {
let min_elements_per_thread = 750;
let optimal_threads = (array_size / min_elements_per_thread).max(1);
let cost_factor = (element_cost / 1.75).clamp(0.6, 2.5);
let scaled_threads = (optimal_threads as f64 * cost_factor) as usize;
scaled_threads.min(num_threads)
}
pub fn work_stealing_scheduler(array_size: usize, element_cost: f64, num_threads: usize) -> usize {
if array_size > 100_000 && element_cost > 1.0 {
return num_threads;
}
let min_elements_per_thread = 250;
let optimal_threads = (array_size / min_elements_per_thread).max(1);
let cost_factor = element_cost.clamp(0.25, 4.0);
let scaled_threads = (optimal_threads as f64 * cost_factor) as usize;
scaled_threads.min(num_threads)
}
fn element_cost_varies(element_cost: f64) -> bool {
element_cost > 1.0 && element_cost < 5.0
}
pub fn calculate_chunk_size(array_size: usize, strategy: SchedulingStrategy) -> usize {
match strategy {
SchedulingStrategy::Static => {
let num_threads = scirs2_core::parallel_ops::num_threads();
(array_size / num_threads).max(1)
}
SchedulingStrategy::Dynamic => {
(array_size / 100).clamp(64, 1024)
}
SchedulingStrategy::Guided => {
(array_size / 10).clamp(128, 4096)
}
SchedulingStrategy::WorkStealing => {
(array_size / 50).clamp(128, 2048)
}
SchedulingStrategy::Adaptive => {
if array_size < 10_000 {
(array_size / 8).max(64)
} else if array_size > 1_000_000 {
1024
} else {
512
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_work_stealing_scheduler() {
let small_threads = work_stealing_scheduler(1000, 0.5, 12);
assert!(small_threads < 12);
let large_threads = work_stealing_scheduler(1_000_000, 2.0, 12);
assert_eq!(large_threads, 12);
let medium_low_cost = work_stealing_scheduler(50_000, 0.5, 12);
let medium_high_cost = work_stealing_scheduler(50_000, 2.0, 12);
assert!(medium_high_cost >= medium_low_cost);
}
#[test]
fn test_optimize_scheduling() {
let num_threads = 12;
let static_threads =
optimize_scheduling(100_000, 1.0, SchedulingStrategy::Static, num_threads);
let dynamic_threads =
optimize_scheduling(100_000, 1.0, SchedulingStrategy::Dynamic, num_threads);
let guided_threads =
optimize_scheduling(100_000, 1.0, SchedulingStrategy::Guided, num_threads);
assert!(dynamic_threads >= static_threads);
assert!(guided_threads >= static_threads);
}
#[test]
fn test_chunk_size() {
let static_size = calculate_chunk_size(10_000, SchedulingStrategy::Static);
let dynamic_size = calculate_chunk_size(10_000, SchedulingStrategy::Dynamic);
assert!(static_size > dynamic_size);
let small_static = calculate_chunk_size(100, SchedulingStrategy::Static);
assert!(small_static >= 1);
let large_dynamic = calculate_chunk_size(10_000_000, SchedulingStrategy::Dynamic);
assert!(large_dynamic <= 1024);
}
}