grafos_std/cpu_shared/
partition.rs1pub fn range(total: usize, worker_index: u16, worker_count: u16) -> core::ops::Range<usize> {
15 assert!(worker_count > 0, "worker_count must be > 0");
16 assert!(worker_index < worker_count, "worker_index out of range");
17 let wc = worker_count as usize;
18 let wi = worker_index as usize;
19 let base = total / wc;
20 let rem = total % wc;
21 let start = wi * base + wi.min(rem);
22 let len = base + if wi < rem { 1 } else { 0 };
23 start..start + len
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct UnevenError {
33 pub total: usize,
34 pub worker_count: u16,
35}
36
37impl core::fmt::Display for UnevenError {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 write!(
40 f,
41 "partition::range_even: total {} is not evenly divisible by worker_count {}",
42 self.total, self.worker_count
43 )
44 }
45}
46
47pub fn range_even(
59 total: usize,
60 worker_index: u16,
61 worker_count: u16,
62) -> Result<core::ops::Range<usize>, UnevenError> {
63 assert!(worker_count > 0, "worker_count must be > 0");
64 assert!(worker_index < worker_count, "worker_index out of range");
65 if total % (worker_count as usize) != 0 {
66 return Err(UnevenError {
67 total,
68 worker_count,
69 });
70 }
71 Ok(range(total, worker_index, worker_count))
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub struct Tile {
77 pub x: u32,
78 pub y: u32,
79 pub w: u32,
80 pub h: u32,
81}
82
83pub fn tiles(
89 width: u32,
90 height: u32,
91 tile_w: u32,
92 tile_h: u32,
93 worker_index: u16,
94 worker_count: u16,
95) -> impl Iterator<Item = Tile> {
96 assert!(tile_w > 0 && tile_h > 0);
97 assert!(worker_count > 0 && worker_index < worker_count);
98 let cols = width.div_ceil(tile_w);
99 let rows = height.div_ceil(tile_h);
100 let total = (cols as usize) * (rows as usize);
101 let wi = worker_index as usize;
102 let wc = worker_count as usize;
103 (0..total).filter(move |i| i % wc == wi).map(move |i| {
104 let cx = (i as u32) % cols;
105 let cy = (i as u32) / cols;
106 let x = cx * tile_w;
107 let y = cy * tile_h;
108 let w = (tile_w).min(width - x);
109 let h = (tile_h).min(height - y);
110 Tile { x, y, w, h }
111 })
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn range_even_succeeds_when_total_divides_evenly() {
120 assert_eq!(range_even(64, 0, 4), Ok(0..16));
121 assert_eq!(range_even(64, 1, 4), Ok(16..32));
122 assert_eq!(range_even(64, 2, 4), Ok(32..48));
123 assert_eq!(range_even(64, 3, 4), Ok(48..64));
124 }
125
126 #[test]
127 fn range_even_returns_error_when_total_does_not_divide_evenly() {
128 assert_eq!(
130 range_even(64, 0, 3),
131 Err(UnevenError {
132 total: 64,
133 worker_count: 3,
134 })
135 );
136 assert_eq!(
137 range_even(10, 2, 4),
138 Err(UnevenError {
139 total: 10,
140 worker_count: 4,
141 })
142 );
143 }
144
145 #[test]
146 fn range_even_handles_corner_cases() {
147 assert_eq!(range_even(0, 0, 1), Ok(0..0));
149 assert_eq!(range_even(0, 1, 4), Ok(0..0));
150 assert_eq!(range_even(1, 0, 1), Ok(0..1));
152 assert_eq!(
154 range_even(1, 0, 2),
155 Err(UnevenError {
156 total: 1,
157 worker_count: 2,
158 })
159 );
160 }
161}