1use std::time::Instant;
8
9use rayon::prelude::*;
10
11use honeycomb::{
12 core::{
13 cmap::SewError,
14 stm::{Transaction, TransactionControl, TransactionResult},
15 },
16 kernels::remeshing::{cut_inner_edge, cut_outer_edge},
17 prelude::{CMap2, CMapBuilder, CoordsFloat, DartIdType, EdgeIdType},
18};
19
20use crate::{
21 cli::CutEdgesArgs,
22 prof_start, prof_stop,
23 utils::{get_num_threads, hash_file},
24};
25
26pub fn bench_cut_edges<T: CoordsFloat>(args: CutEdgesArgs) -> CMap2<T> {
29 let input_map = args.input.to_str().unwrap();
30 let target_len = T::from(args.target_length).unwrap();
31
32 let n_threads = if let Ok(val) = get_num_threads() {
33 val
34 } else {
35 std::thread::available_parallelism()
36 .map(|n| n.get())
37 .unwrap_or(1)
38 };
39
40 let mut instant = Instant::now();
42 let input_hash = hash_file(input_map).expect("E: could not compute input hash"); let mut map: CMap2<T> = if input_map.ends_with(".cmap") {
45 CMapBuilder::<2, _>::from_cmap_file(input_map)
46 .build()
47 .unwrap()
48 } else if input_map.ends_with(".vtk") {
49 CMapBuilder::<2, _>::from_vtk_file(input_map)
50 .build()
51 .unwrap()
52 } else {
53 panic!(
54 "E: Unknown file format; only .cmap or .vtk files are supported for map initialization"
55 );
56 };
57 #[cfg(debug_assertions)] {
59 use honeycomb::prelude::OrbitPolicy;
60 assert!(
61 map.iter_faces()
62 .all(|f| { map.orbit(OrbitPolicy::Face, f as DartIdType).count() == 3 }),
63 "Input mesh isn't a triangle mesh"
64 );
65 }
66 println!("| cut-edges benchmark");
67 println!("|-> input : {input_map} (hash: {input_hash:#0x})",);
68 println!(
69 "|-> backend : {:?} with {n_threads} thread(s)",
70 args.backend
71 );
72 println!("|-> target size: {target_len:?}");
73 println!("|-> init time : {}ms", instant.elapsed().as_millis());
74
75 println!(
76 " Step | n_edge_total | n_edge_to_process | t_compute_batch(s) | t_process_batch(s) | n_transac_retry"
77 );
78
79 let mut step = 0;
80 print!(" {step:>4} "); prof_start!("HCBENCH_CUTS");
82
83 prof_start!("HCBENCH_CUTS_COMPUTE");
85 instant = Instant::now();
86 let mut edges: Vec<EdgeIdType> = map.iter_edges().collect();
87 print!("| {:>12} ", edges.len()); edges.retain(|&e| {
89 let (vid1, vid2) = (
90 map.vertex_id(e as DartIdType),
91 map.vertex_id(map.beta::<1>(e as DartIdType)),
92 );
93 match (map.force_read_vertex(vid1), map.force_read_vertex(vid2)) {
94 (Some(v1), Some(v2)) => (v2 - v1).norm() > target_len,
95 (_, _) => false,
96 }
97 });
98 let n_e = edges.len();
99 print!("| {n_e:>17} "); let mut nd = map.allocate_used_darts(6 * n_e); let mut darts: Vec<DartIdType> = (nd..nd + 6 * n_e as DartIdType).collect();
102 prof_stop!("HCBENCH_CUTS_COMPUTE");
103 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); while !edges.is_empty() {
107 prof_start!("HCBENCH_CUTS_PROCESS");
109 instant = Instant::now();
110 let n_retry = match args.backend {
111 crate::cli::Backend::RayonIter => dispatch_rayon(&map, &mut edges, &darts),
112 crate::cli::Backend::RayonChunks => {
113 dispatch_rayon_chunks(&map, &mut edges, &darts, n_threads)
114 }
115 crate::cli::Backend::StdThreads => {
116 dispatch_std_threads(&map, &mut edges, &darts, n_threads)
117 }
118 };
119 prof_stop!("HCBENCH_CUTS_PROCESS");
120 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); println!("| {n_retry:>15}",); (1..map.n_darts() as DartIdType).for_each(|d| {
124 if map.is_free(d) && !map.is_unused(d) {
125 map.release_dart(d).expect("E: unreachable");
126 }
127 });
128
129 step += 1;
131 print!(" {step:>4} "); prof_start!("HCBENCH_CUTS_COMPUTE");
133 instant = Instant::now();
134 edges.extend(map.iter_edges());
135 print!("| {:>12} ", edges.len()); edges.retain(|&e| {
137 let (vid1, vid2) = (
138 map.vertex_id(e as DartIdType),
139 map.vertex_id(map.beta::<1>(e as DartIdType)),
140 );
141 match (map.force_read_vertex(vid1), map.force_read_vertex(vid2)) {
142 (Some(v1), Some(v2)) => (v2 - v1).norm() > target_len,
143 (_, _) => false,
144 }
145 });
146 let n_e = edges.len();
147 print!("| {n_e:>17} "); nd = map.allocate_used_darts(6 * n_e);
149 darts.par_drain(..); darts.extend(nd..nd + 6 * n_e as DartIdType);
151 prof_stop!("HCBENCH_CUTS_COMPUTE");
152 if n_e != 0 {
153 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); } else {
155 print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); print!("| {:>18.6e} ", 0.0); println!("| {:>15}", 0); }
159 }
160 prof_stop!("HCBENCH_CUTS");
161
162 map
163}
164
165#[inline]
166fn dispatch_rayon<T: CoordsFloat>(
167 map: &CMap2<T>,
168 edges: &mut Vec<EdgeIdType>,
169 darts: &[DartIdType],
170) -> u32 {
171 let units: Vec<(u32, [u32; 6])> = edges
172 .drain(..)
173 .zip(darts.chunks(6))
174 .map(|(e, sl)| (e, sl.try_into().unwrap()))
175 .collect();
176 units
177 .into_par_iter()
178 .map(|(e, new_darts)| {
179 let mut n_retry = 0;
180 if map.is_i_free::<2>(e as DartIdType) {
181 while !process_outer_edge(map, &mut n_retry, e, new_darts).is_validated() {}
182 } else {
183 while !process_inner_edge(map, &mut n_retry, e, new_darts).is_validated() {}
184 }
185 n_retry as u32
186 }) .sum()
188}
189
190#[inline]
191fn dispatch_rayon_chunks<T: CoordsFloat>(
192 map: &CMap2<T>,
193 edges: &mut Vec<EdgeIdType>,
194 darts: &[DartIdType],
195 n_threads: usize,
196) -> u32 {
197 let units: Vec<(u32, [u32; 6])> = edges
198 .drain(..)
199 .zip(darts.chunks(6))
200 .map(|(e, sl)| (e, sl.try_into().unwrap()))
201 .collect();
202 units
203 .par_chunks(1 + units.len() / n_threads)
204 .map(|c| {
205 let mut n = 0;
206 c.iter().for_each(|&(e, new_darts)| {
207 let mut n_retry = 0;
208 if map.is_i_free::<2>(e as DartIdType) {
209 while !process_outer_edge(map, &mut n_retry, e, new_darts).is_validated() {}
210 } else {
211 while !process_inner_edge(map, &mut n_retry, e, new_darts).is_validated() {}
212 }
213 n += n_retry as u32;
214 });
215 n
216 }) .sum()
218}
219
220#[inline]
221fn dispatch_std_threads<T: CoordsFloat>(
222 map: &CMap2<T>,
223 edges: &mut Vec<EdgeIdType>,
224 darts: &[DartIdType],
225 n_threads: usize,
226) -> u32 {
227 let units: Vec<(u32, [u32; 6])> = edges
228 .drain(..)
229 .zip(darts.chunks(6))
230 .map(|(e, sl)| (e, sl.try_into().unwrap()))
231 .collect();
232
233 #[cfg(feature = "bind-threads")]
234 {
235 use std::sync::Arc;
236
237 use hwlocality::{Topology, cpu::binding::CpuBindingFlags};
238
239 use crate::utils::get_proc_list;
240
241 let topo = Arc::new(Topology::new().unwrap());
242 let mut cores = get_proc_list(&topo).unwrap_or_default().into_iter().cycle();
243 std::thread::scope(|s| {
244 let mut handles = Vec::new();
245 for wl in units.chunks(1 + units.len() / n_threads) {
246 let topo = topo.clone();
247 let core = cores.next();
248 handles.push(s.spawn(move || {
249 if let Some(c) = core {
251 let tid = hwlocality::current_thread_id();
252 topo.bind_thread_cpu(tid, &c, CpuBindingFlags::empty())
253 .unwrap();
254 }
255 let mut n = 0;
257 wl.iter().for_each(|&(e, new_darts)| {
258 let mut n_retry = 0;
259 if map.is_i_free::<2>(e as DartIdType) {
260 while !process_outer_edge(map, &mut n_retry, e, new_darts)
261 .is_validated()
262 {}
263 } else {
264 while !process_inner_edge(map, &mut n_retry, e, new_darts)
265 .is_validated()
266 {}
267 }
268 n += n_retry as u32;
269 });
270 n
271 })); } handles.into_iter().map(|h| h.join().unwrap()).sum()
274 }) }
276
277 #[cfg(not(feature = "bind-threads"))]
278 {
279 std::thread::scope(|s| {
280 let mut handles = Vec::new();
281 for wl in units.chunks(1 + units.len() / n_threads) {
282 handles.push(s.spawn(|| {
283 let mut n = 0;
284 wl.iter().for_each(|&(e, new_darts)| {
285 let mut n_retry = 0;
286 if map.is_i_free::<2>(e as DartIdType) {
287 while !process_outer_edge(map, &mut n_retry, e, new_darts)
288 .is_validated()
289 {}
290 } else {
291 while !process_inner_edge(map, &mut n_retry, e, new_darts)
292 .is_validated()
293 {}
294 }
295 n += n_retry as u32;
296 });
297 n
298 })); } handles.into_iter().map(|h| h.join().unwrap()).sum()
301 }) }
303}
304
305#[inline]
306fn process_outer_edge<T: CoordsFloat>(
307 map: &CMap2<T>,
308 n_retry: &mut u8,
309 e: EdgeIdType,
310 [nd1, nd2, nd3, _, _, _]: [DartIdType; 6],
311) -> TransactionResult<(), SewError> {
312 Transaction::with_control_and_err(
313 |_| {
314 *n_retry += 1;
315 TransactionControl::Retry
316 },
317 |trans| cut_outer_edge(trans, map, e, [nd1, nd2, nd3]),
318 ) }
320
321#[inline]
322fn process_inner_edge<T: CoordsFloat>(
323 map: &CMap2<T>,
324 n_retry: &mut u8,
325 e: EdgeIdType,
326 nds: [DartIdType; 6],
327) -> TransactionResult<(), SewError> {
328 Transaction::with_control_and_err(
329 |_| {
330 *n_retry += 1;
331 TransactionControl::Retry
332 },
333 |trans| cut_inner_edge(trans, map, e, nds),
334 ) }