From 1003ecbcf61bf5d6786175333b7ff7c1a0e7de9c Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 3 Jun 2026 16:23:33 +0100 Subject: [PATCH 1/9] Working ray_benchmark miniapp --- tools/CMakeLists.txt | 1 + tools/ray_benchmark.cpp | 213 ++++++++++++++++++++++++++++++++++++++++ tools/ray_benchmark.h | 71 ++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 tools/ray_benchmark.cpp create mode 100644 tools/ray_benchmark.h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 08004ab3..50a0c650 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,6 +1,7 @@ set(TOOL_NAMES particle_sim ray_fire +ray_benchmark find_volume point_in_volume overlap_check diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp new file mode 100644 index 00000000..5d190ebb --- /dev/null +++ b/tools/ray_benchmark.cpp @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef XDG_OPENMP +#include +#endif + +#include "argparse/argparse.hpp" + +#include "xdg/constants.h" +#include "xdg/error.h" +#include "xdg/timer.h" +#include "xdg/vec3da.h" +#include "xdg/xdg.h" + +#include "ray_benchmark.h" + +using namespace xdg; + +int main(int argc, char** argv) +{ + argparse::ArgumentParser args("XDG Ray Tracing throughput benchmarking tool", + "1.0", + argparse::default_arguments::help); + + args.add_argument("filename") + .help("Path to the input file"); + + args.add_argument("volume") + .help("Volume ID to query") + .scan<'i', int>(); + + args.add_argument("-n", "--num-rays") + .default_value(10'000'000) + .help("Number of rays to cast for the benchmark") + .scan<'u', std::uint32_t>(); + + args.add_argument("-s", "--seed") + .default_value(12345) + .help("Seed for random ray generation") + .scan<'u', std::uint32_t>(); + + args.add_argument("-o", "-p", "--origin", "--position") + .default_value(std::vector{0.0, 0.0, 0.0}) + .help("Ray origin/position") + .scan<'g', double>() + .nargs(3); + + args.add_argument("-m", "--mesh-library") + .help("Mesh library to use. One of (MOAB, LIBMESH)") + .default_value("MOAB"); + + args.add_argument("-rt", "--rt-library") + .help("Ray tracing library to use. Currently implemented: EMBREE") + .default_value("EMBREE"); + + args.add_argument("-l", "--list") + .default_value(false) + .implicit_value(true) + .help("List all volumes in the file and exit"); + + args.add_argument("-sr", "--source-radius") + .default_value(0.0) + .help("Radius of a scattered source around the origin") + .scan<'g', double>(); + + args.add_description( + "Benchmarks ray-fire throughput for a selected mesh volume. A source " + "position is provided and ray directions are randomly generated from it."); + + try { + args.parse_args(argc, argv); + } + catch (const std::runtime_error& err) { + std::cout << err.what() << std::endl; + std::cout << args; + exit(0); + } + + std::string mesh_str = args.get("--mesh-library"); + std::string rt_str = args.get("--rt-library"); + std::string rt_label = rt_str; + + RTLibrary rt_lib; + if (rt_str == "EMBREE") { + rt_lib = RTLibrary::EMBREE; + } else { + fatal_error("Ray tracing library '{}' is not implemented in this benchmark tool yet", rt_str); + } + + MeshLibrary mesh_lib; + if (mesh_str == "MOAB") { + mesh_lib = MeshLibrary::MOAB; + } else if (mesh_str == "LIBMESH") { + mesh_lib = MeshLibrary::LIBMESH; + } else { + fatal_error("Invalid mesh library '{}' specified", mesh_str); + } + + const MeshID volume = args.get("volume"); + const std::size_t num_rays = args.get("--num-rays"); + const std::uint32_t seed = args.get("--seed"); + const Position origin = args.get>("--origin"); + const double source_radius = args.get("--source-radius"); + + Timer wall_timer; + Timer setup_timer; + Timer generation_timer; + Timer trace_timer; + + wall_timer.start(); + + // XDG setup and ray tracer initialisation + setup_timer.start(); + std::shared_ptr xdg = XDG::create(mesh_lib, rt_lib); + const auto& mesh_manager = xdg->mesh_manager(); + mesh_manager->load_file(args.get("filename")); + mesh_manager->init(); + + if (args.get("--list")) { + for (auto mesh_volume : mesh_manager->volumes()) { + std::cout << mesh_volume << std::endl; + } + return 0; + } + + xdg->prepare_volume_for_raytracing(volume); + xdg->ray_tracing_interface()->init(); + setup_timer.stop(); + + if (rt_lib == RTLibrary::EMBREE) { + #ifdef XDG_OPENMP + rt_label += " (" + std::to_string(omp_get_max_threads()) + " CPU threads)"; + #endif + } + + std::cout << "Volume ID: " << volume + << " with: " << mesh_manager->num_volume_faces(volume) + << " faces" << std::endl; + std::cout << "Starting ray fire benchmark with " << num_rays + << " rays using " << rt_label << "\n" << std::endl; + std::cout << "XDG initialisation time = " + << setup_timer.elapsed() << "s" << std::endl; + + if (rt_lib == RTLibrary::EMBREE) { + // Generate random rays from source + generation_timer.start(); + std::vector origins(num_rays); + std::vector directions(num_rays); + + #pragma omp parallel for schedule(static) + for (std::size_t i = 0; i < num_rays; ++i) { + std::uint32_t state = seed ^ static_cast(i); + auto sample = tools::benchmark::random_spherical_source(origin.x, + origin.y, + origin.z, + state, + source_radius); + origins[i] = Position(sample.position[0], + sample.position[1], + sample.position[2]); + directions[i] = Direction(sample.direction[0], + sample.direction[1], + sample.direction[2]); + } + generation_timer.stop(); + + // Trace rays + trace_timer.start(); + + #pragma omp parallel for schedule(static) + for (std::size_t i = 0; i < num_rays; ++i) { + (void) xdg->ray_fire(volume, origins[i], directions[i]); + } + + trace_timer.stop(); + } + + const double generation_time = generation_timer.elapsed(); + const double trace_time = trace_timer.elapsed(); + const double end_to_end_time = generation_time + trace_time; + const double trace_only_rps = trace_time > 0.0 + ? static_cast(num_rays) / trace_time + : 0.0; + const double end_to_end_rps = end_to_end_time > 0.0 + ? static_cast(num_rays) / end_to_end_time + : 0.0; + + wall_timer.stop(); + + std::cout << "Random ray generation time = " + << generation_time << "s" << std::endl; + std::cout << "Generation + tracing time = " + << end_to_end_time << "s" << std::endl; + std::cout << "End-to-end throughput = " + << end_to_end_rps << " rays/s" << std::endl; + std::cout << "Full wall-clock time = " + << wall_timer.elapsed() << "s (post-argparse)" << std::endl; + + std::cout << "----------------------------------------" << std::endl; + std::cout << "Ray tracing time (trace-only)= " + << trace_time << "s for " << num_rays << " rays" << std::endl; + std::cout << "Trace-only throughput = " + << trace_only_rps << " rays/s" << std::endl; + std::cout << "----------------------------------------" << std::endl; + + return 0; +} diff --git a/tools/ray_benchmark.h b/tools/ray_benchmark.h new file mode 100644 index 00000000..65a48339 --- /dev/null +++ b/tools/ray_benchmark.h @@ -0,0 +1,71 @@ +#ifndef _XDG_RAY_BENCHMARK_H +#define _XDG_RAY_BENCHMARK_H + +#include +#include + +namespace xdg::tools::benchmark { + +struct SourceSample { + double position[3]; + double direction[3]; +}; + +#ifdef _OPENMP +#pragma omp declare target +#endif + +inline double rand01(std::uint32_t& state) +{ + state = state * 1664525u + 1013904223u; + return static_cast(state) * (1.0 / 4294967296.0); +} + +inline void random_unit_dir_lcg(std::uint32_t& state, double direction[3]) +{ + double x1; + double x2; + double s; + + do { + x1 = rand01(state) * 2.0 - 1.0; + x2 = rand01(state) * 2.0 - 1.0; + s = x1 * x1 + x2 * x2; + } while (s <= 0.0 || s >= 1.0); + + const double t = 2.0 * std::sqrt(1.0 - s); + direction[0] = x1 * t; + direction[1] = x2 * t; + direction[2] = 1.0 - 2.0 * s; +} + +inline SourceSample random_spherical_source(double origin_x, + double origin_y, + double origin_z, + std::uint32_t state, + double source_radius) +{ + SourceSample sample; + random_unit_dir_lcg(state, sample.direction); + + sample.position[0] = origin_x; + sample.position[1] = origin_y; + sample.position[2] = origin_z; + + if (source_radius > 0.0) { + const double radius = source_radius * std::cbrt(rand01(state)); + sample.position[0] += sample.direction[0] * radius; + sample.position[1] += sample.direction[1] * radius; + sample.position[2] += sample.direction[2] * radius; + } + + return sample; +} + +#ifdef _OPENMP +#pragma omp end declare target +#endif + +} // namespace xdg::tools::benchmark + +#endif // _XDG_RAY_BENCHMARK_H From bfa8ff693d5df7e1a06a970947dc1a1954683403 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 10 Jun 2026 10:12:21 +0100 Subject: [PATCH 2/9] Default origin to model bbox + add flag to use volume bbox --- tools/ray_benchmark.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index 5d190ebb..b9ce753e 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -46,11 +46,15 @@ int main(int argc, char** argv) .scan<'u', std::uint32_t>(); args.add_argument("-o", "-p", "--origin", "--position") - .default_value(std::vector{0.0, 0.0, 0.0}) - .help("Ray origin/position") + .help("Ray origin/position. Defaults to the center of the model bounding box") .scan<'g', double>() .nargs(3); + args.add_argument("--volume-center") + .default_value(false) + .implicit_value(true) + .help("Use the queried volume bounding box center as the ray origin"); + args.add_argument("-m", "--mesh-library") .help("Mesh library to use. One of (MOAB, LIBMESH)") .default_value("MOAB"); @@ -105,7 +109,6 @@ int main(int argc, char** argv) const MeshID volume = args.get("volume"); const std::size_t num_rays = args.get("--num-rays"); const std::uint32_t seed = args.get("--seed"); - const Position origin = args.get>("--origin"); const double source_radius = args.get("--source-radius"); Timer wall_timer; @@ -129,6 +132,20 @@ int main(int argc, char** argv) return 0; } + const auto origin_arg = args.present>("--origin"); + bool use_volume_center = args.get("--volume-center"); + if (origin_arg && use_volume_center) { + warning("--volume-center enabled but an explicit origin was also provided. The explicit origin will be used and the volume center will be ignored."); + use_volume_center = false; + } + + Position origin = mesh_manager->global_bounding_box().center(); + if (origin_arg) { + origin = Position(origin_arg.value()); + } else if (use_volume_center) { + origin = mesh_manager->volume_bounding_box(volume).center(); + } + xdg->prepare_volume_for_raytracing(volume); xdg->ray_tracing_interface()->init(); setup_timer.stop(); From 1b9e5035dafee991a03a814bf9ee2a463295283c Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 10 Jun 2026 10:49:18 +0100 Subject: [PATCH 3/9] Added fmt delimitting + spacing for volume listing --- tools/ray_benchmark.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index b9ce753e..ff980bbc 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -19,6 +19,8 @@ #include "xdg/xdg.h" #include "ray_benchmark.h" +#include + using namespace xdg; @@ -126,9 +128,7 @@ int main(int argc, char** argv) mesh_manager->init(); if (args.get("--list")) { - for (auto mesh_volume : mesh_manager->volumes()) { - std::cout << mesh_volume << std::endl; - } + std::cout << "[" << fmt::format("{}", fmt::join(mesh_manager->volumes(), ", ")) << "]\n"; return 0; } From fc464158d36727a598b22ce4048a2bfe8ae9c53e Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 10 Jun 2026 11:21:56 +0100 Subject: [PATCH 4/9] Attempting to make use of XDGConfig and failing... --- tools/ray_benchmark.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index ff980bbc..345c73fa 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -6,12 +6,10 @@ #include #include -#ifdef XDG_OPENMP -#include -#endif - #include "argparse/argparse.hpp" +#include +#include "xdg/config.h" #include "xdg/constants.h" #include "xdg/error.h" #include "xdg/timer.h" @@ -19,7 +17,6 @@ #include "xdg/xdg.h" #include "ray_benchmark.h" -#include using namespace xdg; @@ -149,11 +146,9 @@ int main(int argc, char** argv) xdg->prepare_volume_for_raytracing(volume); xdg->ray_tracing_interface()->init(); setup_timer.stop(); - if (rt_lib == RTLibrary::EMBREE) { - #ifdef XDG_OPENMP - rt_label += " (" + std::to_string(omp_get_max_threads()) + " CPU threads)"; - #endif + rt_label += " (" + std::to_string(XDGConfig::config().n_threads()) + + " CPU threads)"; } std::cout << "Volume ID: " << volume From 8c148a0b9bc9a0807ea1c4f16373e84014493a23 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 10 Jun 2026 13:34:30 +0100 Subject: [PATCH 5/9] Added a flag to specify human readable stdout or csv --- tools/ray_benchmark.cpp | 110 +++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 25 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index 345c73fa..47f4c62f 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -23,7 +24,7 @@ using namespace xdg; int main(int argc, char** argv) { - argparse::ArgumentParser args("XDG Ray Tracing throughput benchmarking tool", + argparse::ArgumentParser args("XDG Raytracing throughput benchmarking tool", "1.0", argparse::default_arguments::help); @@ -72,6 +73,11 @@ int main(int argc, char** argv) .help("Radius of a scattered source around the origin") .scan<'g', double>(); + args.add_argument("--format") + .default_value("human") + .choices("human", "csv") + .help("stdout format. Human readable (default) or csv"); + args.add_description( "Benchmarks ray-fire throughput for a selected mesh volume. A source " "position is provided and ray directions are randomly generated from it."); @@ -106,9 +112,12 @@ int main(int argc, char** argv) } const MeshID volume = args.get("volume"); + const std::string model_filename = args.get("filename"); + const std::string model_name = std::filesystem::path(model_filename).filename().string(); const std::size_t num_rays = args.get("--num-rays"); const std::uint32_t seed = args.get("--seed"); const double source_radius = args.get("--source-radius"); + const std::string output_format = args.get("--format"); Timer wall_timer; Timer setup_timer; @@ -121,7 +130,7 @@ int main(int argc, char** argv) setup_timer.start(); std::shared_ptr xdg = XDG::create(mesh_lib, rt_lib); const auto& mesh_manager = xdg->mesh_manager(); - mesh_manager->load_file(args.get("filename")); + mesh_manager->load_file(model_filename); mesh_manager->init(); if (args.get("--list")) { @@ -151,13 +160,7 @@ int main(int argc, char** argv) + " CPU threads)"; } - std::cout << "Volume ID: " << volume - << " with: " << mesh_manager->num_volume_faces(volume) - << " faces" << std::endl; - std::cout << "Starting ray fire benchmark with " << num_rays - << " rays using " << rt_label << "\n" << std::endl; - std::cout << "XDG initialisation time = " - << setup_timer.elapsed() << "s" << std::endl; + const auto num_faces = mesh_manager->num_volume_faces(volume); if (rt_lib == RTLibrary::EMBREE) { // Generate random rays from source @@ -196,6 +199,7 @@ int main(int argc, char** argv) const double generation_time = generation_timer.elapsed(); const double trace_time = trace_timer.elapsed(); const double end_to_end_time = generation_time + trace_time; + const double setup_time = setup_timer.elapsed(); const double trace_only_rps = trace_time > 0.0 ? static_cast(num_rays) / trace_time : 0.0; @@ -204,22 +208,78 @@ int main(int argc, char** argv) : 0.0; wall_timer.stop(); - - std::cout << "Random ray generation time = " - << generation_time << "s" << std::endl; - std::cout << "Generation + tracing time = " - << end_to_end_time << "s" << std::endl; - std::cout << "End-to-end throughput = " - << end_to_end_rps << " rays/s" << std::endl; - std::cout << "Full wall-clock time = " - << wall_timer.elapsed() << "s (post-argparse)" << std::endl; - - std::cout << "----------------------------------------" << std::endl; - std::cout << "Ray tracing time (trace-only)= " - << trace_time << "s for " << num_rays << " rays" << std::endl; - std::cout << "Trace-only throughput = " - << trace_only_rps << " rays/s" << std::endl; - std::cout << "----------------------------------------" << std::endl; + const double wall_time = wall_timer.elapsed(); + + const std::vector csv_columns { + "model", + "mesh_library", + "rt_library", + "volume", + "num_faces", + "num_rays", + "seed", + "source_radius", + "origin_x", + "origin_y", + "origin_z", + "n_threads", + "initialisation_time_s", + "generation_time_s", + "trace_time_s", + "generation_trace_time_s", + "end_to_end_throughput_rays_per_s", + "trace_only_throughput_rays_per_s", + "wall_time_s" + }; + + const std::vector csv_values { + model_name, + mesh_str, + rt_str, + fmt::format("{}", volume), + fmt::format("{}", num_faces), + fmt::format("{}", num_rays), + fmt::format("{}", seed), + fmt::format("{}", source_radius), + fmt::format("{}", origin.x), + fmt::format("{}", origin.y), + fmt::format("{}", origin.z), + fmt::format("{}", XDGConfig::config().n_threads()), + fmt::format("{}", setup_time), + fmt::format("{}", generation_time), + fmt::format("{}", trace_time), + fmt::format("{}", end_to_end_time), + fmt::format("{}", end_to_end_rps), + fmt::format("{}", trace_only_rps), + fmt::format("{}", wall_time) + }; + + if (output_format == "csv") { + std::cout << fmt::format("{}\n", fmt::join(csv_columns, ",")); + std::cout << fmt::format("{}\n", fmt::join(csv_values, ",")); + } else { + std::cout << "XDG ray benchmark\n"; + std::cout << "----------------------------------------\n"; + std::cout << "Model : " << model_name << "\n"; + std::cout << "Mesh library : " << mesh_str << "\n"; + std::cout << "Ray tracing library : " << rt_label << "\n"; + std::cout << "Volume : " << volume << "\n"; + std::cout << "Volume faces : " << num_faces << "\n"; + std::cout << "Rays : " << num_rays << "\n"; + std::cout << "Seed : " << seed << "\n"; + std::cout << "Source radius : " << source_radius << "\n"; + std::cout << "Origin : " + << origin.x << ", " << origin.y << ", " << origin.z << "\n"; + std::cout << "----------------------------------------\n"; + std::cout << "Initialisation time : " << setup_time << " s\n"; + std::cout << "Ray generation time : " << generation_time << " s\n"; + std::cout << "Ray tracing time : " << trace_time << " s\n"; + std::cout << "Generation + tracing : " << end_to_end_time << " s\n"; + std::cout << "Full wall-clock time : " << wall_time << " s\n"; + std::cout << "----------------------------------------\n"; + std::cout << "End-to-end throughput : " << end_to_end_rps << " rays/s\n"; + std::cout << "Trace-only throughput : " << trace_only_rps << " rays/s\n"; + } return 0; } From ca884e49d1aede21f1eedc936028179640666517 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 10 Jun 2026 13:38:25 +0100 Subject: [PATCH 6/9] Set omp pragma schedule to runtime --- tools/ray_benchmark.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index 47f4c62f..81296b89 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -168,7 +168,7 @@ int main(int argc, char** argv) std::vector origins(num_rays); std::vector directions(num_rays); - #pragma omp parallel for schedule(static) + #pragma omp parallel for schedule(runtime) for (std::size_t i = 0; i < num_rays; ++i) { std::uint32_t state = seed ^ static_cast(i); auto sample = tools::benchmark::random_spherical_source(origin.x, @@ -188,7 +188,7 @@ int main(int argc, char** argv) // Trace rays trace_timer.start(); - #pragma omp parallel for schedule(static) + #pragma omp parallel for schedule(runtime) for (std::size_t i = 0; i < num_rays; ++i) { (void) xdg->ray_fire(volume, origins[i], directions[i]); } From 187caf6f24ba5fdc738f7373948dca4982f36a9c Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Fri, 12 Jun 2026 10:25:19 +0100 Subject: [PATCH 7/9] Changed XDGConfig::initialize() to set n_threads = omp_get_max_threads() --- src/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.cpp b/src/config.cpp index 7f534ed6..dbd50466 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -38,7 +38,7 @@ void XDGConfig::initialize() { // libMesh respects the OpenMP settings of the host application if (n_threads() == -1) { #ifdef XDG_HAVE_OPENMP - set_n_threads(omp_get_num_threads()); + set_n_threads(omp_get_max_threads()); #else set_n_threads(1); #endif From 2f4741771f64c63d28836c4508611a5c547a8741 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Tue, 16 Jun 2026 11:38:44 +0100 Subject: [PATCH 8/9] Output warnings to indicate ray origin(s) are outside the volume --- tools/ray_benchmark.cpp | 75 ++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index 81296b89..856d25e0 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -154,7 +154,23 @@ int main(int argc, char** argv) xdg->prepare_volume_for_raytracing(volume); xdg->ray_tracing_interface()->init(); + + const bool origin_in_volume = xdg->point_in_volume(volume, origin); + if (!origin_in_volume) { + const std::string origin_label = source_radius > 0.0 ? "source center" : "ray origin"; + warning(fmt::format("The {} ({}, {}, {}) is not inside volume {}. Results may be skewed by fewer intersections with the BVH.", + origin_label, origin.x, origin.y, origin.z, volume)); + } else { + const double nearest_surface_distance = xdg->closest_distance(volume, origin); + if (source_radius > nearest_surface_distance) { + warning(fmt::format("The source radius ({}) is larger than the nearest surface distance ({}) from the source center to volume {}. " + "Some sampled source points may be outside the volume.", + source_radius, nearest_surface_distance, volume)); + } + } + setup_timer.stop(); + if (rt_lib == RTLibrary::EMBREE) { rt_label += " (" + std::to_string(XDGConfig::config().n_threads()) + " CPU threads)"; @@ -162,40 +178,39 @@ int main(int argc, char** argv) const auto num_faces = mesh_manager->num_volume_faces(volume); - if (rt_lib == RTLibrary::EMBREE) { - // Generate random rays from source - generation_timer.start(); - std::vector origins(num_rays); - std::vector directions(num_rays); - - #pragma omp parallel for schedule(runtime) - for (std::size_t i = 0; i < num_rays; ++i) { - std::uint32_t state = seed ^ static_cast(i); - auto sample = tools::benchmark::random_spherical_source(origin.x, - origin.y, - origin.z, - state, - source_radius); - origins[i] = Position(sample.position[0], - sample.position[1], - sample.position[2]); - directions[i] = Direction(sample.direction[0], - sample.direction[1], - sample.direction[2]); - } - generation_timer.stop(); - // Trace rays - trace_timer.start(); + // Generate random rays from source + generation_timer.start(); + std::vector origins(num_rays); + std::vector directions(num_rays); + + #pragma omp parallel for schedule(runtime) + for (std::size_t i = 0; i < num_rays; ++i) { + std::uint32_t state = seed ^ static_cast(i); + auto sample = tools::benchmark::random_spherical_source(origin.x, + origin.y, + origin.z, + state, + source_radius); + origins[i] = Position(sample.position[0], + sample.position[1], + sample.position[2]); + directions[i] = Direction(sample.direction[0], + sample.direction[1], + sample.direction[2]); + } + generation_timer.stop(); - #pragma omp parallel for schedule(runtime) - for (std::size_t i = 0; i < num_rays; ++i) { - (void) xdg->ray_fire(volume, origins[i], directions[i]); - } + // Trace rays + trace_timer.start(); - trace_timer.stop(); + #pragma omp parallel for schedule(runtime) + for (std::size_t i = 0; i < num_rays; ++i) { + (void) xdg->ray_fire(volume, origins[i], directions[i]); } + trace_timer.stop(); + const double generation_time = generation_timer.elapsed(); const double trace_time = trace_timer.elapsed(); const double end_to_end_time = generation_time + trace_time; @@ -258,7 +273,7 @@ int main(int argc, char** argv) std::cout << fmt::format("{}\n", fmt::join(csv_columns, ",")); std::cout << fmt::format("{}\n", fmt::join(csv_values, ",")); } else { - std::cout << "XDG ray benchmark\n"; + std::cout << "\nXDG ray benchmark\n"; std::cout << "----------------------------------------\n"; std::cout << "Model : " << model_name << "\n"; std::cout << "Mesh library : " << mesh_str << "\n"; From e83edbf6975ae609f95a88672c3a114904425022 Mon Sep 17 00:00:00 2001 From: waqar-ukaea Date: Wed, 17 Jun 2026 12:39:03 +0100 Subject: [PATCH 9/9] Count hits and misses and caclulate hit fraction + updates to outputting --- tools/ray_benchmark.cpp | 62 ++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/tools/ray_benchmark.cpp b/tools/ray_benchmark.cpp index 856d25e0..dea28351 100644 --- a/tools/ray_benchmark.cpp +++ b/tools/ray_benchmark.cpp @@ -204,13 +204,20 @@ int main(int argc, char** argv) // Trace rays trace_timer.start(); - #pragma omp parallel for schedule(runtime) + std::size_t num_hits = 0; + + #pragma omp parallel for schedule(runtime) reduction(+:num_hits) for (std::size_t i = 0; i < num_rays; ++i) { - (void) xdg->ray_fire(volume, origins[i], directions[i]); + const auto hit = xdg->ray_fire(volume, origins[i], directions[i]); + if (hit.second != ID_NONE) num_hits++; } trace_timer.stop(); + const std::size_t num_misses = num_rays - num_hits; + const double hit_fraction = num_rays > 0 + ? static_cast(num_hits) / static_cast(num_rays) + : 0.0; const double generation_time = generation_timer.elapsed(); const double trace_time = trace_timer.elapsed(); const double end_to_end_time = generation_time + trace_time; @@ -232,6 +239,9 @@ int main(int argc, char** argv) "volume", "num_faces", "num_rays", + "num_hits", + "num_misses", + "hit_fraction", "seed", "source_radius", "origin_x", @@ -254,6 +264,9 @@ int main(int argc, char** argv) fmt::format("{}", volume), fmt::format("{}", num_faces), fmt::format("{}", num_rays), + fmt::format("{}", num_hits), + fmt::format("{}", num_misses), + fmt::format("{}", hit_fraction), fmt::format("{}", seed), fmt::format("{}", source_radius), fmt::format("{}", origin.x), @@ -273,27 +286,36 @@ int main(int argc, char** argv) std::cout << fmt::format("{}\n", fmt::join(csv_columns, ",")); std::cout << fmt::format("{}\n", fmt::join(csv_values, ",")); } else { - std::cout << "\nXDG ray benchmark\n"; + std::cout << "\nXDG ray benchmark results\n"; + std::cout << "----------------------------------------\n"; + std::cout << "Model : " << model_name << "\n"; + std::cout << "Mesh library : " << mesh_str << "\n"; + std::cout << "Ray tracing library : " << rt_label << "\n"; + std::cout << "Volume : " << volume << "\n"; + std::cout << "Volume faces : " << num_faces << "\n"; + std::cout << "Seed : " << seed << "\n"; + std::cout << "Rays : " << num_rays << "\n"; + if (source_radius != 0.0) { + std::cout << "Source center : " + << origin.x << ", " << origin.y << ", " << origin.z << "\n"; + std::cout << "Source radius : " << source_radius << "\n"; + } else { + std::cout << "Origin (fixed) : " + << origin.x << ", " << origin.y << ", " << origin.z << "\n"; + } std::cout << "----------------------------------------\n"; - std::cout << "Model : " << model_name << "\n"; - std::cout << "Mesh library : " << mesh_str << "\n"; - std::cout << "Ray tracing library : " << rt_label << "\n"; - std::cout << "Volume : " << volume << "\n"; - std::cout << "Volume faces : " << num_faces << "\n"; - std::cout << "Rays : " << num_rays << "\n"; - std::cout << "Seed : " << seed << "\n"; - std::cout << "Source radius : " << source_radius << "\n"; - std::cout << "Origin : " - << origin.x << ", " << origin.y << ", " << origin.z << "\n"; + std::cout << "Hits : " << num_hits << "\n"; + std::cout << "Misses : " << num_misses << "\n"; + std::cout << "Hit fraction : " << hit_fraction << "\n"; std::cout << "----------------------------------------\n"; - std::cout << "Initialisation time : " << setup_time << " s\n"; - std::cout << "Ray generation time : " << generation_time << " s\n"; - std::cout << "Ray tracing time : " << trace_time << " s\n"; - std::cout << "Generation + tracing : " << end_to_end_time << " s\n"; - std::cout << "Full wall-clock time : " << wall_time << " s\n"; + std::cout << "Initialisation time : " << setup_time << " s\n"; + std::cout << "Ray generation time : " << generation_time << " s\n"; + std::cout << "Ray tracing time : " << trace_time << " s\n"; + std::cout << "Generation + tracing : " << end_to_end_time << " s\n"; + std::cout << "Full wall-clock time : " << wall_time << " s\n"; std::cout << "----------------------------------------\n"; - std::cout << "End-to-end throughput : " << end_to_end_rps << " rays/s\n"; - std::cout << "Trace-only throughput : " << trace_only_rps << " rays/s\n"; + std::cout << "End-to-end throughput : " << end_to_end_rps << " rays/s\n"; + std::cout << "Trace-only throughput : " << trace_only_rps << " rays/s\n"; } return 0;