pub struct ParallelCsr32Operator<'a> { /* private fields */ }Expand description
Read-only parallel CSR operator wrapper. It shares the validated CSR
storage and only changes the row execution policy used by apply.
Implementations§
Source§impl<'a> ParallelCsr32Operator<'a>
impl<'a> ParallelCsr32Operator<'a>
Sourcepub fn new(matrix: &'a Csr32Matrix) -> ParallelCsr32Operator<'a>
pub fn new(matrix: &'a Csr32Matrix) -> ParallelCsr32Operator<'a>
Examples found in repository?
examples/fem_structural_auto.rs (line 389)
304fn main() -> Result<(), Box<dyn Error>> {
305 let args = Args::parse()?;
306 println!(
307 "HyBIT {} structural-auto FEM benchmark",
308 env!("CARGO_PKG_VERSION")
309 );
310 println!("matrix : {}", args.matrix.display());
311 println!("coordinates : {}", args.coordinates.display());
312
313 let load_start = Instant::now();
314 let (matrix, mm) = read_matrix_market(&args.matrix)?;
315 let matrix_load_seconds = load_start.elapsed().as_secs_f64();
316 let coord_start = Instant::now();
317 let coordinates = read_coordinates(&args.coordinates)?;
318 let coord_load_seconds = coord_start.elapsed().as_secs_f64();
319 let profile = analyze_csr32(&matrix)?;
320
321 println!(
322 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
323 mm.symmetry, mm.input_entries, mm.csr_nnz
324 );
325 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
326 println!("nnz : {}", profile.nnz);
327 println!(
328 "CSR storage : {:.3} MiB",
329 mib(matrix.storage_bytes())
330 );
331 println!("matrix load : {:.3} ms", matrix_load_seconds * 1.0e3);
332 println!("coordinate nodes : {}", coordinates.len());
333 println!("coordinate load : {:.3} ms", coord_load_seconds * 1.0e3);
334
335 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
336 return Err(
337 "rigid-body two-level PCG requires a square matrix with a complete positive diagonal"
338 .into(),
339 );
340 }
341 if matrix.nrows() != coordinates.len() * 3 {
342 return Err(format!(
343 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
344 matrix.nrows(),
345 coordinates.len()
346 )
347 .into());
348 }
349
350 let generated_rhs = args.rhs.is_none();
351 let b = if let Some(path) = args.rhs.as_deref() {
352 println!("RHS : {}", path.display());
353 load_rhs(path, matrix.nrows())?
354 } else {
355 println!("RHS : generated as b=A*1 (known exact solution)");
356 let ones = vec![1.0; matrix.ncols()];
357 matrix.spmv(&ones)?
358 };
359 let mut solver = HybitSolver::new();
360 solver.set_options(SolverOptions {
361 relative_tolerance: args.relative_tolerance,
362 absolute_tolerance: 0.0,
363 max_iterations: args.max_iterations,
364 })?;
365 solver.set_structural_options(StructuralOptions {
366 target_coarse_dimension: args.target_coarse_dimension,
367 aggregation: args.aggregation,
368 spmv_policy: args.spmv_policy,
369 preconditioner_policy: args.preconditioner_policy,
370 pcg_vector_policy: args.pcg_vector_policy,
371 })?;
372
373 let analysis = solver.analyze_csr32(&matrix)?;
374 let mut prepared = solver.prepare_structural_csr32(&matrix, &analysis, &coordinates)?;
375 println!("policy : StructuralAuto/RigidBodyTwoLevel");
376 println!("aggregation : {:?}", prepared.aggregation());
377 println!("SpMV policy : {:?}", prepared.spmv_policy());
378 println!(
379 "precond policy : {:?}",
380 prepared.structural_preconditioner_policy()
381 );
382 println!("PCG vector policy : {:?}", prepared.pcg_vector_policy());
383 if prepared.parallel_spmv_enabled()
384 || prepared.parallel_preconditioner_enabled()
385 || prepared.parallel_pcg_vectors_enabled()
386 {
387 println!(
388 "Rayon threads : {}",
389 ParallelCsr32Operator::new(&matrix).rayon_threads()
390 );
391 }
392 if prepared.parallel_preconditioner_enabled() {
393 println!(
394 "parallel index : {:.3} MiB",
395 mib(prepared.parallel_preconditioner_index_bytes())
396 );
397 }
398 println!("target coarse dim : {}", args.target_coarse_dimension);
399 println!(
400 "aggregate nodes : {} (auto-selected)",
401 prepared.aggregate_nodes()
402 );
403 println!("fine block size : 3");
404 println!("aggregate count : {}", prepared.aggregate_count());
405 println!(
406 "aggregate min/max : {} / {} nodes",
407 prepared.min_aggregate_nodes(),
408 prepared.max_aggregate_nodes()
409 );
410 println!("modes/aggregate : 6");
411 println!("coarse dimension : {}", prepared.coarse_dimension());
412 println!(
413 "base factor : {:.3} MiB",
414 mib(prepared.base_factor_bytes())
415 );
416 println!(
417 "coarse factor : {:.3} MiB",
418 mib(prepared.coarse_factor_bytes())
419 );
420 println!(
421 "geometry storage : {:.3} MiB",
422 mib(prepared.geometry_bytes())
423 );
424 println!(
425 "total prec storage : {:.3} MiB",
426 mib(prepared.preconditioner_bytes())
427 );
428 println!(
429 "analysis : {:.3} ms",
430 prepared.analysis_seconds() * 1.0e3
431 );
432 println!(
433 "prepare : {:.3} ms",
434 prepared.prepare_seconds() * 1.0e3
435 );
436
437 let mut x = vec![0.0; matrix.ncols()];
438 let report = prepared.solve(&matrix, &b, &mut x)?;
439 let verified = verified_relative_residual(&matrix, &b, &x)?;
440
441 println!();
442 println!("Structural Auto API: 3x3 Block-Jacobi + six rigid-body coarse modes");
443 println!("status : {:?}", report.status);
444 println!("preconditioner : {:?}", report.preconditioner);
445 println!("iterations : {}", report.iterations);
446 println!("reported residual : {:.6e}", report.relative_residual);
447 println!("verified residual : {:.6e}", verified);
448 if generated_rhs {
449 println!("relative x error : {:.6e}", relative_error_to_ones(&x));
450 }
451 println!(
452 "solve time : {:.3} ms",
453 report.solve_seconds * 1.0e3
454 );
455 println!(
456 "total setup+solve : {:.3} ms",
457 (report.setup_seconds + report.solve_seconds) * 1.0e3
458 );
459
460 if !verified.is_finite() {
461 return Err("non-finite independently verified residual".into());
462 }
463 Ok(())
464}More examples
examples/fem_structural_pcg_parallel.rs (line 324)
245fn main() -> Result<(), Box<dyn Error>> {
246 let args = Args::parse()?;
247 println!(
248 "HyBIT {} serial vs parallel/fused PCG vector benchmark",
249 env!("CARGO_PKG_VERSION")
250 );
251 println!("matrix : {}", args.matrix.display());
252 println!("coordinates : {}", args.coordinates.display());
253
254 let load_start = Instant::now();
255 let (matrix, mm) = read_matrix_market(&args.matrix)?;
256 let matrix_load = load_start.elapsed().as_secs_f64();
257 let coord_start = Instant::now();
258 let coordinates = read_coordinates(&args.coordinates)?;
259 let coord_load = coord_start.elapsed().as_secs_f64();
260 let matrix_profile = analyze_csr32(&matrix)?;
261
262 println!(
263 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
264 mm.symmetry, mm.input_entries, mm.csr_nnz
265 );
266 println!(
267 "dimensions : {} x {}",
268 matrix_profile.nrows, matrix_profile.ncols
269 );
270 println!("nnz : {}", matrix_profile.nnz);
271 println!(
272 "CSR storage : {:.3} MiB",
273 mib(matrix.storage_bytes())
274 );
275 println!("matrix load : {:.3} ms", matrix_load * 1.0e3);
276 println!("coordinate nodes : {}", coordinates.len());
277 println!("coordinate load : {:.3} ms", coord_load * 1.0e3);
278 println!("aggregation : {:?}", args.aggregation);
279 println!("target coarse dim : {}", args.target_coarse_dimension);
280
281 if !matrix_profile.square || !matrix_profile.full_diagonal || !matrix_profile.positive_diagonal
282 {
283 return Err(
284 "structural PCG benchmark requires a square matrix with a complete positive diagonal"
285 .into(),
286 );
287 }
288 if matrix.nrows() != coordinates.len() * 3 {
289 return Err(format!(
290 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
291 matrix.nrows(),
292 coordinates.len()
293 )
294 .into());
295 }
296
297 let b = if let Some(path) = args.rhs.as_deref() {
298 println!("RHS : {}", path.display());
299 load_rhs(path, matrix.nrows())?
300 } else {
301 println!("RHS : generated as b=A*1 (known exact solution)");
302 matrix.spmv(&vec![1.0; matrix.ncols()])?
303 };
304
305 let aggregate_nodes =
306 recommend_rigid_body_aggregate_nodes(coordinates.len(), args.target_coarse_dimension)?;
307 let setup_start = Instant::now();
308 let preconditioner = match args.aggregation {
309 RigidBodyAggregation::Graph => {
310 RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32_graph(
311 &matrix,
312 &coordinates,
313 aggregate_nodes,
314 )?
315 }
316 RigidBodyAggregation::Contiguous => RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32(
317 &matrix,
318 &coordinates,
319 aggregate_nodes,
320 )?,
321 RigidBodyAggregation::Auto => unreachable!(),
322 };
323 let parallel_preconditioner = ParallelRigidBodyTwoLevelPreconditioner::new(&preconditioner)?;
324 let parallel_operator = ParallelCsr32Operator::new(&matrix);
325 let setup_seconds = setup_start.elapsed().as_secs_f64();
326
327 println!("aggregate target : {} nodes", aggregate_nodes);
328 println!("aggregate count : {}", preconditioner.aggregate_count());
329 println!("coarse dimension : {}", preconditioner.coarse_dimension());
330 println!(
331 "prec storage : {:.3} MiB",
332 mib(preconditioner.factor_bytes())
333 );
334 println!(
335 "parallel index : {:.3} MiB",
336 mib(parallel_preconditioner.index_storage_bytes())
337 );
338 println!("setup : {:.3} ms", setup_seconds * 1.0e3);
339 println!(
340 "Rayon threads : {}",
341 parallel_preconditioner.rayon_threads()
342 );
343 println!("vector chunk : {} values", PARALLEL_PCG_VECTOR_CHUNK);
344
345 let options = SolverOptions {
346 relative_tolerance: args.relative_tolerance,
347 absolute_tolerance: 0.0,
348 max_iterations: args.max_iterations,
349 };
350 let b_norm = norm2(&b).max(f64::MIN_POSITIVE);
351
352 let mut x_serial = vec![0.0; matrix.ncols()];
353 let mut ws_serial = PcgWorkspace::new(matrix.nrows());
354 let serial_start = Instant::now();
355 let serial_out = pcg_with_workspace(
356 ¶llel_operator,
357 ¶llel_preconditioner,
358 &b,
359 &mut x_serial,
360 options,
361 &mut ws_serial,
362 )?;
363 let serial_wall = serial_start.elapsed().as_secs_f64();
364 let serial_verified = verified_relative_residual(&matrix, &b, &x_serial)?;
365
366 let mut x_parallel = vec![0.0; matrix.ncols()];
367 let mut ws_parallel = PcgWorkspace::new(matrix.nrows());
368 let parallel_start = Instant::now();
369 let parallel_out = pcg_with_workspace_parallel_vectors(
370 ¶llel_operator,
371 ¶llel_preconditioner,
372 &b,
373 &mut x_parallel,
374 options,
375 &mut ws_parallel,
376 )?;
377 let parallel_wall = parallel_start.elapsed().as_secs_f64();
378 let parallel_verified = verified_relative_residual(&matrix, &b, &x_parallel)?;
379
380 println!();
381 println!("[1/2] Production PCG vector kernels");
382 println!("status : {:?}", serial_out.status);
383 println!("iterations : {}", serial_out.iterations);
384 println!(
385 "reported residual : {:.6e}",
386 serial_out.final_residual / b_norm
387 );
388 println!("verified residual : {:.6e}", serial_verified);
389 println!("solve time : {:.3} ms", serial_wall * 1.0e3);
390
391 println!();
392 println!("[2/2] Parallel/fused PCG vector kernels");
393 println!("status : {:?}", parallel_out.status);
394 println!("iterations : {}", parallel_out.iterations);
395 println!(
396 "reported residual : {:.6e}",
397 parallel_out.final_residual / b_norm
398 );
399 println!("verified residual : {:.6e}", parallel_verified);
400 println!("solve time : {:.3} ms", parallel_wall * 1.0e3);
401
402 println!();
403 println!("Comparison");
404 println!(
405 "iteration ratio : {:.3} (parallel vectors / production)",
406 parallel_out.iterations as f64 / (serial_out.iterations.max(1) as f64)
407 );
408 println!(
409 "solve-time ratio : {:.3} (parallel vectors / production)",
410 parallel_wall / serial_wall.max(f64::MIN_POSITIVE)
411 );
412 println!(
413 "PCG speedup : {:.3}x",
414 serial_wall / parallel_wall.max(f64::MIN_POSITIVE)
415 );
416 println!(
417 "solution delta : {:.6e} relative L2",
418 relative_difference(&x_serial, &x_parallel)
419 );
420 println!("note : sparse operator and preconditioner are identical in both runs");
421
422 Ok(())
423}examples/fem_structural_spmv.rs (line 319)
243fn main() -> Result<(), Box<dyn Error>> {
244 let args = Args::parse()?;
245 println!(
246 "HyBIT {} serial vs parallel CSR structural benchmark",
247 env!("CARGO_PKG_VERSION")
248 );
249 println!("matrix : {}", args.matrix.display());
250 println!("coordinates : {}", args.coordinates.display());
251
252 let load_start = Instant::now();
253 let (matrix, mm) = read_matrix_market(&args.matrix)?;
254 let matrix_load = load_start.elapsed().as_secs_f64();
255 let coord_start = Instant::now();
256 let coordinates = read_coordinates(&args.coordinates)?;
257 let coord_load = coord_start.elapsed().as_secs_f64();
258 let profile = analyze_csr32(&matrix)?;
259
260 println!(
261 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
262 mm.symmetry, mm.input_entries, mm.csr_nnz
263 );
264 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
265 println!("nnz : {}", profile.nnz);
266 println!(
267 "CSR storage : {:.3} MiB",
268 mib(matrix.storage_bytes())
269 );
270 println!("matrix load : {:.3} ms", matrix_load * 1.0e3);
271 println!("coordinate nodes : {}", coordinates.len());
272 println!("coordinate load : {:.3} ms", coord_load * 1.0e3);
273 println!("aggregation : {:?}", args.aggregation);
274 println!("target coarse dim : {}", args.target_coarse_dimension);
275 println!("kernel repeats : {}", args.kernel_repeats);
276
277 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
278 return Err(
279 "structural PCG benchmark requires a square matrix with a complete positive diagonal"
280 .into(),
281 );
282 }
283 if matrix.nrows() != coordinates.len() * 3 {
284 return Err(format!(
285 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
286 matrix.nrows(),
287 coordinates.len()
288 )
289 .into());
290 }
291
292 let b = if let Some(path) = args.rhs.as_deref() {
293 println!("RHS : {}", path.display());
294 load_rhs(path, matrix.nrows())?
295 } else {
296 println!("RHS : generated as b=A*1 (known exact solution)");
297 matrix.spmv(&vec![1.0; matrix.ncols()])?
298 };
299
300 let aggregate_nodes =
301 recommend_rigid_body_aggregate_nodes(coordinates.len(), args.target_coarse_dimension)?;
302 let setup_start = Instant::now();
303 let preconditioner = match args.aggregation {
304 RigidBodyAggregation::Graph => {
305 RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32_graph(
306 &matrix,
307 &coordinates,
308 aggregate_nodes,
309 )?
310 }
311 RigidBodyAggregation::Contiguous => RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32(
312 &matrix,
313 &coordinates,
314 aggregate_nodes,
315 )?,
316 RigidBodyAggregation::Auto => unreachable!(),
317 };
318 let setup_seconds = setup_start.elapsed().as_secs_f64();
319 let parallel = ParallelCsr32Operator::new(&matrix);
320
321 println!("aggregate target : {} nodes", aggregate_nodes);
322 println!("aggregate count : {}", preconditioner.aggregate_count());
323 println!("coarse dimension : {}", preconditioner.coarse_dimension());
324 println!(
325 "prec storage : {:.3} MiB",
326 mib(preconditioner.factor_bytes())
327 );
328 println!("setup : {:.3} ms", setup_seconds * 1.0e3);
329 println!("Rayon threads : {}", parallel.rayon_threads());
330
331 let mut ys = vec![0.0; matrix.nrows()];
332 let mut yp = vec![0.0; matrix.nrows()];
333 matrix.apply(&b, &mut ys)?;
334 parallel.apply(&b, &mut yp)?;
335 if ys != yp {
336 return Err("serial and parallel CSR kernels produced different results".into());
337 }
338
339 let serial_start = Instant::now();
340 for _ in 0..args.kernel_repeats {
341 matrix.apply(&b, &mut ys)?;
342 }
343 let serial_spmv = serial_start.elapsed().as_secs_f64();
344
345 let parallel_start = Instant::now();
346 for _ in 0..args.kernel_repeats {
347 parallel.apply(&b, &mut yp)?;
348 }
349 let parallel_spmv = parallel_start.elapsed().as_secs_f64();
350
351 println!();
352 println!("CSR SpMV microbenchmark");
353 println!(
354 "serial / call : {:.3} ms",
355 ms_per(serial_spmv, args.kernel_repeats)
356 );
357 println!(
358 "parallel / call : {:.3} ms",
359 ms_per(parallel_spmv, args.kernel_repeats)
360 );
361 println!(
362 "SpMV speedup : {:.3}x",
363 serial_spmv / parallel_spmv.max(f64::MIN_POSITIVE)
364 );
365
366 let options = SolverOptions {
367 relative_tolerance: args.relative_tolerance,
368 absolute_tolerance: 0.0,
369 max_iterations: args.max_iterations,
370 };
371
372 let mut xs = vec![0.0; matrix.ncols()];
373 let mut ws = PcgWorkspace::new(matrix.nrows());
374 let serial_solve_start = Instant::now();
375 let serial_out = pcg_with_workspace(&matrix, &preconditioner, &b, &mut xs, options, &mut ws)?;
376 let serial_solve = serial_solve_start.elapsed().as_secs_f64();
377 let serial_verified = verified_relative_residual(&matrix, &b, &xs)?;
378
379 let mut xp = vec![0.0; matrix.ncols()];
380 let mut wp = PcgWorkspace::new(matrix.nrows());
381 let parallel_solve_start = Instant::now();
382 let parallel_out =
383 pcg_with_workspace(¶llel, &preconditioner, &b, &mut xp, options, &mut wp)?;
384 let parallel_solve = parallel_solve_start.elapsed().as_secs_f64();
385 let parallel_verified = verified_relative_residual(&matrix, &b, &xp)?;
386
387 println!();
388 println!("[1/2] Serial CSR PCG");
389 println!("status : {:?}", serial_out.status);
390 println!("iterations : {}", serial_out.iterations);
391 println!(
392 "reported residual : {:.6e}",
393 serial_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
394 );
395 println!("verified residual : {:.6e}", serial_verified);
396 println!("solve time : {:.3} ms", serial_solve * 1.0e3);
397
398 println!();
399 println!("[2/2] Parallel CSR PCG");
400 println!("status : {:?}", parallel_out.status);
401 println!("iterations : {}", parallel_out.iterations);
402 println!(
403 "reported residual : {:.6e}",
404 parallel_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
405 );
406 println!("verified residual : {:.6e}", parallel_verified);
407 println!("solve time : {:.3} ms", parallel_solve * 1.0e3);
408
409 println!();
410 println!("Comparison");
411 println!(
412 "iteration ratio : {:.3} (parallel / serial)",
413 parallel_out.iterations as f64 / serial_out.iterations.max(1) as f64
414 );
415 println!(
416 "solve-time ratio : {:.3} (parallel / serial)",
417 parallel_solve / serial_solve.max(f64::MIN_POSITIVE)
418 );
419 println!(
420 "PCG speedup : {:.3}x",
421 serial_solve / parallel_solve.max(f64::MIN_POSITIVE)
422 );
423
424 if !serial_verified.is_finite() || !parallel_verified.is_finite() {
425 return Err("non-finite independently verified residual".into());
426 }
427 Ok(())
428}examples/fem_structural_pcg_profile.rs (line 557)
478fn main() -> Result<(), Box<dyn Error>> {
479 let args = Args::parse()?;
480 println!(
481 "HyBIT {} PCG vector-kernel profile",
482 env!("CARGO_PKG_VERSION")
483 );
484 println!("matrix : {}", args.matrix.display());
485 println!("coordinates : {}", args.coordinates.display());
486
487 let load_start = Instant::now();
488 let (matrix, mm) = read_matrix_market(&args.matrix)?;
489 let matrix_load = load_start.elapsed().as_secs_f64();
490 let coord_start = Instant::now();
491 let coordinates = read_coordinates(&args.coordinates)?;
492 let coord_load = coord_start.elapsed().as_secs_f64();
493 let matrix_profile = analyze_csr32(&matrix)?;
494
495 println!(
496 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
497 mm.symmetry, mm.input_entries, mm.csr_nnz
498 );
499 println!(
500 "dimensions : {} x {}",
501 matrix_profile.nrows, matrix_profile.ncols
502 );
503 println!("nnz : {}", matrix_profile.nnz);
504 println!(
505 "CSR storage : {:.3} MiB",
506 mib(matrix.storage_bytes())
507 );
508 println!("matrix load : {:.3} ms", matrix_load * 1.0e3);
509 println!("coordinate nodes : {}", coordinates.len());
510 println!("coordinate load : {:.3} ms", coord_load * 1.0e3);
511 println!("aggregation : {:?}", args.aggregation);
512 println!("target coarse dim : {}", args.target_coarse_dimension);
513
514 if !matrix_profile.square || !matrix_profile.full_diagonal || !matrix_profile.positive_diagonal
515 {
516 return Err(
517 "structural PCG benchmark requires a square matrix with a complete positive diagonal"
518 .into(),
519 );
520 }
521 if matrix.nrows() != coordinates.len() * 3 {
522 return Err(format!(
523 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
524 matrix.nrows(),
525 coordinates.len()
526 )
527 .into());
528 }
529
530 let b = if let Some(path) = args.rhs.as_deref() {
531 println!("RHS : {}", path.display());
532 load_rhs(path, matrix.nrows())?
533 } else {
534 println!("RHS : generated as b=A*1 (known exact solution)");
535 matrix.spmv(&vec![1.0; matrix.ncols()])?
536 };
537
538 let aggregate_nodes =
539 recommend_rigid_body_aggregate_nodes(coordinates.len(), args.target_coarse_dimension)?;
540 let setup_start = Instant::now();
541 let preconditioner = match args.aggregation {
542 RigidBodyAggregation::Graph => {
543 RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32_graph(
544 &matrix,
545 &coordinates,
546 aggregate_nodes,
547 )?
548 }
549 RigidBodyAggregation::Contiguous => RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32(
550 &matrix,
551 &coordinates,
552 aggregate_nodes,
553 )?,
554 RigidBodyAggregation::Auto => unreachable!(),
555 };
556 let parallel_preconditioner = ParallelRigidBodyTwoLevelPreconditioner::new(&preconditioner)?;
557 let parallel_operator = ParallelCsr32Operator::new(&matrix);
558 let setup_seconds = setup_start.elapsed().as_secs_f64();
559
560 println!("aggregate target : {} nodes", aggregate_nodes);
561 println!("aggregate count : {}", preconditioner.aggregate_count());
562 println!("coarse dimension : {}", preconditioner.coarse_dimension());
563 println!(
564 "prec storage : {:.3} MiB",
565 mib(preconditioner.factor_bytes())
566 );
567 println!(
568 "parallel index : {:.3} MiB",
569 mib(parallel_preconditioner.index_storage_bytes())
570 );
571 println!("setup : {:.3} ms", setup_seconds * 1.0e3);
572 println!(
573 "Rayon threads : {}",
574 parallel_preconditioner.rayon_threads()
575 );
576
577 let options = SolverOptions {
578 relative_tolerance: args.relative_tolerance,
579 absolute_tolerance: 0.0,
580 max_iterations: args.max_iterations,
581 };
582
583 let mut x_profiled = vec![0.0; matrix.ncols()];
584 let (profiled_out, stages, profiled_wall) = profiled_pcg(
585 ¶llel_operator,
586 ¶llel_preconditioner,
587 &b,
588 &mut x_profiled,
589 options,
590 )?;
591 let profiled_verified = verified_relative_residual(&matrix, &b, &x_profiled)?;
592
593 let mut x_control = vec![0.0; matrix.ncols()];
594 let mut workspace = PcgWorkspace::new(matrix.nrows());
595 let control_start = Instant::now();
596 let control_out = pcg_with_workspace(
597 ¶llel_operator,
598 ¶llel_preconditioner,
599 &b,
600 &mut x_control,
601 options,
602 &mut workspace,
603 )?;
604 let control_wall = control_start.elapsed().as_secs_f64();
605 let control_verified = verified_relative_residual(&matrix, &b, &x_control)?;
606
607 println!();
608 println!("Profiled PCG");
609 println!("status : {:?}", profiled_out.status);
610 println!("iterations : {}", profiled_out.iterations);
611 println!(
612 "reported residual : {:.6e}",
613 profiled_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
614 );
615 println!("verified residual : {:.6e}", profiled_verified);
616 println!("profiled wall : {:.3} ms", profiled_wall * 1.0e3);
617 println!(
618 "observed / iter : {:.3} ms",
619 if profiled_out.iterations == 0 {
620 0.0
621 } else {
622 profiled_wall * 1.0e3 / profiled_out.iterations as f64
623 }
624 );
625 println!();
626 println!("Actual nested PCG stage timings");
627 print_stage(
628 "operator A*x",
629 stages.operator_seconds,
630 stages.operator_calls,
631 profiled_wall,
632 );
633 print_stage(
634 "preconditioner",
635 stages.preconditioner_seconds,
636 stages.preconditioner_calls,
637 profiled_wall,
638 );
639 print_stage(
640 "dot reductions",
641 stages.dot_seconds,
642 stages.dot_calls,
643 profiled_wall,
644 );
645 print_stage(
646 "norm reductions",
647 stages.norm_seconds,
648 stages.norm_calls,
649 profiled_wall,
650 );
651 print_stage(
652 "x/r fused update",
653 stages.update_x_r_seconds,
654 stages.update_x_r_calls,
655 profiled_wall,
656 );
657 print_stage(
658 "p update",
659 stages.update_p_seconds,
660 stages.update_p_calls,
661 profiled_wall,
662 );
663 print_stage(
664 "initial residual",
665 stages.residual_init_seconds,
666 1,
667 profiled_wall,
668 );
669 print_stage("initial p copy", stages.copy_p_seconds, 1, profiled_wall);
670 let accounted = stages.accounted_seconds();
671 let unaccounted = (profiled_wall - accounted).max(0.0);
672 println!(
673 "accounted total : {:9.3} ms {:5.1}%",
674 accounted * 1.0e3,
675 100.0 * accounted / profiled_wall.max(f64::MIN_POSITIVE)
676 );
677 println!(
678 "timer/control overhead : {:9.3} ms {:5.1}%",
679 unaccounted * 1.0e3,
680 100.0 * unaccounted / profiled_wall.max(f64::MIN_POSITIVE)
681 );
682
683 println!();
684 println!("Uninstrumented control");
685 println!("status : {:?}", control_out.status);
686 println!("iterations : {}", control_out.iterations);
687 println!(
688 "reported residual : {:.6e}",
689 control_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
690 );
691 println!("verified residual : {:.6e}", control_verified);
692 println!("solve time : {:.3} ms", control_wall * 1.0e3);
693 println!(
694 "profile/control : {:.3}x",
695 profiled_wall / control_wall.max(f64::MIN_POSITIVE)
696 );
697 println!("note : profile uses the same serial PCG vector kernels as production; only nested timers are added");
698
699 Ok(())
700}examples/fem_structural_precond_parallel.rs (line 322)
245fn main() -> Result<(), Box<dyn Error>> {
246 let args = Args::parse()?;
247 println!(
248 "HyBIT {} serial vs parallel rigid-body preconditioner benchmark",
249 env!("CARGO_PKG_VERSION")
250 );
251 println!("matrix : {}", args.matrix.display());
252 println!("coordinates : {}", args.coordinates.display());
253
254 let load_start = Instant::now();
255 let (matrix, mm) = read_matrix_market(&args.matrix)?;
256 let matrix_load = load_start.elapsed().as_secs_f64();
257 let coord_start = Instant::now();
258 let coordinates = read_coordinates(&args.coordinates)?;
259 let coord_load = coord_start.elapsed().as_secs_f64();
260 let profile = analyze_csr32(&matrix)?;
261
262 println!(
263 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
264 mm.symmetry, mm.input_entries, mm.csr_nnz
265 );
266 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
267 println!("nnz : {}", profile.nnz);
268 println!(
269 "CSR storage : {:.3} MiB",
270 mib(matrix.storage_bytes())
271 );
272 println!("matrix load : {:.3} ms", matrix_load * 1.0e3);
273 println!("coordinate nodes : {}", coordinates.len());
274 println!("coordinate load : {:.3} ms", coord_load * 1.0e3);
275 println!("aggregation : {:?}", args.aggregation);
276 println!("target coarse dim : {}", args.target_coarse_dimension);
277 println!("kernel repeats : {}", args.kernel_repeats);
278
279 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
280 return Err(
281 "structural PCG benchmark requires a square matrix with a complete positive diagonal"
282 .into(),
283 );
284 }
285 if matrix.nrows() != coordinates.len() * 3 {
286 return Err(format!(
287 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
288 matrix.nrows(),
289 coordinates.len()
290 )
291 .into());
292 }
293
294 let b = if let Some(path) = args.rhs.as_deref() {
295 println!("RHS : {}", path.display());
296 load_rhs(path, matrix.nrows())?
297 } else {
298 println!("RHS : generated as b=A*1 (known exact solution)");
299 matrix.spmv(&vec![1.0; matrix.ncols()])?
300 };
301
302 let aggregate_nodes =
303 recommend_rigid_body_aggregate_nodes(coordinates.len(), args.target_coarse_dimension)?;
304 let setup_start = Instant::now();
305 let preconditioner = match args.aggregation {
306 RigidBodyAggregation::Graph => {
307 RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32_graph(
308 &matrix,
309 &coordinates,
310 aggregate_nodes,
311 )?
312 }
313 RigidBodyAggregation::Contiguous => RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32(
314 &matrix,
315 &coordinates,
316 aggregate_nodes,
317 )?,
318 RigidBodyAggregation::Auto => unreachable!(),
319 };
320 let setup_seconds = setup_start.elapsed().as_secs_f64();
321 let parallel_preconditioner = ParallelRigidBodyTwoLevelPreconditioner::new(&preconditioner)?;
322 let parallel_operator = ParallelCsr32Operator::new(&matrix);
323
324 println!("aggregate target : {} nodes", aggregate_nodes);
325 println!("aggregate count : {}", preconditioner.aggregate_count());
326 println!("coarse dimension : {}", preconditioner.coarse_dimension());
327 println!(
328 "prec storage : {:.3} MiB",
329 mib(preconditioner.factor_bytes())
330 );
331 println!(
332 "parallel index : {:.3} MiB",
333 mib(parallel_preconditioner.index_storage_bytes())
334 );
335 println!("setup : {:.3} ms", setup_seconds * 1.0e3);
336 println!(
337 "Rayon threads : {}",
338 parallel_preconditioner.rayon_threads()
339 );
340
341 let mut zs = vec![0.0; matrix.nrows()];
342 let mut zp = vec![0.0; matrix.nrows()];
343 preconditioner.apply(&b, &mut zs)?;
344 parallel_preconditioner.apply(&b, &mut zp)?;
345 let scale = zs.iter().fold(1.0f64, |m, &v| m.max(v.abs()));
346 let max_diff = zs
347 .iter()
348 .zip(&zp)
349 .fold(0.0f64, |m, (&a, &b)| m.max((a - b).abs()));
350 if max_diff > 1.0e-11 * scale {
351 return Err(format!(
352 "serial/parallel preconditioner mismatch: max diff={max_diff:e}, scale={scale:e}"
353 )
354 .into());
355 }
356
357 let serial_prec_start = Instant::now();
358 for _ in 0..args.kernel_repeats {
359 preconditioner.apply(&b, &mut zs)?;
360 }
361 let serial_prec = serial_prec_start.elapsed().as_secs_f64();
362
363 let parallel_prec_start = Instant::now();
364 for _ in 0..args.kernel_repeats {
365 parallel_preconditioner.apply(&b, &mut zp)?;
366 }
367 let parallel_prec = parallel_prec_start.elapsed().as_secs_f64();
368
369 println!();
370 println!("Preconditioner microbenchmark");
371 println!(
372 "serial / apply : {:.3} ms",
373 ms_per(serial_prec, args.kernel_repeats)
374 );
375 println!(
376 "parallel / apply : {:.3} ms",
377 ms_per(parallel_prec, args.kernel_repeats)
378 );
379 println!(
380 "precond speedup : {:.3}x",
381 serial_prec / parallel_prec.max(f64::MIN_POSITIVE)
382 );
383
384 let options = SolverOptions {
385 relative_tolerance: args.relative_tolerance,
386 absolute_tolerance: 0.0,
387 max_iterations: args.max_iterations,
388 };
389
390 let mut xs = vec![0.0; matrix.ncols()];
391 let mut ws = PcgWorkspace::new(matrix.nrows());
392 let serial_solve_start = Instant::now();
393 let serial_out = pcg_with_workspace(
394 ¶llel_operator,
395 &preconditioner,
396 &b,
397 &mut xs,
398 options,
399 &mut ws,
400 )?;
401 let serial_solve = serial_solve_start.elapsed().as_secs_f64();
402 let serial_verified = verified_relative_residual(&matrix, &b, &xs)?;
403
404 let mut xp = vec![0.0; matrix.ncols()];
405 let mut wp = PcgWorkspace::new(matrix.nrows());
406 let parallel_solve_start = Instant::now();
407 let parallel_out = pcg_with_workspace(
408 ¶llel_operator,
409 ¶llel_preconditioner,
410 &b,
411 &mut xp,
412 options,
413 &mut wp,
414 )?;
415 let parallel_solve = parallel_solve_start.elapsed().as_secs_f64();
416 let parallel_verified = verified_relative_residual(&matrix, &b, &xp)?;
417
418 println!();
419 println!("[1/2] Parallel CSR + serial preconditioner");
420 println!("status : {:?}", serial_out.status);
421 println!("iterations : {}", serial_out.iterations);
422 println!(
423 "reported residual : {:.6e}",
424 serial_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
425 );
426 println!("verified residual : {:.6e}", serial_verified);
427 println!("solve time : {:.3} ms", serial_solve * 1.0e3);
428
429 println!();
430 println!("[2/2] Parallel CSR + parallel preconditioner");
431 println!("status : {:?}", parallel_out.status);
432 println!("iterations : {}", parallel_out.iterations);
433 println!(
434 "reported residual : {:.6e}",
435 parallel_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
436 );
437 println!("verified residual : {:.6e}", parallel_verified);
438 println!("solve time : {:.3} ms", parallel_solve * 1.0e3);
439
440 println!();
441 println!("Comparison");
442 println!(
443 "iteration ratio : {:.3} (parallel prec / serial prec)",
444 parallel_out.iterations as f64 / serial_out.iterations.max(1) as f64
445 );
446 println!(
447 "solve-time ratio : {:.3} (parallel prec / serial prec)",
448 parallel_solve / serial_solve.max(f64::MIN_POSITIVE)
449 );
450 println!(
451 "PCG speedup : {:.3}x",
452 serial_solve / parallel_solve.max(f64::MIN_POSITIVE)
453 );
454
455 if !serial_verified.is_finite() || !parallel_verified.is_finite() {
456 return Err("non-finite independently verified residual".into());
457 }
458 Ok(())
459}examples/fem_structural_prepared.rs (line 402)
311fn main() -> Result<(), Box<dyn Error>> {
312 let args = Args::parse()?;
313 println!(
314 "HyBIT {} prepared structural solve-many FEM benchmark",
315 env!("CARGO_PKG_VERSION")
316 );
317 println!("matrix : {}", args.matrix.display());
318 println!("coordinates : {}", args.coordinates.display());
319
320 let load_start = Instant::now();
321 let (matrix, mm) = read_matrix_market(&args.matrix)?;
322 let matrix_load_seconds = load_start.elapsed().as_secs_f64();
323 let coord_start = Instant::now();
324 let coordinates = read_coordinates(&args.coordinates)?;
325 let coord_load_seconds = coord_start.elapsed().as_secs_f64();
326 let profile = analyze_csr32(&matrix)?;
327
328 println!(
329 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
330 mm.symmetry, mm.input_entries, mm.csr_nnz
331 );
332 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
333 println!("nnz : {}", profile.nnz);
334 println!(
335 "CSR storage : {:.3} MiB",
336 mib(matrix.storage_bytes())
337 );
338 println!("matrix load : {:.3} ms", matrix_load_seconds * 1.0e3);
339 println!("coordinate nodes : {}", coordinates.len());
340 println!("coordinate load : {:.3} ms", coord_load_seconds * 1.0e3);
341
342 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
343 return Err(
344 "prepared structural PCG requires a square matrix with a complete positive diagonal"
345 .into(),
346 );
347 }
348 if matrix.nrows() != coordinates.len() * 3 {
349 return Err(format!(
350 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
351 matrix.nrows(),
352 coordinates.len()
353 )
354 .into());
355 }
356
357 let generated_rhs = args.rhs.is_none();
358 let b = if let Some(path) = args.rhs.as_deref() {
359 println!("RHS : {}", path.display());
360 load_rhs(path, matrix.nrows())?
361 } else {
362 println!("RHS : generated as b=A*1 (known exact solution)");
363 let ones = vec![1.0; matrix.ncols()];
364 matrix.spmv(&ones)?
365 };
366 println!(
367 "repeats : {} (fresh zero initial guess each solve)",
368 args.repeats
369 );
370
371 let mut solver = HybitSolver::new();
372 solver.set_options(SolverOptions {
373 relative_tolerance: args.relative_tolerance,
374 absolute_tolerance: 0.0,
375 max_iterations: args.max_iterations,
376 })?;
377 solver.set_structural_options(StructuralOptions {
378 target_coarse_dimension: args.target_coarse_dimension,
379 aggregation: args.aggregation,
380 spmv_policy: args.spmv_policy,
381 preconditioner_policy: args.preconditioner_policy,
382 pcg_vector_policy: args.pcg_vector_policy,
383 })?;
384
385 let analysis = solver.analyze_csr32(&matrix)?;
386 let mut prepared = solver.prepare_structural_csr32(&matrix, &analysis, &coordinates)?;
387
388 println!("policy : StructuralAuto/RigidBodyTwoLevel/Prepared");
389 println!("aggregation : {:?}", prepared.aggregation());
390 println!("SpMV policy : {:?}", prepared.spmv_policy());
391 println!(
392 "precond policy : {:?}",
393 prepared.structural_preconditioner_policy()
394 );
395 println!("PCG vector policy : {:?}", prepared.pcg_vector_policy());
396 if prepared.parallel_spmv_enabled()
397 || prepared.parallel_preconditioner_enabled()
398 || prepared.parallel_pcg_vectors_enabled()
399 {
400 println!(
401 "Rayon threads : {}",
402 ParallelCsr32Operator::new(&matrix).rayon_threads()
403 );
404 }
405 if prepared.parallel_preconditioner_enabled() {
406 println!(
407 "parallel index : {:.3} MiB",
408 mib(prepared.parallel_preconditioner_index_bytes())
409 );
410 }
411 println!("target coarse dim : {}", args.target_coarse_dimension);
412 println!(
413 "aggregate nodes : {} (auto-selected)",
414 prepared.aggregate_nodes()
415 );
416 println!("aggregate count : {}", prepared.aggregate_count());
417 println!(
418 "aggregate min/max : {} / {} nodes",
419 prepared.min_aggregate_nodes(),
420 prepared.max_aggregate_nodes()
421 );
422 println!("modes/aggregate : 6");
423 println!("coarse dimension : {}", prepared.coarse_dimension());
424 println!(
425 "base factor : {:.3} MiB",
426 mib(prepared.base_factor_bytes())
427 );
428 println!(
429 "coarse factor : {:.3} MiB",
430 mib(prepared.coarse_factor_bytes())
431 );
432 println!(
433 "geometry storage : {:.3} MiB",
434 mib(prepared.geometry_bytes())
435 );
436 println!(
437 "total prec storage : {:.3} MiB",
438 mib(prepared.preconditioner_bytes())
439 );
440 println!(
441 "Krylov workspace : {:.3} MiB",
442 mib(prepared.krylov_workspace_bytes())
443 );
444 println!(
445 "analysis once : {:.3} ms",
446 prepared.analysis_seconds() * 1.0e3
447 );
448 println!(
449 "prepare once : {:.3} ms",
450 prepared.prepare_seconds() * 1.0e3
451 );
452
453 let mut total_solve_seconds = 0.0f64;
454 for repetition in 1..=args.repeats {
455 let mut x = vec![0.0; matrix.ncols()];
456 let wall_start = Instant::now();
457 let report = prepared.solve(&matrix, &b, &mut x)?;
458 let wall_seconds = wall_start.elapsed().as_secs_f64();
459 let verified = verified_relative_residual(&matrix, &b, &x)?;
460 total_solve_seconds += report.solve_seconds;
461
462 println!();
463 println!("Solve #{repetition}");
464 println!("status : {:?}", report.status);
465 println!("preconditioner : {:?}", report.preconditioner);
466 println!("reused : {}", report.preconditioner_reused);
467 println!("sequence : {}", report.solve_sequence);
468 println!("iterations : {}", report.iterations);
469 println!("reported residual : {:.6e}", report.relative_residual);
470 println!("verified residual : {:.6e}", verified);
471 if generated_rhs {
472 println!("relative x error : {:.6e}", relative_error_to_ones(&x));
473 }
474 println!(
475 "analysis charged : {:.3} ms",
476 report.analysis_seconds * 1.0e3
477 );
478 println!(
479 "prepare charged : {:.3} ms",
480 report.prepare_seconds * 1.0e3
481 );
482 println!(
483 "solve time : {:.3} ms",
484 report.solve_seconds * 1.0e3
485 );
486 println!("call wall : {:.3} ms", wall_seconds * 1.0e3);
487
488 if repetition == 1 && report.preconditioner_reused {
489 return Err("first prepared structural solve unexpectedly reported reuse".into());
490 }
491 if repetition > 1 && !report.preconditioner_reused {
492 return Err(
493 "subsequent prepared structural solve did not report preconditioner reuse".into(),
494 );
495 }
496 if repetition > 1 && (report.analysis_seconds != 0.0 || report.prepare_seconds != 0.0) {
497 return Err(
498 "subsequent prepared structural solve was charged reusable setup cost".into(),
499 );
500 }
501 if !verified.is_finite() {
502 return Err("non-finite independently verified residual".into());
503 }
504 }
505
506 println!();
507 println!("Prepared reuse summary");
508 println!("solve count : {}", prepared.solve_count());
509 println!(
510 "one-time setup : {:.3} ms",
511 (prepared.analysis_seconds() + prepared.prepare_seconds()) * 1.0e3
512 );
513 println!("sum solve time : {:.3} ms", total_solve_seconds * 1.0e3);
514 println!(
515 "amortized total : {:.3} ms/solve",
516 ((prepared.analysis_seconds() + prepared.prepare_seconds() + total_solve_seconds)
517 / args.repeats as f64)
518 * 1.0e3
519 );
520
521 Ok(())
522}pub fn matrix(&self) -> &'a Csr32Matrix
Sourcepub fn rayon_threads(&self) -> usize
pub fn rayon_threads(&self) -> usize
Examples found in repository?
examples/fem_structural_auto.rs (line 389)
304fn main() -> Result<(), Box<dyn Error>> {
305 let args = Args::parse()?;
306 println!(
307 "HyBIT {} structural-auto FEM benchmark",
308 env!("CARGO_PKG_VERSION")
309 );
310 println!("matrix : {}", args.matrix.display());
311 println!("coordinates : {}", args.coordinates.display());
312
313 let load_start = Instant::now();
314 let (matrix, mm) = read_matrix_market(&args.matrix)?;
315 let matrix_load_seconds = load_start.elapsed().as_secs_f64();
316 let coord_start = Instant::now();
317 let coordinates = read_coordinates(&args.coordinates)?;
318 let coord_load_seconds = coord_start.elapsed().as_secs_f64();
319 let profile = analyze_csr32(&matrix)?;
320
321 println!(
322 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
323 mm.symmetry, mm.input_entries, mm.csr_nnz
324 );
325 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
326 println!("nnz : {}", profile.nnz);
327 println!(
328 "CSR storage : {:.3} MiB",
329 mib(matrix.storage_bytes())
330 );
331 println!("matrix load : {:.3} ms", matrix_load_seconds * 1.0e3);
332 println!("coordinate nodes : {}", coordinates.len());
333 println!("coordinate load : {:.3} ms", coord_load_seconds * 1.0e3);
334
335 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
336 return Err(
337 "rigid-body two-level PCG requires a square matrix with a complete positive diagonal"
338 .into(),
339 );
340 }
341 if matrix.nrows() != coordinates.len() * 3 {
342 return Err(format!(
343 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
344 matrix.nrows(),
345 coordinates.len()
346 )
347 .into());
348 }
349
350 let generated_rhs = args.rhs.is_none();
351 let b = if let Some(path) = args.rhs.as_deref() {
352 println!("RHS : {}", path.display());
353 load_rhs(path, matrix.nrows())?
354 } else {
355 println!("RHS : generated as b=A*1 (known exact solution)");
356 let ones = vec![1.0; matrix.ncols()];
357 matrix.spmv(&ones)?
358 };
359 let mut solver = HybitSolver::new();
360 solver.set_options(SolverOptions {
361 relative_tolerance: args.relative_tolerance,
362 absolute_tolerance: 0.0,
363 max_iterations: args.max_iterations,
364 })?;
365 solver.set_structural_options(StructuralOptions {
366 target_coarse_dimension: args.target_coarse_dimension,
367 aggregation: args.aggregation,
368 spmv_policy: args.spmv_policy,
369 preconditioner_policy: args.preconditioner_policy,
370 pcg_vector_policy: args.pcg_vector_policy,
371 })?;
372
373 let analysis = solver.analyze_csr32(&matrix)?;
374 let mut prepared = solver.prepare_structural_csr32(&matrix, &analysis, &coordinates)?;
375 println!("policy : StructuralAuto/RigidBodyTwoLevel");
376 println!("aggregation : {:?}", prepared.aggregation());
377 println!("SpMV policy : {:?}", prepared.spmv_policy());
378 println!(
379 "precond policy : {:?}",
380 prepared.structural_preconditioner_policy()
381 );
382 println!("PCG vector policy : {:?}", prepared.pcg_vector_policy());
383 if prepared.parallel_spmv_enabled()
384 || prepared.parallel_preconditioner_enabled()
385 || prepared.parallel_pcg_vectors_enabled()
386 {
387 println!(
388 "Rayon threads : {}",
389 ParallelCsr32Operator::new(&matrix).rayon_threads()
390 );
391 }
392 if prepared.parallel_preconditioner_enabled() {
393 println!(
394 "parallel index : {:.3} MiB",
395 mib(prepared.parallel_preconditioner_index_bytes())
396 );
397 }
398 println!("target coarse dim : {}", args.target_coarse_dimension);
399 println!(
400 "aggregate nodes : {} (auto-selected)",
401 prepared.aggregate_nodes()
402 );
403 println!("fine block size : 3");
404 println!("aggregate count : {}", prepared.aggregate_count());
405 println!(
406 "aggregate min/max : {} / {} nodes",
407 prepared.min_aggregate_nodes(),
408 prepared.max_aggregate_nodes()
409 );
410 println!("modes/aggregate : 6");
411 println!("coarse dimension : {}", prepared.coarse_dimension());
412 println!(
413 "base factor : {:.3} MiB",
414 mib(prepared.base_factor_bytes())
415 );
416 println!(
417 "coarse factor : {:.3} MiB",
418 mib(prepared.coarse_factor_bytes())
419 );
420 println!(
421 "geometry storage : {:.3} MiB",
422 mib(prepared.geometry_bytes())
423 );
424 println!(
425 "total prec storage : {:.3} MiB",
426 mib(prepared.preconditioner_bytes())
427 );
428 println!(
429 "analysis : {:.3} ms",
430 prepared.analysis_seconds() * 1.0e3
431 );
432 println!(
433 "prepare : {:.3} ms",
434 prepared.prepare_seconds() * 1.0e3
435 );
436
437 let mut x = vec![0.0; matrix.ncols()];
438 let report = prepared.solve(&matrix, &b, &mut x)?;
439 let verified = verified_relative_residual(&matrix, &b, &x)?;
440
441 println!();
442 println!("Structural Auto API: 3x3 Block-Jacobi + six rigid-body coarse modes");
443 println!("status : {:?}", report.status);
444 println!("preconditioner : {:?}", report.preconditioner);
445 println!("iterations : {}", report.iterations);
446 println!("reported residual : {:.6e}", report.relative_residual);
447 println!("verified residual : {:.6e}", verified);
448 if generated_rhs {
449 println!("relative x error : {:.6e}", relative_error_to_ones(&x));
450 }
451 println!(
452 "solve time : {:.3} ms",
453 report.solve_seconds * 1.0e3
454 );
455 println!(
456 "total setup+solve : {:.3} ms",
457 (report.setup_seconds + report.solve_seconds) * 1.0e3
458 );
459
460 if !verified.is_finite() {
461 return Err("non-finite independently verified residual".into());
462 }
463 Ok(())
464}More examples
examples/fem_structural_spmv.rs (line 329)
243fn main() -> Result<(), Box<dyn Error>> {
244 let args = Args::parse()?;
245 println!(
246 "HyBIT {} serial vs parallel CSR structural benchmark",
247 env!("CARGO_PKG_VERSION")
248 );
249 println!("matrix : {}", args.matrix.display());
250 println!("coordinates : {}", args.coordinates.display());
251
252 let load_start = Instant::now();
253 let (matrix, mm) = read_matrix_market(&args.matrix)?;
254 let matrix_load = load_start.elapsed().as_secs_f64();
255 let coord_start = Instant::now();
256 let coordinates = read_coordinates(&args.coordinates)?;
257 let coord_load = coord_start.elapsed().as_secs_f64();
258 let profile = analyze_csr32(&matrix)?;
259
260 println!(
261 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
262 mm.symmetry, mm.input_entries, mm.csr_nnz
263 );
264 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
265 println!("nnz : {}", profile.nnz);
266 println!(
267 "CSR storage : {:.3} MiB",
268 mib(matrix.storage_bytes())
269 );
270 println!("matrix load : {:.3} ms", matrix_load * 1.0e3);
271 println!("coordinate nodes : {}", coordinates.len());
272 println!("coordinate load : {:.3} ms", coord_load * 1.0e3);
273 println!("aggregation : {:?}", args.aggregation);
274 println!("target coarse dim : {}", args.target_coarse_dimension);
275 println!("kernel repeats : {}", args.kernel_repeats);
276
277 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
278 return Err(
279 "structural PCG benchmark requires a square matrix with a complete positive diagonal"
280 .into(),
281 );
282 }
283 if matrix.nrows() != coordinates.len() * 3 {
284 return Err(format!(
285 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
286 matrix.nrows(),
287 coordinates.len()
288 )
289 .into());
290 }
291
292 let b = if let Some(path) = args.rhs.as_deref() {
293 println!("RHS : {}", path.display());
294 load_rhs(path, matrix.nrows())?
295 } else {
296 println!("RHS : generated as b=A*1 (known exact solution)");
297 matrix.spmv(&vec![1.0; matrix.ncols()])?
298 };
299
300 let aggregate_nodes =
301 recommend_rigid_body_aggregate_nodes(coordinates.len(), args.target_coarse_dimension)?;
302 let setup_start = Instant::now();
303 let preconditioner = match args.aggregation {
304 RigidBodyAggregation::Graph => {
305 RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32_graph(
306 &matrix,
307 &coordinates,
308 aggregate_nodes,
309 )?
310 }
311 RigidBodyAggregation::Contiguous => RigidBodyTwoLevelBlockJacobiPreconditioner::from_csr32(
312 &matrix,
313 &coordinates,
314 aggregate_nodes,
315 )?,
316 RigidBodyAggregation::Auto => unreachable!(),
317 };
318 let setup_seconds = setup_start.elapsed().as_secs_f64();
319 let parallel = ParallelCsr32Operator::new(&matrix);
320
321 println!("aggregate target : {} nodes", aggregate_nodes);
322 println!("aggregate count : {}", preconditioner.aggregate_count());
323 println!("coarse dimension : {}", preconditioner.coarse_dimension());
324 println!(
325 "prec storage : {:.3} MiB",
326 mib(preconditioner.factor_bytes())
327 );
328 println!("setup : {:.3} ms", setup_seconds * 1.0e3);
329 println!("Rayon threads : {}", parallel.rayon_threads());
330
331 let mut ys = vec![0.0; matrix.nrows()];
332 let mut yp = vec![0.0; matrix.nrows()];
333 matrix.apply(&b, &mut ys)?;
334 parallel.apply(&b, &mut yp)?;
335 if ys != yp {
336 return Err("serial and parallel CSR kernels produced different results".into());
337 }
338
339 let serial_start = Instant::now();
340 for _ in 0..args.kernel_repeats {
341 matrix.apply(&b, &mut ys)?;
342 }
343 let serial_spmv = serial_start.elapsed().as_secs_f64();
344
345 let parallel_start = Instant::now();
346 for _ in 0..args.kernel_repeats {
347 parallel.apply(&b, &mut yp)?;
348 }
349 let parallel_spmv = parallel_start.elapsed().as_secs_f64();
350
351 println!();
352 println!("CSR SpMV microbenchmark");
353 println!(
354 "serial / call : {:.3} ms",
355 ms_per(serial_spmv, args.kernel_repeats)
356 );
357 println!(
358 "parallel / call : {:.3} ms",
359 ms_per(parallel_spmv, args.kernel_repeats)
360 );
361 println!(
362 "SpMV speedup : {:.3}x",
363 serial_spmv / parallel_spmv.max(f64::MIN_POSITIVE)
364 );
365
366 let options = SolverOptions {
367 relative_tolerance: args.relative_tolerance,
368 absolute_tolerance: 0.0,
369 max_iterations: args.max_iterations,
370 };
371
372 let mut xs = vec![0.0; matrix.ncols()];
373 let mut ws = PcgWorkspace::new(matrix.nrows());
374 let serial_solve_start = Instant::now();
375 let serial_out = pcg_with_workspace(&matrix, &preconditioner, &b, &mut xs, options, &mut ws)?;
376 let serial_solve = serial_solve_start.elapsed().as_secs_f64();
377 let serial_verified = verified_relative_residual(&matrix, &b, &xs)?;
378
379 let mut xp = vec![0.0; matrix.ncols()];
380 let mut wp = PcgWorkspace::new(matrix.nrows());
381 let parallel_solve_start = Instant::now();
382 let parallel_out =
383 pcg_with_workspace(¶llel, &preconditioner, &b, &mut xp, options, &mut wp)?;
384 let parallel_solve = parallel_solve_start.elapsed().as_secs_f64();
385 let parallel_verified = verified_relative_residual(&matrix, &b, &xp)?;
386
387 println!();
388 println!("[1/2] Serial CSR PCG");
389 println!("status : {:?}", serial_out.status);
390 println!("iterations : {}", serial_out.iterations);
391 println!(
392 "reported residual : {:.6e}",
393 serial_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
394 );
395 println!("verified residual : {:.6e}", serial_verified);
396 println!("solve time : {:.3} ms", serial_solve * 1.0e3);
397
398 println!();
399 println!("[2/2] Parallel CSR PCG");
400 println!("status : {:?}", parallel_out.status);
401 println!("iterations : {}", parallel_out.iterations);
402 println!(
403 "reported residual : {:.6e}",
404 parallel_out.final_residual / norm2(&b).max(f64::MIN_POSITIVE)
405 );
406 println!("verified residual : {:.6e}", parallel_verified);
407 println!("solve time : {:.3} ms", parallel_solve * 1.0e3);
408
409 println!();
410 println!("Comparison");
411 println!(
412 "iteration ratio : {:.3} (parallel / serial)",
413 parallel_out.iterations as f64 / serial_out.iterations.max(1) as f64
414 );
415 println!(
416 "solve-time ratio : {:.3} (parallel / serial)",
417 parallel_solve / serial_solve.max(f64::MIN_POSITIVE)
418 );
419 println!(
420 "PCG speedup : {:.3}x",
421 serial_solve / parallel_solve.max(f64::MIN_POSITIVE)
422 );
423
424 if !serial_verified.is_finite() || !parallel_verified.is_finite() {
425 return Err("non-finite independently verified residual".into());
426 }
427 Ok(())
428}examples/fem_structural_prepared.rs (line 402)
311fn main() -> Result<(), Box<dyn Error>> {
312 let args = Args::parse()?;
313 println!(
314 "HyBIT {} prepared structural solve-many FEM benchmark",
315 env!("CARGO_PKG_VERSION")
316 );
317 println!("matrix : {}", args.matrix.display());
318 println!("coordinates : {}", args.coordinates.display());
319
320 let load_start = Instant::now();
321 let (matrix, mm) = read_matrix_market(&args.matrix)?;
322 let matrix_load_seconds = load_start.elapsed().as_secs_f64();
323 let coord_start = Instant::now();
324 let coordinates = read_coordinates(&args.coordinates)?;
325 let coord_load_seconds = coord_start.elapsed().as_secs_f64();
326 let profile = analyze_csr32(&matrix)?;
327
328 println!(
329 "Matrix Market : {:?}, {} input entries -> {} CSR nnz",
330 mm.symmetry, mm.input_entries, mm.csr_nnz
331 );
332 println!("dimensions : {} x {}", profile.nrows, profile.ncols);
333 println!("nnz : {}", profile.nnz);
334 println!(
335 "CSR storage : {:.3} MiB",
336 mib(matrix.storage_bytes())
337 );
338 println!("matrix load : {:.3} ms", matrix_load_seconds * 1.0e3);
339 println!("coordinate nodes : {}", coordinates.len());
340 println!("coordinate load : {:.3} ms", coord_load_seconds * 1.0e3);
341
342 if !profile.square || !profile.full_diagonal || !profile.positive_diagonal {
343 return Err(
344 "prepared structural PCG requires a square matrix with a complete positive diagonal"
345 .into(),
346 );
347 }
348 if matrix.nrows() != coordinates.len() * 3 {
349 return Err(format!(
350 "matrix/coordinate mismatch: {} matrix rows != {} coordinate nodes * 3",
351 matrix.nrows(),
352 coordinates.len()
353 )
354 .into());
355 }
356
357 let generated_rhs = args.rhs.is_none();
358 let b = if let Some(path) = args.rhs.as_deref() {
359 println!("RHS : {}", path.display());
360 load_rhs(path, matrix.nrows())?
361 } else {
362 println!("RHS : generated as b=A*1 (known exact solution)");
363 let ones = vec![1.0; matrix.ncols()];
364 matrix.spmv(&ones)?
365 };
366 println!(
367 "repeats : {} (fresh zero initial guess each solve)",
368 args.repeats
369 );
370
371 let mut solver = HybitSolver::new();
372 solver.set_options(SolverOptions {
373 relative_tolerance: args.relative_tolerance,
374 absolute_tolerance: 0.0,
375 max_iterations: args.max_iterations,
376 })?;
377 solver.set_structural_options(StructuralOptions {
378 target_coarse_dimension: args.target_coarse_dimension,
379 aggregation: args.aggregation,
380 spmv_policy: args.spmv_policy,
381 preconditioner_policy: args.preconditioner_policy,
382 pcg_vector_policy: args.pcg_vector_policy,
383 })?;
384
385 let analysis = solver.analyze_csr32(&matrix)?;
386 let mut prepared = solver.prepare_structural_csr32(&matrix, &analysis, &coordinates)?;
387
388 println!("policy : StructuralAuto/RigidBodyTwoLevel/Prepared");
389 println!("aggregation : {:?}", prepared.aggregation());
390 println!("SpMV policy : {:?}", prepared.spmv_policy());
391 println!(
392 "precond policy : {:?}",
393 prepared.structural_preconditioner_policy()
394 );
395 println!("PCG vector policy : {:?}", prepared.pcg_vector_policy());
396 if prepared.parallel_spmv_enabled()
397 || prepared.parallel_preconditioner_enabled()
398 || prepared.parallel_pcg_vectors_enabled()
399 {
400 println!(
401 "Rayon threads : {}",
402 ParallelCsr32Operator::new(&matrix).rayon_threads()
403 );
404 }
405 if prepared.parallel_preconditioner_enabled() {
406 println!(
407 "parallel index : {:.3} MiB",
408 mib(prepared.parallel_preconditioner_index_bytes())
409 );
410 }
411 println!("target coarse dim : {}", args.target_coarse_dimension);
412 println!(
413 "aggregate nodes : {} (auto-selected)",
414 prepared.aggregate_nodes()
415 );
416 println!("aggregate count : {}", prepared.aggregate_count());
417 println!(
418 "aggregate min/max : {} / {} nodes",
419 prepared.min_aggregate_nodes(),
420 prepared.max_aggregate_nodes()
421 );
422 println!("modes/aggregate : 6");
423 println!("coarse dimension : {}", prepared.coarse_dimension());
424 println!(
425 "base factor : {:.3} MiB",
426 mib(prepared.base_factor_bytes())
427 );
428 println!(
429 "coarse factor : {:.3} MiB",
430 mib(prepared.coarse_factor_bytes())
431 );
432 println!(
433 "geometry storage : {:.3} MiB",
434 mib(prepared.geometry_bytes())
435 );
436 println!(
437 "total prec storage : {:.3} MiB",
438 mib(prepared.preconditioner_bytes())
439 );
440 println!(
441 "Krylov workspace : {:.3} MiB",
442 mib(prepared.krylov_workspace_bytes())
443 );
444 println!(
445 "analysis once : {:.3} ms",
446 prepared.analysis_seconds() * 1.0e3
447 );
448 println!(
449 "prepare once : {:.3} ms",
450 prepared.prepare_seconds() * 1.0e3
451 );
452
453 let mut total_solve_seconds = 0.0f64;
454 for repetition in 1..=args.repeats {
455 let mut x = vec![0.0; matrix.ncols()];
456 let wall_start = Instant::now();
457 let report = prepared.solve(&matrix, &b, &mut x)?;
458 let wall_seconds = wall_start.elapsed().as_secs_f64();
459 let verified = verified_relative_residual(&matrix, &b, &x)?;
460 total_solve_seconds += report.solve_seconds;
461
462 println!();
463 println!("Solve #{repetition}");
464 println!("status : {:?}", report.status);
465 println!("preconditioner : {:?}", report.preconditioner);
466 println!("reused : {}", report.preconditioner_reused);
467 println!("sequence : {}", report.solve_sequence);
468 println!("iterations : {}", report.iterations);
469 println!("reported residual : {:.6e}", report.relative_residual);
470 println!("verified residual : {:.6e}", verified);
471 if generated_rhs {
472 println!("relative x error : {:.6e}", relative_error_to_ones(&x));
473 }
474 println!(
475 "analysis charged : {:.3} ms",
476 report.analysis_seconds * 1.0e3
477 );
478 println!(
479 "prepare charged : {:.3} ms",
480 report.prepare_seconds * 1.0e3
481 );
482 println!(
483 "solve time : {:.3} ms",
484 report.solve_seconds * 1.0e3
485 );
486 println!("call wall : {:.3} ms", wall_seconds * 1.0e3);
487
488 if repetition == 1 && report.preconditioner_reused {
489 return Err("first prepared structural solve unexpectedly reported reuse".into());
490 }
491 if repetition > 1 && !report.preconditioner_reused {
492 return Err(
493 "subsequent prepared structural solve did not report preconditioner reuse".into(),
494 );
495 }
496 if repetition > 1 && (report.analysis_seconds != 0.0 || report.prepare_seconds != 0.0) {
497 return Err(
498 "subsequent prepared structural solve was charged reusable setup cost".into(),
499 );
500 }
501 if !verified.is_finite() {
502 return Err("non-finite independently verified residual".into());
503 }
504 }
505
506 println!();
507 println!("Prepared reuse summary");
508 println!("solve count : {}", prepared.solve_count());
509 println!(
510 "one-time setup : {:.3} ms",
511 (prepared.analysis_seconds() + prepared.prepare_seconds()) * 1.0e3
512 );
513 println!("sum solve time : {:.3} ms", total_solve_seconds * 1.0e3);
514 println!(
515 "amortized total : {:.3} ms/solve",
516 ((prepared.analysis_seconds() + prepared.prepare_seconds() + total_solve_seconds)
517 / args.repeats as f64)
518 * 1.0e3
519 );
520
521 Ok(())
522}Trait Implementations§
Source§impl<'a> Clone for ParallelCsr32Operator<'a>
impl<'a> Clone for ParallelCsr32Operator<'a>
Source§fn clone(&self) -> ParallelCsr32Operator<'a>
fn clone(&self) -> ParallelCsr32Operator<'a>
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreimpl<'a> Copy for ParallelCsr32Operator<'a>
Source§impl<'a> Debug for ParallelCsr32Operator<'a>
impl<'a> Debug for ParallelCsr32Operator<'a>
Auto Trait Implementations§
impl<'a> Freeze for ParallelCsr32Operator<'a>
impl<'a> RefUnwindSafe for ParallelCsr32Operator<'a>
impl<'a> Send for ParallelCsr32Operator<'a>
impl<'a> Sync for ParallelCsr32Operator<'a>
impl<'a> Unpin for ParallelCsr32Operator<'a>
impl<'a> UnsafeUnpin for ParallelCsr32Operator<'a>
impl<'a> UnwindSafe for ParallelCsr32Operator<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more