1use std::time::Instant;
2
3use rayon::prelude::*;
4
5use honeycomb::{
6 core::cmap::LinkError,
7 core::stm::{retry, StmError, Transaction, TransactionControl, TransactionResult},
8 prelude::{CMap2, CMapBuilder, CoordsFloat, DartIdType, EdgeIdType, Vertex2},
9};
10
11use crate::{cli::CutEdgesArgs, utils::hash_file};
12
13pub fn bench_cut_edges<T: CoordsFloat>(args: CutEdgesArgs) -> CMap2<T> {
16 let input_map = args.input.to_str().unwrap();
17 let target_len = T::from(args.target_length).unwrap();
18
19 let n_threads = std::thread::available_parallelism()
20 .map(|n| n.get())
21 .unwrap_or(1);
22
23 let mut instant = Instant::now();
25 let input_hash = hash_file(input_map).expect("E: could not compute input hash"); let mut map: CMap2<T> = CMapBuilder::from(input_map).build().unwrap();
27 #[cfg(debug_assertions)] {
29 use honeycomb::prelude::{Orbit2, OrbitPolicy};
30 assert!(
31 map.iter_faces()
32 .all(|f| { Orbit2::new(&map, OrbitPolicy::Face, f as DartIdType).count() == 3 }),
33 "Input mesh isn't a triangle mesh"
34 );
35 }
36 println!("| cut-edges benchmark");
37 println!("|-> input : {input_map} (hash: {input_hash:#0x})",);
38 println!(
39 "|-> backend : {:?} with {n_threads} thread(s)",
40 args.backend
41 );
42 println!("|-> target size: {target_len:?}");
43 println!("|-> init time : {}ms", instant.elapsed().as_millis());
44
45 println!(" Step | n_edge_total | n_edge_to_process | t_compute_batch(s) | t_process_batch(s) | n_transac_retry");
46
47 let mut step = 0;
48 print!(" {step:>4} "); instant = Instant::now();
52 let mut edges: Vec<EdgeIdType> = map.iter_edges().collect();
53 print!("| {:>12} ", edges.len()); edges.retain(|&e| {
55 let (vid1, vid2) = (
56 map.vertex_id(e as DartIdType),
57 map.vertex_id(map.beta::<1>(e as DartIdType)),
58 );
59 match (map.force_read_vertex(vid1), map.force_read_vertex(vid2)) {
60 (Some(v1), Some(v2)) => (v2 - v1).norm() > target_len,
61 (_, _) => false,
62 }
63 });
64 let n_e = edges.len();
65 print!("| {n_e:>17} "); let mut nd = map.add_free_darts(6 * n_e); let mut darts: Vec<DartIdType> = (nd..nd + 6 * n_e as DartIdType).collect();
68 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); while !edges.is_empty() {
72 instant = Instant::now();
74 let n_retry = match args.backend {
75 crate::cli::Backend::RayonIter => dispatch_rayon(&map, &mut edges, &darts),
76 crate::cli::Backend::RayonChunks => {
77 dispatch_rayon_chunks(&map, &mut edges, &darts, n_threads)
78 }
79 crate::cli::Backend::StdThreads => {
80 dispatch_std_threads(&map, &mut edges, &darts, n_threads)
81 }
82 };
83 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); println!("| {n_retry:>15}",); (1..map.n_darts() as DartIdType).for_each(|d| {
87 if map.is_free(d) && !map.is_unused(d) {
88 map.remove_free_dart(d);
89 }
90 });
91
92 step += 1;
94 print!(" {step:>4} "); instant = Instant::now();
96 edges.extend(map.iter_edges());
97 print!("| {:>12} ", edges.len()); edges.retain(|&e| {
99 let (vid1, vid2) = (
100 map.vertex_id(e as DartIdType),
101 map.vertex_id(map.beta::<1>(e as DartIdType)),
102 );
103 match (map.force_read_vertex(vid1), map.force_read_vertex(vid2)) {
104 (Some(v1), Some(v2)) => (v2 - v1).norm() > target_len,
105 (_, _) => false,
106 }
107 });
108 let n_e = edges.len();
109 print!("| {n_e:>17} "); nd = map.add_free_darts(6 * n_e);
111 darts.par_drain(..); darts.extend(nd..nd + 6 * n_e as DartIdType);
113 if n_e != 0 {
114 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); } else {
116 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); println!("| {:>18.6e} ", 0.0); }
119 }
120
121 map
122}
123
124#[inline]
125fn dispatch_rayon<T: CoordsFloat>(
126 map: &CMap2<T>,
127 edges: &mut Vec<EdgeIdType>,
128 darts: &[DartIdType],
129) -> u32 {
130 let units: Vec<(u32, [u32; 6])> = edges
131 .drain(..)
132 .zip(darts.chunks(6))
133 .map(|(e, sl)| (e, sl.try_into().unwrap()))
134 .collect();
135 units
136 .into_par_iter()
137 .map(|(e, new_darts)| {
138 let mut n_retry = 0;
139 if map.is_i_free::<2>(e as DartIdType) {
140 if !process_outer_edge(map, &mut n_retry, e, new_darts).is_validated() {
141 unreachable!()
142 }
143 } else if !process_inner_edge(map, &mut n_retry, e, new_darts).is_validated() {
144 unreachable!()
145 }
146 n_retry as u32
147 }) .sum()
149}
150
151#[inline]
152fn dispatch_rayon_chunks<T: CoordsFloat>(
153 map: &CMap2<T>,
154 edges: &mut Vec<EdgeIdType>,
155 darts: &[DartIdType],
156 n_threads: usize,
157) -> u32 {
158 let units: Vec<(u32, [u32; 6])> = edges
159 .drain(..)
160 .zip(darts.chunks(6))
161 .map(|(e, sl)| (e, sl.try_into().unwrap()))
162 .collect();
163 units
164 .par_chunks(1 + units.len() / n_threads)
165 .map(|c| {
166 let mut n = 0;
167 c.iter().for_each(|&(e, new_darts)| {
168 let mut n_retry = 0;
169 if map.is_i_free::<2>(e as DartIdType) {
170 if !process_outer_edge(map, &mut n_retry, e, new_darts).is_validated() {
171 unreachable!()
172 }
173 } else if !process_inner_edge(map, &mut n_retry, e, new_darts).is_validated() {
174 unreachable!()
175 }
176 n += n_retry as u32;
177 });
178 n
179 }) .sum()
181}
182
183#[inline]
184fn dispatch_std_threads<T: CoordsFloat>(
185 map: &CMap2<T>,
186 edges: &mut Vec<EdgeIdType>,
187 darts: &[DartIdType],
188 n_threads: usize,
189) -> u32 {
190 let units: Vec<(u32, [u32; 6])> = edges
191 .drain(..)
192 .zip(darts.chunks(6))
193 .map(|(e, sl)| (e, sl.try_into().unwrap()))
194 .collect();
195 std::thread::scope(|s| {
196 let mut handles = Vec::new();
197 for wl in units.chunks(1 + units.len() / n_threads) {
198 handles.push(s.spawn(|| {
199 let mut n = 0;
200 wl.iter().for_each(|&(e, new_darts)| {
201 let mut n_retry = 0;
202 if map.is_i_free::<2>(e as DartIdType) {
203 if !process_outer_edge(map, &mut n_retry, e, new_darts).is_validated() {
204 unreachable!()
205 }
206 } else if !process_inner_edge(map, &mut n_retry, e, new_darts).is_validated() {
207 unreachable!()
208 }
209 n += n_retry as u32;
210 });
211 n
212 })); } handles.into_iter().map(|h| h.join().unwrap()).sum()
215 }) }
217
218#[inline]
219fn process_outer_edge<T: CoordsFloat>(
220 map: &CMap2<T>,
221 n_retry: &mut u8,
222 e: EdgeIdType,
223 [nd1, nd2, nd3, _, _, _]: [DartIdType; 6],
224) -> TransactionResult<(), LinkError> {
225 Transaction::with_control_and_err(
226 |_| {
227 *n_retry += 1;
228 TransactionControl::Retry
229 },
230 |trans| {
231 map.link::<2>(trans, nd1, nd2)?;
233 map.link::<1>(trans, nd2, nd3)?;
234
235 let ld = e as DartIdType;
236 let (b0ld, b1ld) = (
237 map.beta_transac::<0>(trans, ld)?,
238 map.beta_transac::<1>(trans, ld)?,
239 );
240
241 let (vid1, vid2) = (
242 map.vertex_id_transac(trans, ld)?,
243 map.vertex_id_transac(trans, b1ld)?,
244 );
245 let new_v = match (map.read_vertex(trans, vid1)?, map.read_vertex(trans, vid2)?) {
246 (Some(v1), Some(v2)) => Vertex2::average(&v1, &v2),
247 _ => retry()?,
248 };
249 map.write_vertex(trans, nd1, new_v)?;
250
251 map.unsew::<1>(trans, ld).map_err(|_| StmError::Failure)?;
252 map.unsew::<1>(trans, b1ld).map_err(|_| StmError::Failure)?;
253
254 map.sew::<1>(trans, ld, nd1)
255 .map_err(|_| StmError::Failure)?;
256 map.sew::<1>(trans, nd1, b0ld)
257 .map_err(|_| StmError::Failure)?;
258 map.sew::<1>(trans, nd3, b1ld)
259 .map_err(|_| StmError::Failure)?;
260 map.sew::<1>(trans, b1ld, nd2)
261 .map_err(|_| StmError::Failure)?;
262
263 Ok(())
264 },
265 ) }
267
268#[inline]
269fn process_inner_edge<T: CoordsFloat>(
270 map: &CMap2<T>,
271 n_retry: &mut u8,
272 e: EdgeIdType,
273 [nd1, nd2, nd3, nd4, nd5, nd6]: [DartIdType; 6],
274) -> TransactionResult<(), LinkError> {
275 Transaction::with_control_and_err(
276 |_| {
277 *n_retry += 1;
278 TransactionControl::Retry
279 },
280 |trans| {
281 map.link::<2>(trans, nd1, nd2)?;
283 map.link::<1>(trans, nd2, nd3)?;
284 map.link::<2>(trans, nd4, nd5)?;
285 map.link::<1>(trans, nd5, nd6)?;
286
287 let (ld, rd) = (
288 e as DartIdType,
289 map.beta_transac::<2>(trans, e as DartIdType)?,
290 );
291 let (b0ld, b1ld) = (
292 map.beta_transac::<0>(trans, ld)?,
293 map.beta_transac::<1>(trans, ld)?,
294 );
295 let (b0rd, b1rd) = (
296 map.beta_transac::<0>(trans, rd)?,
297 map.beta_transac::<1>(trans, rd)?,
298 );
299
300 let (vid1, vid2) = (
301 map.vertex_id_transac(trans, ld)?,
302 map.vertex_id_transac(trans, b1ld)?,
303 );
304 let new_v = match (map.read_vertex(trans, vid1)?, map.read_vertex(trans, vid2)?) {
305 (Some(v1), Some(v2)) => Vertex2::average(&v1, &v2),
306 _ => retry()?,
307 };
308 map.write_vertex(trans, nd1, new_v)?;
309
310 map.unsew::<2>(trans, ld).map_err(|_| StmError::Failure)?;
311 map.unsew::<1>(trans, ld).map_err(|_| StmError::Failure)?;
312 map.unsew::<1>(trans, b1ld).map_err(|_| StmError::Failure)?;
313 map.unsew::<1>(trans, rd).map_err(|_| StmError::Failure)?;
314 map.unsew::<1>(trans, b1rd).map_err(|_| StmError::Failure)?;
315
316 map.sew::<2>(trans, ld, nd6)
317 .map_err(|_| StmError::Failure)?;
318 map.sew::<2>(trans, rd, nd3)
319 .map_err(|_| StmError::Failure)?;
320
321 map.sew::<1>(trans, ld, nd1)
322 .map_err(|_| StmError::Failure)?;
323 map.sew::<1>(trans, nd1, b0ld)
324 .map_err(|_| StmError::Failure)?;
325 map.sew::<1>(trans, nd3, b1ld)
326 .map_err(|_| StmError::Failure)?;
327 map.sew::<1>(trans, b1ld, nd2)
328 .map_err(|_| StmError::Failure)?;
329
330 map.sew::<1>(trans, rd, nd4)
331 .map_err(|_| StmError::Failure)?;
332 map.sew::<1>(trans, nd4, b0rd)
333 .map_err(|_| StmError::Failure)?;
334 map.sew::<1>(trans, nd6, b1rd)
335 .map_err(|_| StmError::Failure)?;
336 map.sew::<1>(trans, b1rd, nd5)
337 .map_err(|_| StmError::Failure)?;
338
339 Ok(())
340 },
341 ) }