honeycomb_benches/
cut_edges.rs

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
13// const MAX_RETRY: u8 = 10;
14
15pub 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    // load map from file
24    let mut instant = Instant::now();
25    let input_hash = hash_file(input_map).expect("E: could not compute input hash"); // file id for posterity
26    let mut map: CMap2<T> = CMapBuilder::from(input_map).build().unwrap();
27    #[cfg(debug_assertions)] // check input
28    {
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} "); // Step
49
50    // compute first batch
51    instant = Instant::now();
52    let mut edges: Vec<EdgeIdType> = map.iter_edges().collect();
53    print!("| {:>12} ", edges.len()); // n_edge_total
54    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} "); // n_edge_to_process
66    let mut nd = map.add_free_darts(6 * n_e); // 2 for edge split + 2*2 for new edges in neighbor tets
67    let mut darts: Vec<DartIdType> = (nd..nd + 6 * n_e as DartIdType).collect();
68    print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_compute_batch
69
70    // while there are edges to cut
71    while !edges.is_empty() {
72        // process batch
73        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()); // t_process_batch
84        println!("| {n_retry:>15}",); // n_transac_retry
85
86        (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        // compute the new batch
93        step += 1;
94        print!(" {step:>4} "); // Step
95        instant = Instant::now();
96        edges.extend(map.iter_edges());
97        print!("| {:>12} ", edges.len()); // n_edge_total
98        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} "); // n_edge_to_process
110        nd = map.add_free_darts(6 * n_e);
111        darts.par_drain(..); // is there a better way?
112        darts.extend(nd..nd + 6 * n_e as DartIdType);
113        if n_e != 0 {
114            print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_compute_batch
115        } else {
116            print!("| {:>18.6e} ", instant.elapsed().as_secs_f64()); // t_compute_batch
117            println!("| {:>18.6e} ", 0.0); // t_process_batch
118        }
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        }) // par_map
148        .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        }) // par_for_each
180        .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            })); // s.spawn
213        } // for wl in workloads
214        handles.into_iter().map(|h| h.join().unwrap()).sum()
215    }) // std::thread::scope
216}
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            // unfallible
232            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    ) // Transaction::with_control
266}
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            // unfallible
282            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    ) // Transaction::with_control
342}