Parallelize DFS, use an AtomicBool for short-circuiting
time: [73.407 µs 76.145 µs 79.345 µs] change: [−40.126% −24.679% −3.8062%] (p = 0.04 < 0.05)
This commit is contained in:
3
justfile
3
justfile
@@ -22,6 +22,9 @@ test-integration:
|
|||||||
test-all:
|
test-all:
|
||||||
cd {{tauri_path}} && cargo test --release -- --nocapture
|
cd {{tauri_path}} && cargo test --release -- --nocapture
|
||||||
|
|
||||||
|
bench:
|
||||||
|
cd {{tauri_path}} && cargo bench
|
||||||
|
|
||||||
# profile:
|
# profile:
|
||||||
# cd {{tauri_path}} && cargo flamegraph
|
# cd {{tauri_path}} && cargo flamegraph
|
||||||
|
|
||||||
|
|||||||
237
src-tauri/Cargo.lock
generated
237
src-tauri/Cargo.lock
generated
@@ -43,6 +43,15 @@ dependencies = [
|
|||||||
"alloc-no-stdlib",
|
"alloc-no-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloca"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_log-sys"
|
name = "android_log-sys"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -69,6 +78,18 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anes"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.100"
|
version = "1.0.100"
|
||||||
@@ -489,6 +510,12 @@ dependencies = [
|
|||||||
"toml 0.9.8",
|
"toml 0.9.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cast"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.49"
|
version = "1.2.49"
|
||||||
@@ -552,6 +579,58 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"ciborium-ll",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-io"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ciborium-ll"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||||
|
dependencies = [
|
||||||
|
"ciborium-io",
|
||||||
|
"half",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color_quant"
|
name = "color_quant"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -651,6 +730,41 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf"
|
||||||
|
dependencies = [
|
||||||
|
"alloca",
|
||||||
|
"anes",
|
||||||
|
"cast",
|
||||||
|
"ciborium",
|
||||||
|
"clap",
|
||||||
|
"criterion-plot",
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"num-traits",
|
||||||
|
"oorandom",
|
||||||
|
"page_size",
|
||||||
|
"plotters",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tinytemplate",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion-plot"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4"
|
||||||
|
dependencies = [
|
||||||
|
"cast",
|
||||||
|
"itertools 0.13.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.15"
|
version = "0.5.15"
|
||||||
@@ -660,12 +774,37 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -1523,6 +1662,17 @@ dependencies = [
|
|||||||
"syn 2.0.111",
|
"syn 2.0.111",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crunchy",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -1883,6 +2033,15 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -2509,6 +2668,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oorandom"
|
||||||
|
version = "11.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "5.3.3"
|
version = "5.3.3"
|
||||||
@@ -2537,6 +2702,16 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
@@ -2779,6 +2954,34 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-svg",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.16"
|
version = "0.17.16"
|
||||||
@@ -3070,6 +3273,26 @@ version = "0.6.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.18"
|
version = "0.5.18"
|
||||||
@@ -3224,10 +3447,12 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"criterion",
|
||||||
"docx-rs",
|
"docx-rs",
|
||||||
"itertools",
|
"itertools 0.14.0",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
|
"rayon",
|
||||||
"rstest",
|
"rstest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -4272,6 +4497,16 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinytemplate"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
|||||||
@@ -30,3 +30,19 @@ log = "0.4.29"
|
|||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
docx-rs = "0.4.18"
|
docx-rs = "0.4.18"
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
|
rayon = "1.11"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = { version = "0.8.1", features = ["html_reports"] }
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "rayon"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
indexing_slicing = "deny"
|
||||||
|
fallible_impl_from = "deny"
|
||||||
|
wildcard_enum_match_arm = "deny"
|
||||||
|
unneeded_field_pattern = "deny"
|
||||||
|
fn_params_excessive_bools = "deny"
|
||||||
|
# must_use_candidate = "deny"
|
||||||
|
|||||||
53
src-tauri/benches/rayon.rs
Normal file
53
src-tauri/benches/rayon.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use rota_lib::{
|
||||||
|
config::{ToxicPair, UserConfig},
|
||||||
|
resident::Resident,
|
||||||
|
schedule::{MonthlySchedule, ShiftType},
|
||||||
|
scheduler::Scheduler,
|
||||||
|
slot::Day,
|
||||||
|
workload::WorkloadTracker,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let config = maximal_config();
|
||||||
|
let scheduler = Scheduler::new_with_config(config);
|
||||||
|
|
||||||
|
c.bench_function("scheduler run", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut schedule = MonthlySchedule::new();
|
||||||
|
let mut tracker = WorkloadTracker::default();
|
||||||
|
let result = scheduler.run(&mut schedule, &mut tracker);
|
||||||
|
black_box(result)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maximal_config() -> UserConfig {
|
||||||
|
UserConfig::default()
|
||||||
|
.with_holidays(vec![2, 3, 10, 11, 12, 25])
|
||||||
|
.with_residents(vec![
|
||||||
|
Resident::new(1, "R1").with_max_shifts(3),
|
||||||
|
Resident::new(2, "R2").with_max_shifts(4),
|
||||||
|
Resident::new(3, "R3").with_reduced_load(),
|
||||||
|
Resident::new(4, "R4").with_allowed_types(vec![ShiftType::Closed]),
|
||||||
|
Resident::new(5, "R5")
|
||||||
|
.with_allowed_types(vec![ShiftType::OpenFirst, ShiftType::OpenSecond]),
|
||||||
|
Resident::new(6, "R6").with_negative_shifts(vec![Day(5), Day(15), Day(25)]),
|
||||||
|
Resident::new(7, "R7")
|
||||||
|
.with_allowed_types(vec![ShiftType::OpenFirst, ShiftType::OpenSecond]),
|
||||||
|
Resident::new(8, "R8"),
|
||||||
|
Resident::new(9, "R9"),
|
||||||
|
Resident::new(10, "R10").with_reduced_load(),
|
||||||
|
])
|
||||||
|
.with_toxic_pairs(vec![
|
||||||
|
ToxicPair::new(1, 2),
|
||||||
|
ToxicPair::new(3, 4),
|
||||||
|
ToxicPair::new(3, 5),
|
||||||
|
ToxicPair::new(7, 8),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::UserConfig,
|
config::UserConfig,
|
||||||
resident::ResidentId,
|
resident::ResidentId,
|
||||||
@@ -7,7 +9,10 @@ use crate::{
|
|||||||
workload::{WorkloadBounds, WorkloadTracker},
|
workload::{WorkloadBounds, WorkloadTracker},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use log::warn;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
pub config: UserConfig,
|
pub config: UserConfig,
|
||||||
@@ -46,7 +51,46 @@ impl Scheduler {
|
|||||||
|
|
||||||
//TODO: add validation
|
//TODO: add validation
|
||||||
|
|
||||||
self.search(schedule, tracker, Slot::default())
|
// find first non-manually-filled slot
|
||||||
|
let slot = (0..=self.config.total_slots)
|
||||||
|
.find(|&slot_idx| !schedule.0.contains_key(&Slot::from(slot_idx)))
|
||||||
|
.map(Slot::from)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Schedule is already full"))?;
|
||||||
|
|
||||||
|
let resident_ids = self.valid_residents(slot, schedule);
|
||||||
|
let solved_in_thread = AtomicBool::new(false);
|
||||||
|
|
||||||
|
let solved_schedule = resident_ids.par_iter().find_map_any(|&id| {
|
||||||
|
let mut local_schedule = schedule.clone();
|
||||||
|
let mut local_tracker = tracker.clone();
|
||||||
|
|
||||||
|
local_schedule.insert(slot, id);
|
||||||
|
local_tracker.insert(id, &self.config, slot);
|
||||||
|
|
||||||
|
let solved = self.search(
|
||||||
|
&mut local_schedule,
|
||||||
|
&mut local_tracker,
|
||||||
|
slot.next(),
|
||||||
|
&solved_in_thread,
|
||||||
|
);
|
||||||
|
match solved {
|
||||||
|
Ok(true) => Some(local_schedule),
|
||||||
|
Ok(false) => None,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Search error: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: can return the schedule instead of a bool
|
||||||
|
|
||||||
|
if let Some(solved_schedule) = solved_schedule {
|
||||||
|
*schedule = solved_schedule;
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DFS where maximum depth is calculated by total_days_of_month + odd_days_of_month each node is called a slot
|
/// DFS where maximum depth is calculated by total_days_of_month + odd_days_of_month each node is called a slot
|
||||||
@@ -57,7 +101,12 @@ impl Scheduler {
|
|||||||
schedule: &mut MonthlySchedule,
|
schedule: &mut MonthlySchedule,
|
||||||
tracker: &mut WorkloadTracker,
|
tracker: &mut WorkloadTracker,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
|
solved_in_thread: &AtomicBool,
|
||||||
) -> anyhow::Result<bool> {
|
) -> anyhow::Result<bool> {
|
||||||
|
if solved_in_thread.load(Ordering::Relaxed) {
|
||||||
|
bail!("Another thread found the solution")
|
||||||
|
}
|
||||||
|
|
||||||
if self.timer.limit_exceeded() {
|
if self.timer.limit_exceeded() {
|
||||||
anyhow::bail!("Time exceeded. Restrictions too tight");
|
anyhow::bail!("Time exceeded. Restrictions too tight");
|
||||||
}
|
}
|
||||||
@@ -69,11 +118,15 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if slot.greater_than(self.config.total_days) {
|
if slot.greater_than(self.config.total_days) {
|
||||||
return Ok(tracker.are_all_thresholds_met(&self.config, &self.bounds));
|
if tracker.are_all_thresholds_met(&self.config, &self.bounds) {
|
||||||
|
solved_in_thread.store(true, Ordering::Relaxed);
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if schedule.is_slot_manually_assigned(&slot) {
|
if schedule.is_slot_manually_assigned(&slot) {
|
||||||
return self.search(schedule, tracker, slot.next());
|
return self.search(schedule, tracker, slot.next(), solved_in_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort candidates by current workload, add rng for tie breakers
|
// sort candidates by current workload, add rng for tie breakers
|
||||||
@@ -88,7 +141,7 @@ impl Scheduler {
|
|||||||
schedule.insert(slot, id);
|
schedule.insert(slot, id);
|
||||||
tracker.insert(id, &self.config, slot);
|
tracker.insert(id, &self.config, slot);
|
||||||
|
|
||||||
if self.search(schedule, tracker, slot.next())? {
|
if self.search(schedule, tracker, slot.next(), solved_in_thread)? {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,16 @@ impl Default for Slot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u8> for Slot {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
let mut slot = Slot::default();
|
||||||
|
for _ in 0..value {
|
||||||
|
slot = slot.next()
|
||||||
|
}
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
|
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Day(pub u8);
|
pub struct Day(pub u8);
|
||||||
|
|||||||
Reference in New Issue
Block a user