From dec18a38d4d9feeea4f6354b7a2737f5f7e0333b Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 26 Mar 2026 15:31:18 +0100 Subject: [PATCH 01/56] running test prim_to_cons, (pre cleanup) --- .gitignore | 1 + CMakeLists.txt | 2 + euler_operators/perfect_gas.hpp | 5 ++ euler_operators/prim_to_cons.hpp | 95 ++++++++++++++++++++++ setups/ruche/skx/prepare.sh | 11 +++ setups/ruche/skx/run.sh | 7 +- plot.py => simulations/plot.py | 0 test/CMakeLists.txt | 18 +++++ test/test_main.cpp | 22 ++++++ test/test_prim_to_cons.cpp | 130 +++++++++++++++++++++++++++++++ 10 files changed, 287 insertions(+), 4 deletions(-) rename plot.py => simulations/plot.py (100%) create mode 100644 test/CMakeLists.txt create mode 100644 test/test_main.cpp create mode 100644 test/test_prim_to_cons.cpp diff --git a/.gitignore b/.gitignore index 049caff..ea85aa9 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ __pycache__/ slurm_out/ results/ build*/ +.cache/ compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt index e1a3205..7de2ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ find_package(Kokkos REQUIRED) add_subdirectory(benchmarks) +add_subdirectory(test) + add_subdirectory(euler_operators) add_subdirectory(simulations) diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 8f3fbe7..fbaf8d7 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -12,6 +12,11 @@ class PerfectGas public: explicit PerfectGas(T const gamma) : m_gamma(gamma) {} + KOKKOS_FUNCTION T gamma() const noexcept + { + return m_gamma; + } + KOKKOS_FUNCTION T speed_of_sound(T const density, T const pressure) const noexcept { return Kokkos::sqrt(m_gamma * pressure / density); diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 4f1717c..010ea82 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -1,6 +1,9 @@ #pragma once #include +#include +#include +#include #include #include @@ -31,3 +34,95 @@ void prim_to_cons( store(cons, cons_arrays, i, j, k); }); } + + +template +void prim_to_cons_vec( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + PerfectGas const& eos) +{ + namespace KE = Kokkos::Experimental; + using simd_t = KE::simd; + constexpr IndexType simd_width = simd_t::size(); + + IndexType const nx = prim_arrays.d.extent(0); + IndexType const ny = prim_arrays.d.extent(1); + IndexType const nz = prim_arrays.d.extent(2); + IndexType const simd_end = (nx / simd_width) * simd_width; + + T const* pd = prim_arrays.d.data_handle(); + T const* pp = prim_arrays.p.data_handle(); + T const* pu0 = prim_arrays.ux0.data_handle(); + T const* pu1 = prim_arrays.ux1.data_handle(); + T const* pu2 = prim_arrays.ux2.data_handle(); + + T* cd = cons_arrays.d.data_handle(); + T* ce = cons_arrays.e.data_handle(); + T* cm0 = cons_arrays.mx0.data_handle(); + T* cm1 = cons_arrays.mx1.data_handle(); + T* cm2 = cons_arrays.mx2.data_handle(); + + simd_t const gamma_minus_one_inv = 1 / (eos.gamma() - 1); // hoist EOS constant + Kokkos::parallel_for( + "prim_to_cons_simd", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx / simd_width, ny, nz}), + KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { + IndexType const base = (i * simd_width) + (nx * j) + (nx * ny * k); + + simd_t const d(pd + base, KE::simd_flag_default); + simd_t const p(pp + base, KE::simd_flag_default); + simd_t const ux0(pu0 + base, KE::simd_flag_default); + simd_t const ux1(pu1 + base, KE::simd_flag_default); + simd_t const ux2(pu2 + base, KE::simd_flag_default); + auto c_simd = {d, p, ux0, ux1, ux2}; + + simd_t const int_e = p * gamma_minus_one_inv; + + simd_t const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / 2; + + d.copy_to(cd + base, KE::simd_flag_default); + (e_kin + int_e).copy_to(ce + base, KE::simd_flag_default); + (d * ux0).copy_to(cm0 + base, KE::simd_flag_default); + (d * ux1).copy_to(cm1 + base, KE::simd_flag_default); + (d * ux2).copy_to(cm2 + base, KE::simd_flag_default); + // KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); + // KE::simd_unchecked_store(e_kin + int_e, ce + base, KE::simd_flag_default); + // KE::simd_unchecked_store(d * ux0, cm0 + base, KE::simd_flag_default); + // KE::simd_unchecked_store(d * ux1, cm1 + base, KE::simd_flag_default); + // KE::simd_unchecked_store(d * ux2, cm2 + base, KE::simd_flag_default); + }); + + // scalar remainder for when nx % simd_width != 0 + // Kokkos::parallel_for( + // "prim_to_cons_remainder", + // Kokkos::MDRangePolicy< + // Kokkos::Rank<2, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + // Kokkos::IndexType>(exec_space, {0, 0}, {ny, nz}), + // KOKKOS_LAMBDA(IndexType const j, IndexType const k) { + // for (IndexType i = simd_end; i < nx; ++i) { + // IndexType const base = i + (nx * j) + (nx * ny * k); + // T const d = pd[base]; + // T const p = pp[base]; + // T const ux0 = pu0[base]; + // T const ux1 = pu1[base]; + // T const ux2 = pu2[base]; + // T const int_e = p * gamma_minus_one_inv; + // T const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / 2; + // cd[base] = d; + // ce[base] = e_kin + int_e; + // cm0[base] = d * ux0; + // cm1[base] = d * ux1; + // cm2[base] = d * ux2; + // } + // }); +} diff --git a/setups/ruche/skx/prepare.sh b/setups/ruche/skx/prepare.sh index 895906b..671dcdf 100755 --- a/setups/ruche/skx/prepare.sh +++ b/setups/ruche/skx/prepare.sh @@ -9,6 +9,7 @@ module load \ export install_dir=$PWD/opt/skx export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git cmake \ @@ -35,5 +36,15 @@ cmake --build build-kokkos cmake --install build-kokkos --prefix $Kokkos_ROOT rm -rf build-kokkos kokkos +git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest +cmake --install build-gtest --prefix $gtest_ROOT +rm -rf build-gtest googletest + cmake -D CMAKE_BUILD_TYPE=Release -B build-skx cmake --build build-skx diff --git a/setups/ruche/skx/run.sh b/setups/ruche/skx/run.sh index 0367e24..c0d325f 100644 --- a/setups/ruche/skx/run.sh +++ b/setups/ruche/skx/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -#SBATCH --job-name=benchmark_skx +#SBATCH --job-name=test_skx #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=20 @@ -21,6 +21,5 @@ echo "RESULT_NAME =" "$RESULT_NAME" mkdir -p slurm_out results/ruche/skx -./build-skx/benchmarks/euler_benchmarks \ - --benchmark_out_format=json \ - --benchmark_out="./results/ruche/skx/[${SLURM_JOB_ID}]_${RESULT_NAME}_bm_skx.json" +# ./build-skx/simulations/euler_simulation +./build-skx/test/euler_tests diff --git a/plot.py b/simulations/plot.py similarity index 100% rename from plot.py rename to simulations/plot.py diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..b806972 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,18 @@ + +find_package(GTest REQUIRED) + +add_executable(euler_tests) +target_link_libraries( + euler_tests + PRIVATE GTest::gtest_main euler_operators Kokkos::kokkos +) +target_sources( + euler_tests + PRIVATE + test_main.cpp + test_prim_to_cons.cpp +) + +enable_testing() +include(GoogleTest) +gtest_discover_tests(euler_tests) diff --git a/test/test_main.cpp b/test/test_main.cpp new file mode 100644 index 0000000..dab222a --- /dev/null +++ b/test/test_main.cpp @@ -0,0 +1,22 @@ +#include + +#include + +#include +#include + +TEST(DummyTest, AlwaysPasses) +{ + EXPECT_EQ(1, 1); +} +int main(int argc, char** argv) +{ + int ret = -1; + Kokkos::initialize(argc, argv); + { + ::testing::InitGoogleTest(&argc, argv); + ret = RUN_ALL_TESTS(); + } + Kokkos::finalize(); + return ret; +} diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp new file mode 100644 index 0000000..daa6d61 --- /dev/null +++ b/test/test_prim_to_cons.cpp @@ -0,0 +1,130 @@ +#include + +#include +#include +#include +#include + +template < + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class LP, + class AP> +void init_from_value_test( + Kokkos::DefaultExecutionSpace const& exec_space, + Kokkos::mdspan, LP, AP> const& array, + ElementType const& value) +{ + Kokkos::parallel_for( + "init_from_value", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>( + exec_space, + {0, 0, 0}, + {array.extent(0), array.extent(1), array.extent(2)}), + KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { + array(i, j, k) = value; + }); +} + + + +template < + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class LP, + class AP> +void init_from_state_test( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays< + Kokkos::mdspan, LP, AP>> const& + prim_arrays, + EulerPrim const& prim) +{ + init_from_value_test(exec_space, prim_arrays.d, prim.d); + init_from_value_test(exec_space, prim_arrays.p, prim.p); + init_from_value_test(exec_space, prim_arrays.ux0, prim.ux0); + init_from_value_test(exec_space, prim_arrays.ux1, prim.ux1); + init_from_value_test(exec_space, prim_arrays.ux2, prim.ux2); +} +// include your headers + +TEST(PrimToCons, ScalarVsVectorized) +{ + using real_t = double; + using index_t = int; + + int const n = 16; // keep small for unit test + + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas eos(1.4); + + // --- allocate --- + auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto cons_alloc_ref = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_alloc_vec = create_cons_arrays_1d(exec_space, n * n * n); + + auto prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + + auto cons_ref = to_mdspan, + Kokkos::layout_left>>(cons_alloc_ref, n, n, n); + + auto cons_vec = to_mdspan, + Kokkos::layout_left>>(cons_alloc_vec, n, n, n); + + // --- initialize with non-trivial state --- + EulerPrim prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + + init_from_state_test(exec_space, prim_arrays, prim); + exec_space.fence(); + + // --- run both implementations --- + prim_to_cons(exec_space, as_const(prim_arrays), cons_ref, eos); + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_vec, eos); + exec_space.fence(); + + // --- compare --- + auto ref_h = EulerConsArrays { + .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.d), + .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx0), + .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx1), + .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx2), + .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.e)}; + + auto vec_h = EulerConsArrays { + .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.d), + .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx0), + .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx1), + .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx2), + .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.e)}; + + + double const tol = 1e-12; + + + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + for (int k = 0; k < n; ++k) { + int idx = i + (n * (j + n * k)); // layout_left flattening + + ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); + ASSERT_NEAR(ref_h.mx0(idx), vec_h.mx0(idx), tol); + ASSERT_NEAR(ref_h.mx1(idx), vec_h.mx1(idx), tol); + ASSERT_NEAR(ref_h.mx2(idx), vec_h.mx2(idx), tol); + ASSERT_NEAR(ref_h.e(idx), vec_h.e(idx), tol); + } +} From 8ffb5b4532c580ab31a68753cb2371f689a516bf Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 26 Mar 2026 17:04:25 +0100 Subject: [PATCH 02/56] gtest for small N=16 testcase prim_to_cons --- .gitignore | 1 + euler_operators/prim_to_cons.hpp | 90 +++++++++++++++----------------- setups/ruche/skx/run.sh | 11 ++-- simulations/display_results.py | 69 +++++++++++++++++++++++- test/test_main.cpp | 5 +- test/test_prim_to_cons.cpp | 16 +++--- 6 files changed, 125 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index ea85aa9..aa38d13 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ slurm_out/ results/ build*/ .cache/ +npy/ compile_commands.json diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 010ea82..05cc99b 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -35,7 +35,6 @@ void prim_to_cons( }); } - template void prim_to_cons_vec( Kokkos::DefaultExecutionSpace const& exec_space, @@ -50,13 +49,12 @@ void prim_to_cons_vec( PerfectGas const& eos) { namespace KE = Kokkos::Experimental; - using simd_t = KE::simd; + using simd_t = KE::simd; constexpr IndexType simd_width = simd_t::size(); IndexType const nx = prim_arrays.d.extent(0); IndexType const ny = prim_arrays.d.extent(1); IndexType const nz = prim_arrays.d.extent(2); - IndexType const simd_end = (nx / simd_width) * simd_width; T const* pd = prim_arrays.d.data_handle(); T const* pp = prim_arrays.p.data_handle(); @@ -70,59 +68,57 @@ void prim_to_cons_vec( T* cm1 = cons_arrays.mx1.data_handle(); T* cm2 = cons_arrays.mx2.data_handle(); - simd_t const gamma_minus_one_inv = 1 / (eos.gamma() - 1); // hoist EOS constant - Kokkos::parallel_for( - "prim_to_cons_simd", - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx / simd_width, ny, nz}), - KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { - IndexType const base = (i * simd_width) + (nx * j) + (nx * ny * k); + simd_t const gamma_minus_one_inv = T(1) / (eos.gamma() - T(1)); - simd_t const d(pd + base, KE::simd_flag_default); - simd_t const p(pp + base, KE::simd_flag_default); - simd_t const ux0(pu0 + base, KE::simd_flag_default); - simd_t const ux1(pu1 + base, KE::simd_flag_default); - simd_t const ux2(pu2 + base, KE::simd_flag_default); - auto c_simd = {d, p, ux0, ux1, ux2}; + IndexType const nx_blocks = nx / simd_width; - simd_t const int_e = p * gamma_minus_one_inv; + Kokkos::parallel_for( + "prim_to_cons_vec", + Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { + IndexType const base = bi * simd_width + nx * j + nx * ny * k; + + // load SIMD lanes + simd_t d(pd + base, KE::simd_flag_default); + simd_t p(pp + base, KE::simd_flag_default); + simd_t ux0(pu0 + base, KE::simd_flag_default); + simd_t ux1(pu1 + base, KE::simd_flag_default); + simd_t ux2(pu2 + base, KE::simd_flag_default); - simd_t const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / 2; + simd_t int_e = p * gamma_minus_one_inv; + simd_t e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); d.copy_to(cd + base, KE::simd_flag_default); (e_kin + int_e).copy_to(ce + base, KE::simd_flag_default); (d * ux0).copy_to(cm0 + base, KE::simd_flag_default); (d * ux1).copy_to(cm1 + base, KE::simd_flag_default); (d * ux2).copy_to(cm2 + base, KE::simd_flag_default); - // KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); - // KE::simd_unchecked_store(e_kin + int_e, ce + base, KE::simd_flag_default); - // KE::simd_unchecked_store(d * ux0, cm0 + base, KE::simd_flag_default); - // KE::simd_unchecked_store(d * ux1, cm1 + base, KE::simd_flag_default); - // KE::simd_unchecked_store(d * ux2, cm2 + base, KE::simd_flag_default); }); - // scalar remainder for when nx % simd_width != 0 - // Kokkos::parallel_for( - // "prim_to_cons_remainder", - // Kokkos::MDRangePolicy< - // Kokkos::Rank<2, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - // Kokkos::IndexType>(exec_space, {0, 0}, {ny, nz}), - // KOKKOS_LAMBDA(IndexType const j, IndexType const k) { - // for (IndexType i = simd_end; i < nx; ++i) { - // IndexType const base = i + (nx * j) + (nx * ny * k); - // T const d = pd[base]; - // T const p = pp[base]; - // T const ux0 = pu0[base]; - // T const ux1 = pu1[base]; - // T const ux2 = pu2[base]; - // T const int_e = p * gamma_minus_one_inv; - // T const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / 2; - // cd[base] = d; - // ce[base] = e_kin + int_e; - // cm0[base] = d * ux0; - // cm1[base] = d * ux1; - // cm2[base] = d * ux2; - // } - // }); + IndexType const rem_start = nx_blocks * simd_width; + if (rem_start < nx) { + Kokkos::parallel_for( + "prim_to_cons_vec_remainder", + Kokkos::MDRangePolicy>({rem_start, 0, 0}, {nx, ny, nz}), + KOKKOS_LAMBDA(IndexType i, IndexType j, IndexType k) { + IndexType const base = i + nx * j + nx * ny * k; + T const d = pd[base]; + T const p = pp[base]; + T const ux0 = pu0[base]; + T const ux1 = pu1[base]; + T const ux2 = pu2[base]; + + T const int_e = p * (T(1) / (eos.gamma() - T(1))); + T const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); + + cd[base] = d; + ce[base] = e_kin + int_e; + cm0[base] = d * ux0; + cm1[base] = d * ux1; + cm2[base] = d * ux2; + }); + } } diff --git a/setups/ruche/skx/run.sh b/setups/ruche/skx/run.sh index c0d325f..f9b5e51 100644 --- a/setups/ruche/skx/run.sh +++ b/setups/ruche/skx/run.sh @@ -1,5 +1,5 @@ #!/bin/bash -#SBATCH --job-name=test_skx +#SBATCH --job-name=simulation_skx #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=20 @@ -14,12 +14,7 @@ module load \ set -x cd ${SLURM_SUBMIT_DIR} -# $1 = optional base name -RESULT_NAME=${1:-"unnamed"} - -echo "RESULT_NAME =" "$RESULT_NAME" - mkdir -p slurm_out results/ruche/skx -# ./build-skx/simulations/euler_simulation -./build-skx/test/euler_tests +./build-skx/simulations/euler_simulation +# ./build-skx/test/euler_test diff --git a/simulations/display_results.py b/simulations/display_results.py index e069a79..f88fe37 100644 --- a/simulations/display_results.py +++ b/simulations/display_results.py @@ -19,5 +19,72 @@ def main(): plt.show() +import argparse +import numpy as np +import matplotlib.pyplot as plt +import glob +import os + +def main_loop(): + parser = argparse.ArgumentParser(description="Display a sequence of .npy files.") + parser.add_argument("path", type=str, help="Directory or glob pattern (e.g. './*.npy')") + parser.add_argument("--delay", type=float, default=0.5, help="Delay between frames (seconds)") + args = parser.parse_args() + + # Resolve files + if os.path.isdir(args.path): + files = glob.glob(os.path.join(args.path, "*.npy")) + else: + files = glob.glob(args.path) + + if not files: + raise RuntimeError("No .npy files found") + + # Sort numerically based on timestep in filename + files.sort() + + print(f"Found {len(files)} files") + + plt.ion() # interactive mode + fig, ax = plt.subplots() + + im = None + i = 0 + while True: + i = i % len(files) + f = files[i] + + + print(f"Loading {f}") + + try: + arr = np.load(f) + except Exception as e: + print(f"Skipping {f}: {e}") + continue + + slice_ = arr[arr.shape[0] // 2] + print(f"{f}: shape={arr.shape}, min={arr.min()}, max={arr.max()}, any NaN={np.isnan(arr).any()}") + + slice_ = arr[arr.shape[0] // 2] + + print(f"Slice {arr.shape[0]//2}: min={slice_.min()}, max={slice_.max()}, any NaN={np.isnan(slice_).any()}") + + if im is None: + im = ax.imshow(slice_, origin="lower") + plt.colorbar(im, ax=ax) + else: + im.set_data(slice_) + im.set_clim(vmin=slice_.min(), vmax=slice_.max()) + + ax.set_title(os.path.basename(f)) + plt.pause(args.delay) + i += 1 + + plt.ioff() + plt.show() + + if __name__ == "__main__": - main() + main_loop() + # main() diff --git a/test/test_main.cpp b/test/test_main.cpp index dab222a..184b4d9 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -5,10 +5,7 @@ #include #include -TEST(DummyTest, AlwaysPasses) -{ - EXPECT_EQ(1, 1); -} + int main(int argc, char** argv) { int ret = -1; diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index daa6d61..3c017bc 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -54,21 +54,22 @@ void init_from_state_test( init_from_value_test(exec_space, prim_arrays.ux1, prim.ux1); init_from_value_test(exec_space, prim_arrays.ux2, prim.ux2); } -// include your headers TEST(PrimToCons, ScalarVsVectorized) { using real_t = double; using index_t = int; - int const n = 16; // keep small for unit test + int const n = 16; Kokkos::DefaultExecutionSpace exec_space; PerfectGas eos(1.4); - // --- allocate --- auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + // --- allocate base --- auto cons_alloc_ref = create_cons_arrays_1d(exec_space, n * n * n); + + // --- allocate vectorized --- auto cons_alloc_vec = create_cons_arrays_1d(exec_space, n * n * n); auto prim_arrays = to_mdspan Date: Mon, 30 Mar 2026 13:22:33 +0200 Subject: [PATCH 03/56] PrimToConsVectorized benchmark --- benchmarks/benchmark_main.cpp | 5 +- benchmarks/benchmark_prim_to_cons.cpp | 30 +++++ euler_operators/prim_to_cons.hpp | 4 +- simulations/euler_simulation.cpp | 4 +- simulations/plot.py | 157 ++++++++++---------------- 5 files changed, 96 insertions(+), 104 deletions(-) diff --git a/benchmarks/benchmark_main.cpp b/benchmarks/benchmark_main.cpp index 0cffc6e..3b8839f 100644 --- a/benchmarks/benchmark_main.cpp +++ b/benchmarks/benchmark_main.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -5,8 +7,9 @@ int main(int argc, char** argv) { ::Kokkos::ScopeGuard const scope(argc, argv); + Kokkos::print_configuration(std::cout); ::benchmark::Initialize(&argc, argv); - ::benchmark::MaybeReenterWithoutASLR(argc, argv); + // ::benchmark::MaybeReenterWithoutASLR(argc, argv); if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { return 1; } diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index 9a3bf40..44877c9 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -40,6 +40,36 @@ void PrimToCons(benchmark::State& state) set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); } +void PrimToConsVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} + } // namespace BENCHMARK(PrimToCons)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(PrimToConsVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 05cc99b..16d9daf 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -68,7 +68,7 @@ void prim_to_cons_vec( T* cm1 = cons_arrays.mx1.data_handle(); T* cm2 = cons_arrays.mx2.data_handle(); - simd_t const gamma_minus_one_inv = T(1) / (eos.gamma() - T(1)); + double const gamma_minus_one_inv = T(1) / (eos.gamma() - T(1)); IndexType const nx_blocks = nx / simd_width; @@ -78,13 +78,13 @@ void prim_to_cons_vec( KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { IndexType const base = bi * simd_width + nx * j + nx * ny * k; - // load SIMD lanes simd_t d(pd + base, KE::simd_flag_default); simd_t p(pp + base, KE::simd_flag_default); simd_t ux0(pu0 + base, KE::simd_flag_default); simd_t ux1(pu1 + base, KE::simd_flag_default); simd_t ux2(pu2 + base, KE::simd_flag_default); + simd_t int_e = p * gamma_minus_one_inv; simd_t e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); diff --git a/simulations/euler_simulation.cpp b/simulations/euler_simulation.cpp index b48e37e..d8cc701 100644 --- a/simulations/euler_simulation.cpp +++ b/simulations/euler_simulation.cpp @@ -48,7 +48,7 @@ int main(int argc, char** argv) Kokkos::layout_left>>(cons_alloc, nx + 2, nx + 2, nx + 2); init_implode(exec_space, prim_arrays, mesh); - prim_to_cons(exec_space, as_const(prim_arrays), cons_arrays, eos); + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_arrays, eos); exec_space.fence(); auto const start = std::chrono::steady_clock::now(); @@ -71,7 +71,7 @@ int main(int argc, char** argv) if (output_freq > 0 && it % output_freq == 0) { int const padding = 10; std::stringstream ss; - ss << "test_" << std::setfill('0') << std::setw(padding) << it << ".npy"; + ss << "test_vec_" << std::setfill('0') << std::setw(padding) << it << ".npy"; std::fstream file(ss.str(), std::fstream::out); std::cout << "Saving " << ss.str() << ' '; save_npy(file, prim_arrays.p); diff --git a/simulations/plot.py b/simulations/plot.py index 023d9cb..52e1806 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -6,7 +6,7 @@ import matplotlib.pyplot as plt from pathlib import Path -KERNEL_BENCHMARKS = ["Godunov", "TimeStep", "ConsToPrim", "PrimToCons"] +KERNEL_BENCHMARKS = ["Godunov", "TimeStep", "ConsToPrim", "PrimToConsVectorized","PrimToCons" ] ALL_BENCHMARKS = KERNEL_BENCHMARKS ALL_BENCHMARKS.append("EulerSimulation") @@ -15,124 +15,83 @@ # --------------------------------------------------------- FILES={ - "a100": "./results/ruche/a100/[412780]_ALL-a100_bm_a100.json", - "v100": "./results/ruche/v100/[412885]_ALL-v100_bm_v100.json", - "skx": "./results/ruche/skx/[419180]_ALL-skx_bm_skx.json", - + # "skx": "./results/ruche/skx/[440175]_cpus-40_bm_ruche.json", + # "skx": "./results/ruche/skx/[440222]_cpus-1_bm_ruche.json", + "skx": "./results/ruche/skx/[452397]_cpus-40_bm_ruche.json", + # "skx": "./results/ruche/skx/[451301]_cpus-1_bm_ruche.json", + # "skx": "./results/ruche/skx/[451664]_cpus-20_bm_ruche.json", + # "skx": "./results/ruche/skx/[451295]_cpus-30_bm_ruche.json" } -# each tuple is (slower, faster) → speedup = time[slower] / time[faster] -SPEEDUPS = [ - ("v100", "a100"), -] - OUT_DIR = "results/plots" -# --------------------------------------------------------- -# Load -# --------------------------------------------------------- -def load(path, benchmarks): - with open(path) as f: - data = json.load(f)["benchmarks"] - rows = [] - for b in data: - name = b["name"] - match = next((bm for bm in benchmarks if bm in name), None) - if match is None: - continue - rows.append({ - "benchmark": match, - "size": int(name.split("/")[-1]), - "time": b["cpu_time"], - "cells_per_second": b.get("cells_per_second"), - "bytes_per_second": b.get("bytes_per_second"), - }) - return pd.DataFrame(rows) - -# --------------------------------------------------------- -# Plot -# --------------------------------------------------------- -def plot_time(files, speedups, out_dir): - out_dir = Path(out_dir) - out_dir.mkdir(parents=True, exist_ok=True) - - datasets = {label: load(path, ALL_BENCHMARKS) for label, path in files.items()} - - for bm in ALL_BENCHMARKS: - fig, ax1 = plt.subplots(figsize=(9, 5)) - ax2 = ax1.twinx() - - # --- times --- - for label, df in datasets.items(): - sub = df[df["benchmark"] == bm].sort_values("size") - if sub.empty: - continue - ax1.plot(sub["size"], sub["time"], marker="o", label=label) - # --- speedups --- - for slow, fast in speedups: - if slow not in datasets or fast not in datasets: +def extract_label(path): + name = Path(path).name + label = name.split("_")[1] + timestamp = name.split("[")[1].split("]")[0] + return timestamp + "_" + label + +def plot_scalar_vs_vector(files, out_dir, base_name): + import json + import pandas as pd + import matplotlib.pyplot as plt + from pathlib import Path + + def load_one(path): + with open(path) as f: + data = json.load(f)["benchmarks"] + rows = [] + for b in data: + name = b["name"] + if base_name not in name and (base_name + "Vectorized") not in name: continue - df_slow = datasets[slow][datasets[slow]["benchmark"] == bm].sort_values("size") - df_fast = datasets[fast][datasets[fast]["benchmark"] == bm].sort_values("size") - merged = df_slow.merge(df_fast, on="size", suffixes=(f"_{slow}", f"_{fast}")) - merged["speedup"] = merged[f"time_{slow}"] / merged[f"time_{fast}"] - ax2.plot(merged["size"], merged["speedup"], linestyle="--", - marker="s", label=f"{slow}→{fast} speedup", color="red") - - ax1.set_title(bm) - ax1.set_xlabel("nx") - ax1.set_ylabel("Time (ns)") - ax2.set_ylabel("Speedup v100/a100") - ax2.axhline(1.0, color="gray", linewidth=0.8, linestyle=":") - - lines1, labels1 = ax1.get_legend_handles_labels() - lines2, labels2 = ax2.get_legend_handles_labels() - ax1.legend(lines1 + lines2, labels1 + labels2, fontsize=8) - - ax1.grid(True) - ax1.set_yscale("log", base=2) - plt.tight_layout() - path = out_dir / f"{bm}_times.png" - plt.savefig(path, dpi=200) - plt.close() - print("saved:", path) + rows.append({ + "benchmark": name.split("/")[0], + "size": int(name.split("/")[-1]), + "cells_per_second": b.get("cells_per_second"), + "bytes_per_second": b.get("bytes_per_second"), + }) + return pd.DataFrame(rows) - -def plot_items(files,out_dir): out_dir = Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) - datasets = {label: load(path, KERNEL_BENCHMARKS) for label, path in files.items()} + vec_name = base_name + "Vectorized" + + for environment, path in files.items(): + df = load_one(path) + bm_label = extract_label(path) - for bm in KERNEL_BENCHMARKS: + s = df[df["benchmark"] == base_name].sort_values("size") + v = df[df["benchmark"] == vec_name].sort_values("size") + + if s.empty or v.empty: + print(f"skipping {base_name}") + continue fig, ax1 = plt.subplots(figsize=(9, 5)) + ax2 = ax1.twinx() - # --- cells_per_second --- - for label, df in datasets.items(): - sub = df[df["benchmark"] == bm].sort_values("size") - if sub.empty: - continue - ax1.plot(sub["size"], sub["cells_per_second"], marker="o", label=label) + # cells/s + ax1.plot(s["size"], s["cells_per_second"], "o-", label="scalar") + ax1.plot(v["size"], v["cells_per_second"], "s-", label="vectorized") + # bytes/s + ax2.plot(s["size"], s["bytes_per_second"], "--") + ax2.plot(v["size"], v["bytes_per_second"], ":") - - ax1.set_title(bm) + ax1.set_title(base_name +" " + bm_label) ax1.set_xlabel("nx") ax1.set_ylabel("cells/s") + ax2.set_ylabel("bytes/s") - lines1, labels1 = ax1.get_legend_handles_labels() - ax1.legend(lines1, labels1, fontsize=8) - + ax1.legend(fontsize=8) ax1.grid(True) plt.tight_layout() - path = out_dir / f"{bm}_items.png" - plt.savefig(path, dpi=200) - plt.close() - print("saved:", path) - -plot_time(FILES, SPEEDUPS, OUT_DIR) -plot_items(FILES, OUT_DIR) + print("saving ...") + plt.savefig(out_dir / f"{environment}_{bm_label}_{base_name}.png", dpi=200) + plt.close() +plot_scalar_vs_vector(FILES, OUT_DIR, "PrimToCons") From 3c3f995399634174d45700c791f51717eb7a5c1f Mon Sep 17 00:00:00 2001 From: Timothy Date: Mon, 30 Mar 2026 16:16:16 +0200 Subject: [PATCH 04/56] PrimToConsVectorized speedup against scalar over plateau --- benchmarks/benchmark_main.cpp | 4 ++-- euler_operators/perfect_gas.hpp | 13 ++++++------- euler_operators/prim_to_cons.hpp | 33 ++++++++++++++++++++------------ setups/ruche/skx/run_bench.sh | 30 +++++++++++++++++++++++++++++ simulations/plot.py | 17 ++++++++-------- 5 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 setups/ruche/skx/run_bench.sh diff --git a/benchmarks/benchmark_main.cpp b/benchmarks/benchmark_main.cpp index 3b8839f..40b8ad2 100644 --- a/benchmarks/benchmark_main.cpp +++ b/benchmarks/benchmark_main.cpp @@ -6,10 +6,10 @@ int main(int argc, char** argv) { + ::benchmark::MaybeReenterWithoutASLR(argc, argv); ::Kokkos::ScopeGuard const scope(argc, argv); - Kokkos::print_configuration(std::cout); ::benchmark::Initialize(&argc, argv); - // ::benchmark::MaybeReenterWithoutASLR(argc, argv); + Kokkos::print_configuration(std::cout); if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { return 1; } diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index fbaf8d7..3b31739 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -8,23 +8,22 @@ class PerfectGas { private: T m_gamma; + T gamma_minus_one_inv; public: - explicit PerfectGas(T const gamma) : m_gamma(gamma) {} + explicit PerfectGas(T const gamma) : m_gamma(gamma), gamma_minus_one_inv(1 / (gamma - 1)) {} - KOKKOS_FUNCTION T gamma() const noexcept - { - return m_gamma; - } KOKKOS_FUNCTION T speed_of_sound(T const density, T const pressure) const noexcept { return Kokkos::sqrt(m_gamma * pressure / density); } - KOKKOS_FUNCTION T internal_energy(T const /*density*/, T const pressure) const noexcept + template + KOKKOS_FUNCTION S internal_energy(S const /*density*/, S const pressure) const noexcept { - return pressure / (m_gamma - 1); + // return pressure / (m_gamma - 1); + return pressure * gamma_minus_one_inv; } KOKKOS_FUNCTION T pressure(T const /*density*/, T const int_e) const noexcept diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 16d9daf..d83336d 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -68,31 +68,40 @@ void prim_to_cons_vec( T* cm1 = cons_arrays.mx1.data_handle(); T* cm2 = cons_arrays.mx2.data_handle(); - double const gamma_minus_one_inv = T(1) / (eos.gamma() - T(1)); IndexType const nx_blocks = nx / simd_width; Kokkos::parallel_for( "prim_to_cons_vec", - Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), + Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { IndexType const base = bi * simd_width + nx * j + nx * ny * k; + // --- Loads --- simd_t d(pd + base, KE::simd_flag_default); simd_t p(pp + base, KE::simd_flag_default); simd_t ux0(pu0 + base, KE::simd_flag_default); simd_t ux1(pu1 + base, KE::simd_flag_default); simd_t ux2(pu2 + base, KE::simd_flag_default); - - simd_t int_e = p * gamma_minus_one_inv; - simd_t e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); - - d.copy_to(cd + base, KE::simd_flag_default); - (e_kin + int_e).copy_to(ce + base, KE::simd_flag_default); - (d * ux0).copy_to(cm0 + base, KE::simd_flag_default); - (d * ux1).copy_to(cm1 + base, KE::simd_flag_default); - (d * ux2).copy_to(cm2 + base, KE::simd_flag_default); + // --- Compute momenta once, reuse for kinetic energy --- + simd_t m0 = d * ux0; // reused below + simd_t m1 = d * ux1; + simd_t m2 = d * ux2; + + // e_kin = 0.5 * (m·u) avoids re-multiplying d + simd_t e_kin = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2); + simd_t e_tot = e_kin + eos.internal_energy(d, p); + + // --- Stores (skip redundant d copy if cd == pd) --- + // d.copy_to(cd + base, KE::simd_flag_default); // remove if cd == pd + e_tot.copy_to(ce + base, KE::simd_flag_default); + m0.copy_to(cm0 + base, KE::simd_flag_default); + m1.copy_to(cm1 + base, KE::simd_flag_default); + m2.copy_to(cm2 + base, KE::simd_flag_default); }); IndexType const rem_start = nx_blocks * simd_width; @@ -111,7 +120,7 @@ void prim_to_cons_vec( T const ux1 = pu1[base]; T const ux2 = pu2[base]; - T const int_e = p * (T(1) / (eos.gamma() - T(1))); + T const int_e = eos.internal_energy(d, p); T const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); cd[base] = d; diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh new file mode 100644 index 0000000..ca99a6e --- /dev/null +++ b/setups/ruche/skx/run_bench.sh @@ -0,0 +1,30 @@ +#!/bin/bash +#SBATCH --job-name=bench_skx +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:15:00 +#SBATCH --partition=cpu_short +#SBATCH --exclusive +#SBATCH --hint=nomultithread + +module purge +module load \ + gcc/13.4.0/gcc-15.1.0 \ + cmake/3.31.9/gcc-15.1.0 + +set -x +cd ${SLURM_SUBMIT_DIR} + +mkdir -p slurm_out results/ruche/skx + +export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK} +export OMP_PROC_BIND=true + +BENCHMARK_FILTER=${1:-""} + +# include SLURM_JOB_ID in the JSON output filename +./build-skx/benchmarks/euler_benchmarks \ + --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out_format=json \ + --benchmark_out=./results/ruche/skx/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}_bm_ruche.json" diff --git a/simulations/plot.py b/simulations/plot.py index 52e1806..9229c00 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -13,16 +13,14 @@ # --------------------------------------------------------- # Config # --------------------------------------------------------- - -FILES={ - # "skx": "./results/ruche/skx/[440175]_cpus-40_bm_ruche.json", - # "skx": "./results/ruche/skx/[440222]_cpus-1_bm_ruche.json", - "skx": "./results/ruche/skx/[452397]_cpus-40_bm_ruche.json", - # "skx": "./results/ruche/skx/[451301]_cpus-1_bm_ruche.json", - # "skx": "./results/ruche/skx/[451664]_cpus-20_bm_ruche.json", - # "skx": "./results/ruche/skx/[451295]_cpus-30_bm_ruche.json" +FILES = { + # "skx_1": "results/ruche/skx/[455228]_cpus-1_bm_ruche.json", + "skx": "results/ruche/skx/[455363]_skx-PrimToCons_bm_ruche.json", + # "skx_10": "results/ruche/skx/[453127]_cpus-10-ref_bm_ruche.json", + # "skx_20": "results/ruche/skx/[453128]_cpus-20-ref_bm_ruche.json", + # "skx_30": "results/ruche/skx/[453129]_cpus-30-ref_bm_ruche.json", + # "skx_40": "results/ruche/skx/[453130]_cpus-40-ref_bm_ruche.json", } - OUT_DIR = "results/plots" @@ -58,6 +56,7 @@ def load_one(path): out_dir.mkdir(parents=True, exist_ok=True) vec_name = base_name + "Vectorized" + print(files) for environment, path in files.items(): df = load_one(path) From c2590c2a35b895abb976909a1d1120f2de278706 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 31 Mar 2026 14:28:24 +0200 Subject: [PATCH 05/56] PrimToConsWorstRem benchmark added --- benchmarks/benchmark_prim_to_cons.cpp | 64 +++++++++- euler_operators/prim_to_cons.hpp | 155 ++++++++++++++---------- setups/ruche/skx/run_bench.sh | 2 +- setups/ruche/skx/{run.sh => run_sim.sh} | 5 +- setups/ruche/skx/run_test.sh | 19 +++ simulations/plot.py | 101 ++++++++------- 6 files changed, 235 insertions(+), 111 deletions(-) rename setups/ruche/skx/{run.sh => run_sim.sh} (78%) create mode 100644 setups/ruche/skx/run_test.sh diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index 44877c9..a191b90 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -68,8 +68,68 @@ void PrimToConsVectorized(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); } +void PrimToConsWorstRem(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + prim_to_cons(exec_space, as_const(prim_arrays), cons_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} + +void PrimToConsWorstRemVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} + } // namespace -BENCHMARK(PrimToCons)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); -BENCHMARK(PrimToConsVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(PrimToCons)->DenseRange(8, 128, 8)->DenseRange(128, 320, 32); +BENCHMARK(PrimToConsVectorized)->DenseRange(8, 128, 8)->DenseRange(128, 320, 32); +BENCHMARK(PrimToConsWorstRem)->DenseRange(7, 128, 8)->DenseRange(127, 320, 32); +BENCHMARK(PrimToConsWorstRemVectorized)->DenseRange(7, 128, 8)->DenseRange(127, 320, 32); diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index d83336d..b23b021 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -35,6 +35,58 @@ void prim_to_cons( }); } + + +template +void prim_to_cons_kernel( + Kokkos::DefaultExecutionSpace const& exec_space, + T const* pd, + T const* pp, + T const* pu0, + T const* pu1, + T const* pu2, + T* cd, + T* ce, + T* cm0, + T* cm1, + T* cm2, + IndexType nx_begin, + IndexType nx_end, + IndexType ny, + IndexType nz, + PerfectGas const& eos) +{ + namespace KE = Kokkos::Experimental; + constexpr IndexType width = SimdType::size(); + IndexType const nx_blocks = (nx_end - nx_begin) / width; + IndexType const nx = nx_end; // full nx for stride computation + + Kokkos::parallel_for( + "prim_to_cons_kernel", + Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { + IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; + SimdType d(pd + base, KE::simd_flag_default); + SimdType p(pp + base, KE::simd_flag_default); + SimdType ux0(pu0 + base, KE::simd_flag_default); + SimdType ux1(pu1 + base, KE::simd_flag_default); + SimdType ux2(pu2 + base, KE::simd_flag_default); + SimdType m0 = d * ux0; + SimdType m1 = d * ux1; + SimdType m2 = d * ux2; + SimdType e_tot + = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2) + eos.internal_energy(d, p); + d.copy_to(cd + base, KE::simd_flag_default); + e_tot.copy_to(ce + base, KE::simd_flag_default); + m0.copy_to(cm0 + base, KE::simd_flag_default); + m1.copy_to(cm1 + base, KE::simd_flag_default); + m2.copy_to(cm2 + base, KE::simd_flag_default); + }); +} + template void prim_to_cons_vec( Kokkos::DefaultExecutionSpace const& exec_space, @@ -50,7 +102,8 @@ void prim_to_cons_vec( { namespace KE = Kokkos::Experimental; using simd_t = KE::simd; - constexpr IndexType simd_width = simd_t::size(); + using simd_scalar_t = KE::basic_simd; + IndexType const nx = prim_arrays.d.extent(0); IndexType const ny = prim_arrays.d.extent(1); @@ -61,73 +114,49 @@ void prim_to_cons_vec( T const* pu0 = prim_arrays.ux0.data_handle(); T const* pu1 = prim_arrays.ux1.data_handle(); T const* pu2 = prim_arrays.ux2.data_handle(); - T* cd = cons_arrays.d.data_handle(); T* ce = cons_arrays.e.data_handle(); T* cm0 = cons_arrays.mx0.data_handle(); T* cm1 = cons_arrays.mx1.data_handle(); T* cm2 = cons_arrays.mx2.data_handle(); - - IndexType const nx_blocks = nx / simd_width; - - Kokkos::parallel_for( - "prim_to_cons_vec", - Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), - KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const base = bi * simd_width + nx * j + nx * ny * k; - - // --- Loads --- - simd_t d(pd + base, KE::simd_flag_default); - simd_t p(pp + base, KE::simd_flag_default); - simd_t ux0(pu0 + base, KE::simd_flag_default); - simd_t ux1(pu1 + base, KE::simd_flag_default); - simd_t ux2(pu2 + base, KE::simd_flag_default); - - // --- Compute momenta once, reuse for kinetic energy --- - simd_t m0 = d * ux0; // reused below - simd_t m1 = d * ux1; - simd_t m2 = d * ux2; - - // e_kin = 0.5 * (m·u) avoids re-multiplying d - simd_t e_kin = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2); - simd_t e_tot = e_kin + eos.internal_energy(d, p); - - // --- Stores (skip redundant d copy if cd == pd) --- - // d.copy_to(cd + base, KE::simd_flag_default); // remove if cd == pd - e_tot.copy_to(ce + base, KE::simd_flag_default); - m0.copy_to(cm0 + base, KE::simd_flag_default); - m1.copy_to(cm1 + base, KE::simd_flag_default); - m2.copy_to(cm2 + base, KE::simd_flag_default); - }); - - IndexType const rem_start = nx_blocks * simd_width; - if (rem_start < nx) { - Kokkos::parallel_for( - "prim_to_cons_vec_remainder", - Kokkos::MDRangePolicy>({rem_start, 0, 0}, {nx, ny, nz}), - KOKKOS_LAMBDA(IndexType i, IndexType j, IndexType k) { - IndexType const base = i + nx * j + nx * ny * k; - T const d = pd[base]; - T const p = pp[base]; - T const ux0 = pu0[base]; - T const ux1 = pu1[base]; - T const ux2 = pu2[base]; - - T const int_e = eos.internal_energy(d, p); - T const e_kin = d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2) / T(2); - - cd[base] = d; - ce[base] = e_kin + int_e; - cm0[base] = d * ux0; - cm1[base] = d * ux1; - cm2[base] = d * ux2; - }); + IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); + + prim_to_cons_kernel( + exec_space, + pd, + pp, + pu0, + pu1, + pu2, + cd, + ce, + cm0, + cm1, + cm2, + IndexType(0), + vec_end, + ny, + nz, + eos); + + if (vec_end < nx) { + prim_to_cons_kernel( + exec_space, + pd, + pp, + pu0, + pu1, + pu2, + cd, + ce, + cm0, + cm1, + cm2, + vec_end, + nx, + ny, + nz, + eos); } } diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index ca99a6e..25f4626 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -3,7 +3,7 @@ #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=1 -#SBATCH --time=00:15:00 +#SBATCH --time=00:10:00 #SBATCH --partition=cpu_short #SBATCH --exclusive #SBATCH --hint=nomultithread diff --git a/setups/ruche/skx/run.sh b/setups/ruche/skx/run_sim.sh similarity index 78% rename from setups/ruche/skx/run.sh rename to setups/ruche/skx/run_sim.sh index f9b5e51..ccf57eb 100644 --- a/setups/ruche/skx/run.sh +++ b/setups/ruche/skx/run_sim.sh @@ -1,9 +1,9 @@ #!/bin/bash -#SBATCH --job-name=simulation_skx +#SBATCH --job-name=sim_skx #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=20 -#SBATCH --time=00:10:00 +#SBATCH --time=00:05:00 #SBATCH --partition=cpu_short module purge @@ -17,4 +17,3 @@ cd ${SLURM_SUBMIT_DIR} mkdir -p slurm_out results/ruche/skx ./build-skx/simulations/euler_simulation -# ./build-skx/test/euler_test diff --git a/setups/ruche/skx/run_test.sh b/setups/ruche/skx/run_test.sh new file mode 100644 index 0000000..1692b7d --- /dev/null +++ b/setups/ruche/skx/run_test.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#SBATCH --job-name=test_skx +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:05:00 +#SBATCH --partition=cpu_short + +module purge +module load \ + gcc/13.4.0/gcc-15.1.0 \ + cmake/3.31.9/gcc-15.1.0 + +set -x +cd ${SLURM_SUBMIT_DIR} + +mkdir -p slurm_out results/ruche/skx + +./build-skx/test/euler_tests diff --git a/simulations/plot.py b/simulations/plot.py index 9229c00..4e26b39 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -15,7 +15,9 @@ # --------------------------------------------------------- FILES = { # "skx_1": "results/ruche/skx/[455228]_cpus-1_bm_ruche.json", - "skx": "results/ruche/skx/[455363]_skx-PrimToCons_bm_ruche.json", + "skx_rem": "results/ruche/skx/[457078]_skx-PrimToCons_bm_ruche.json", + # "skx_rem":"results/ruche/skx/[457041]_skx-PrimToCons_bm_ruche.json", + # "skx_10": "results/ruche/skx/[453127]_cpus-10-ref_bm_ruche.json", # "skx_20": "results/ruche/skx/[453128]_cpus-20-ref_bm_ruche.json", # "skx_30": "results/ruche/skx/[453129]_cpus-30-ref_bm_ruche.json", @@ -30,67 +32,82 @@ def extract_label(path): timestamp = name.split("[")[1].split("]")[0] return timestamp + "_" + label -def plot_scalar_vs_vector(files, out_dir, base_name): + + + +def plot_scalar_vs_vector(files, out_dir): import json import pandas as pd import matplotlib.pyplot as plt from pathlib import Path + BYTES_PER_CELL = 10 * 8 + cache_colors = {1: "green", 2: "orange", 3: "red"} + def load_one(path): with open(path) as f: - data = json.load(f)["benchmarks"] + raw = json.load(f) + caches = {c["level"]: c["size"] for c in raw["context"]["caches"] if c["type"] == "Unified"} rows = [] - for b in data: + for b in raw["benchmarks"]: name = b["name"] - if base_name not in name and (base_name + "Vectorized") not in name: - continue rows.append({ "benchmark": name.split("/")[0], "size": int(name.split("/")[-1]), "cells_per_second": b.get("cells_per_second"), "bytes_per_second": b.get("bytes_per_second"), }) - return pd.DataFrame(rows) + return pd.DataFrame(rows), caches out_dir = Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) - vec_name = base_name + "Vectorized" - print(files) - - for environment, path in files.items(): - df = load_one(path) - bm_label = extract_label(path) + # collect all benchmark names across every file + all_names = set() + for path in files.values(): + df, _ = load_one(path) + all_names.update(df["benchmark"].unique()) + base_names = [b for b in all_names if b + "Vectorized" in all_names] - s = df[df["benchmark"] == base_name].sort_values("size") - v = df[df["benchmark"] == vec_name].sort_values("size") + for base_name in base_names: + vec_name = base_name + "Vectorized" + for environment, path in files.items(): + df, caches = load_one(path) + bm_label = extract_label(path) - if s.empty or v.empty: - print(f"skipping {base_name}") - continue + s = df[df["benchmark"] == base_name].sort_values("size") + v = df[df["benchmark"] == vec_name].sort_values("size") - fig, ax1 = plt.subplots(figsize=(9, 5)) - ax2 = ax1.twinx() - - # cells/s - ax1.plot(s["size"], s["cells_per_second"], "o-", label="scalar") - ax1.plot(v["size"], v["cells_per_second"], "s-", label="vectorized") - - # bytes/s - ax2.plot(s["size"], s["bytes_per_second"], "--") - ax2.plot(v["size"], v["bytes_per_second"], ":") - - ax1.set_title(base_name +" " + bm_label) - ax1.set_xlabel("nx") - ax1.set_ylabel("cells/s") - ax2.set_ylabel("bytes/s") - - ax1.legend(fontsize=8) - ax1.grid(True) - plt.tight_layout() - - print("saving ...") - plt.savefig(out_dir / f"{environment}_{bm_label}_{base_name}.png", dpi=200) - plt.close() + if s.empty or v.empty: + print(f"skipping {base_name} for {environment}") + continue -plot_scalar_vs_vector(FILES, OUT_DIR, "PrimToCons") + fig, ax1 = plt.subplots(figsize=(9, 5)) + ax2 = ax1.twinx() + + for df_series, color, label in [(s, "C0", "scalar"), (v, "C1", "vectorized")]: + aligned = df_series[df_series["size"] % 8 == 0] + unaligned = df_series[df_series["size"] % 8 != 0] + ax1.plot(df_series["size"], df_series["cells_per_second"], "-", color=color, label=label + " cells/s") + ax2.plot(df_series["size"], df_series["bytes_per_second"], "--", color=color, label=label + " bytes/s", alpha=0.4) + ax1.scatter(aligned["size"], aligned["cells_per_second"], marker="o", color=color, zorder=5) + ax1.scatter(unaligned["size"], unaligned["cells_per_second"], marker="x", color=color, zorder=5) + ax2.scatter(aligned["size"], aligned["bytes_per_second"], marker="o", color=color, alpha=0.4) + ax2.scatter(unaligned["size"], unaligned["bytes_per_second"], marker="x", color=color, alpha=0.4) + + for level, size_bytes in sorted(caches.items()): + n_cache = (size_bytes / BYTES_PER_CELL) ** (1/3) + color = cache_colors.get(level, "gray") + ax1.axvline(n_cache, linestyle="--", color=color, alpha=0.7, + label=f"L{level} ({size_bytes // 1024} KB) → n≈{n_cache:.0f}") + + ax1.set_title(f"{base_name} — {bm_label}") + ax1.set_xlabel("n (cube width in cells)") + ax1.set_ylabel("cells/s") + ax2.set_ylabel("bytes/s") + ax1.legend(fontsize=8) + ax1.grid(True) + plt.tight_layout() + plt.savefig(out_dir / f"{bm_label}_{base_name}.png", dpi=200) + plt.close() +plot_scalar_vs_vector(FILES, OUT_DIR) From 552ff17b744485efdf4558d774d09c9eb55b3482 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 31 Mar 2026 14:55:53 +0200 Subject: [PATCH 06/56] kokkos version 4.7.1 -> 5.0.0 --- euler_operators/prim_to_cons.hpp | 13 ++++++++----- setups/ruche/skx/prepare.sh | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index b23b021..eeac9e7 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -79,11 +79,14 @@ void prim_to_cons_kernel( SimdType m2 = d * ux2; SimdType e_tot = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2) + eos.internal_energy(d, p); - d.copy_to(cd + base, KE::simd_flag_default); - e_tot.copy_to(ce + base, KE::simd_flag_default); - m0.copy_to(cm0 + base, KE::simd_flag_default); - m1.copy_to(cm1 + base, KE::simd_flag_default); - m2.copy_to(cm2 + base, KE::simd_flag_default); + + + + KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); + KE::simd_unchecked_store(e_tot, ce + base, KE::simd_flag_default); + KE::simd_unchecked_store(m0, cm0 + base, KE::simd_flag_default); + KE::simd_unchecked_store(m1, cm1 + base, KE::simd_flag_default); + KE::simd_unchecked_store(m2, cm2 + base, KE::simd_flag_default); }); } diff --git a/setups/ruche/skx/prepare.sh b/setups/ruche/skx/prepare.sh index 671dcdf..7d129a5 100755 --- a/setups/ruche/skx/prepare.sh +++ b/setups/ruche/skx/prepare.sh @@ -22,7 +22,8 @@ cmake --build build-benchmark cmake --install build-benchmark --prefix $benchmark_ROOT rm -rf build-benchmark benchmark -git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git +# git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git +git clone --branch 5.0.0 --depth 1 https://github.com/kokkos/kokkos.git cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ From f4f1cba9fadb612b65517137c720dd150a6671cc Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 31 Mar 2026 15:21:07 +0200 Subject: [PATCH 07/56] Seprate global utils for benchmark and tests --- CMakeLists.txt | 2 + benchmarks/CMakeLists.txt | 2 +- benchmarks/benchmark_cons_to_prim.cpp | 1 + benchmarks/benchmark_euler_simulation.cpp | 1 + benchmarks/benchmark_godunov.cpp | 10 +--- benchmarks/benchmark_prim_to_cons.cpp | 1 + benchmarks/benchmark_time_step.cpp | 1 + benchmarks/benchmark_utils.hpp | 70 ----------------------- euler_operators/cons_to_prim.hpp | 1 + simulations/CMakeLists.txt | 2 +- simulations/plot.py | 4 +- test/CMakeLists.txt | 2 +- test/test_prim_to_cons.cpp | 53 +---------------- 13 files changed, 17 insertions(+), 133 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7de2ae1..021d9fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ project(euler_benchmarks CXX) find_package(Kokkos REQUIRED) +add_subdirectory(utils) + add_subdirectory(benchmarks) add_subdirectory(test) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index d846573..b5d1ce9 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -3,7 +3,7 @@ find_package(benchmark REQUIRED) add_executable(euler_benchmarks) target_link_libraries( euler_benchmarks - PRIVATE benchmark::benchmark euler_operators Kokkos::kokkos + PRIVATE benchmark::benchmark euler_operators Kokkos::kokkos euler_utils ) target_sources( euler_benchmarks diff --git a/benchmarks/benchmark_cons_to_prim.cpp b/benchmarks/benchmark_cons_to_prim.cpp index 16d0596..1845214 100644 --- a/benchmarks/benchmark_cons_to_prim.cpp +++ b/benchmarks/benchmark_cons_to_prim.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index e24bfd3..1d5d80a 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -10,6 +10,7 @@ #include #include #include +#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index d2555f4..7aa999a 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" @@ -36,14 +37,7 @@ void Godunov(benchmark::State& state) exec_space.fence(); for ([[maybe_unused]] auto _ : state) { - godunov( - exec_space, - as_const(prim_arrays), - cons_arrays, - eos, - mesh, - hllc(), - dt); + godunov(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); exec_space.fence(); benchmark::ClobberMemory(); } diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index a191b90..aa5e797 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" diff --git a/benchmarks/benchmark_time_step.cpp b/benchmarks/benchmark_time_step.cpp index 16133cb..609f29c 100644 --- a/benchmarks/benchmark_time_step.cpp +++ b/benchmarks/benchmark_time_step.cpp @@ -9,6 +9,7 @@ #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { diff --git a/benchmarks/benchmark_utils.hpp b/benchmarks/benchmark_utils.hpp index 191d8b5..6894e3d 100644 --- a/benchmarks/benchmark_utils.hpp +++ b/benchmarks/benchmark_utils.hpp @@ -5,7 +5,6 @@ #include -#include "euler_arrays.hpp" template R int_cast(T t) @@ -16,75 +15,6 @@ R int_cast(T t) throw std::runtime_error("Conversion cannot preserve value representation"); } -template < - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class LP, - class AP> -void init_from_value( - Kokkos::DefaultExecutionSpace const& exec_space, - Kokkos::mdspan, LP, AP> const& array, - ElementType const& value) -{ - Kokkos::parallel_for( - "init_from_value", - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - Kokkos::IndexType>( - exec_space, - {0, 0, 0}, - {array.extent(0), array.extent(1), array.extent(2)}), - KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { - array(i, j, k) = value; - }); -} - -template < - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class LP, - class AP> -void init_from_state( - Kokkos::DefaultExecutionSpace const& exec_space, - EulerPrimArrays< - Kokkos::mdspan, LP, AP>> const& - prim_arrays, - EulerPrim const& prim) -{ - init_from_value(exec_space, prim_arrays.d, prim.d); - init_from_value(exec_space, prim_arrays.p, prim.p); - init_from_value(exec_space, prim_arrays.ux0, prim.ux0); - init_from_value(exec_space, prim_arrays.ux1, prim.ux1); - init_from_value(exec_space, prim_arrays.ux2, prim.ux2); -} - -template < - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class LP, - class AP> -void init_from_state( - Kokkos::DefaultExecutionSpace const& exec_space, - EulerConsArrays< - Kokkos::mdspan, LP, AP>> const& - cons_arrays, - EulerCons const& cons) -{ - init_from_value(exec_space, cons_arrays.d, cons.d); - init_from_value(exec_space, cons_arrays.e, cons.e); - init_from_value(exec_space, cons_arrays.mx0, cons.mx0); - init_from_value(exec_space, cons_arrays.mx1, cons.mx1); - init_from_value(exec_space, cons_arrays.mx2, cons.mx2); -} void set_constant_bytes_processed(benchmark::State& state, std::size_t bytes); diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 5abd02d..0bf1686 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "utils.hpp" template void cons_to_prim( diff --git a/simulations/CMakeLists.txt b/simulations/CMakeLists.txt index 0232772..5b1e7ed 100644 --- a/simulations/CMakeLists.txt +++ b/simulations/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable(euler_simulation euler_simulation.cpp save_npy.cpp) -target_link_libraries(euler_simulation PRIVATE euler_operators) +target_link_libraries(euler_simulation PRIVATE euler_operators euler_utils) diff --git a/simulations/plot.py b/simulations/plot.py index 4e26b39..e32811f 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -14,8 +14,8 @@ # Config # --------------------------------------------------------- FILES = { - # "skx_1": "results/ruche/skx/[455228]_cpus-1_bm_ruche.json", - "skx_rem": "results/ruche/skx/[457078]_skx-PrimToCons_bm_ruche.json", + "skx_kokkos5.0.0": "results/ruche/skx/[457139]_skx-PrimToCons_bm_ruche.json", + # "skx_rem": "results/ruche/skx/[457078]_skx-PrimToCons_bm_ruche.json", # "skx_rem":"results/ruche/skx/[457041]_skx-PrimToCons_bm_ruche.json", # "skx_10": "results/ruche/skx/[453127]_cpus-10-ref_bm_ruche.json", diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b806972..f8caa84 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(GTest REQUIRED) add_executable(euler_tests) target_link_libraries( euler_tests - PRIVATE GTest::gtest_main euler_operators Kokkos::kokkos + PRIVATE GTest::gtest_main euler_operators Kokkos::kokkos euler_utils ) target_sources( euler_tests diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index 3c017bc..4b919fc 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -1,3 +1,4 @@ + #include #include @@ -5,55 +6,7 @@ #include #include -template < - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class LP, - class AP> -void init_from_value_test( - Kokkos::DefaultExecutionSpace const& exec_space, - Kokkos::mdspan, LP, AP> const& array, - ElementType const& value) -{ - Kokkos::parallel_for( - "init_from_value", - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - Kokkos::IndexType>( - exec_space, - {0, 0, 0}, - {array.extent(0), array.extent(1), array.extent(2)}), - KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { - array(i, j, k) = value; - }); -} - - - -template < - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class LP, - class AP> -void init_from_state_test( - Kokkos::DefaultExecutionSpace const& exec_space, - EulerPrimArrays< - Kokkos::mdspan, LP, AP>> const& - prim_arrays, - EulerPrim const& prim) -{ - init_from_value_test(exec_space, prim_arrays.d, prim.d); - init_from_value_test(exec_space, prim_arrays.p, prim.p); - init_from_value_test(exec_space, prim_arrays.ux0, prim.ux0); - init_from_value_test(exec_space, prim_arrays.ux1, prim.ux1); - init_from_value_test(exec_space, prim_arrays.ux2, prim.ux2); -} +#include "utils.hpp" TEST(PrimToCons, ScalarVsVectorized) { @@ -90,7 +43,7 @@ TEST(PrimToCons, ScalarVsVectorized) // --- initialize with non-trivial state --- EulerPrim prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; - init_from_state_test(exec_space, prim_arrays, prim); + init_from_state(exec_space, prim_arrays, prim); exec_space.fence(); // --- run both --- From a582fb7397602463829ab71faaf5ae68e7b93937 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 31 Mar 2026 16:06:19 +0200 Subject: [PATCH 08/56] include utils directory --- utils/CMakeLists.txt | 2 ++ utils/utils.hpp | 76 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 utils/CMakeLists.txt create mode 100644 utils/utils.hpp diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..10bb63a --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(euler_utils INTERFACE) +target_include_directories(euler_utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/utils/utils.hpp b/utils/utils.hpp new file mode 100644 index 0000000..287941b --- /dev/null +++ b/utils/utils.hpp @@ -0,0 +1,76 @@ +#pragma once +#include + +#include "euler_arrays.hpp" + +template < + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class LP, + class AP> +void init_from_value( + Kokkos::DefaultExecutionSpace const& exec_space, + Kokkos::mdspan, LP, AP> const& array, + ElementType const& value) +{ + Kokkos::parallel_for( + "init_from_value", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>( + exec_space, + {0, 0, 0}, + {array.extent(0), array.extent(1), array.extent(2)}), + KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { + array(i, j, k) = value; + }); +} + + + +template < + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class LP, + class AP> +void init_from_state( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays< + Kokkos::mdspan, LP, AP>> const& + prim_arrays, + EulerPrim const& prim) +{ + init_from_value(exec_space, prim_arrays.d, prim.d); + init_from_value(exec_space, prim_arrays.p, prim.p); + init_from_value(exec_space, prim_arrays.ux0, prim.ux0); + init_from_value(exec_space, prim_arrays.ux1, prim.ux1); + init_from_value(exec_space, prim_arrays.ux2, prim.ux2); +} + +template < + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class LP, + class AP> +void init_from_state( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerConsArrays< + Kokkos::mdspan, LP, AP>> const& + cons_arrays, + EulerCons const& cons) +{ + init_from_value(exec_space, cons_arrays.d, cons.d); + init_from_value(exec_space, cons_arrays.e, cons.e); + init_from_value(exec_space, cons_arrays.mx0, cons.mx0); + init_from_value(exec_space, cons_arrays.mx1, cons.mx1); + init_from_value(exec_space, cons_arrays.mx2, cons.mx2); +} From f6a781292e4de35e833119d23b33cf13e33e4d0d Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 1 Apr 2026 10:05:47 +0200 Subject: [PATCH 09/56] pass cons/prim arrays to prim_to_cons kernel, updated plots --- euler_operators/perfect_gas.hpp | 1 - euler_operators/prim_to_cons.hpp | 98 +++++----------- simulations/plot.py | 184 +++++++++++++++++++++---------- test/test_prim_to_cons.cpp | 4 +- 4 files changed, 156 insertions(+), 131 deletions(-) diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 3b31739..b4b7b11 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -22,7 +22,6 @@ class PerfectGas template KOKKOS_FUNCTION S internal_energy(S const /*density*/, S const pressure) const noexcept { - // return pressure / (m_gamma - 1); return pressure * gamma_minus_one_inv; } diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index eeac9e7..2be942d 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -37,36 +37,44 @@ void prim_to_cons( -template +template void prim_to_cons_kernel( Kokkos::DefaultExecutionSpace const& exec_space, - T const* pd, - T const* pp, - T const* pu0, - T const* pu1, - T const* pu2, - T* cd, - T* ce, - T* cm0, - T* cm1, - T* cm2, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, IndexType nx_begin, IndexType nx_end, - IndexType ny, - IndexType nz, PerfectGas const& eos) { namespace KE = Kokkos::Experimental; constexpr IndexType width = SimdType::size(); IndexType const nx_blocks = (nx_end - nx_begin) / width; - IndexType const nx = nx_end; // full nx for stride computation + IndexType const nx = prim_arrays.d.extent(0); + IndexType const ny = prim_arrays.d.extent(1); + IndexType const nz = prim_arrays.d.extent(2); + + T const* pd = prim_arrays.d.data_handle(); + T const* pp = prim_arrays.p.data_handle(); + T const* pu0 = prim_arrays.ux0.data_handle(); + T const* pu1 = prim_arrays.ux1.data_handle(); + T const* pu2 = prim_arrays.ux2.data_handle(); + T* cd = cons_arrays.d.data_handle(); + T* ce = cons_arrays.e.data_handle(); + T* cm0 = cons_arrays.mx0.data_handle(); + T* cm1 = cons_arrays.mx1.data_handle(); + T* cm2 = cons_arrays.mx2.data_handle(); Kokkos::parallel_for( "prim_to_cons_kernel", - Kokkos::MDRangePolicy>({0, 0, 0}, {nx_blocks, ny, nz}), + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; SimdType d(pd + base, KE::simd_flag_default); @@ -79,9 +87,6 @@ void prim_to_cons_kernel( SimdType m2 = d * ux2; SimdType e_tot = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2) + eos.internal_energy(d, p); - - - KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); KE::simd_unchecked_store(e_tot, ce + base, KE::simd_flag_default); KE::simd_unchecked_store(m0, cm0 + base, KE::simd_flag_default); @@ -107,59 +112,12 @@ void prim_to_cons_vec( using simd_t = KE::simd; using simd_scalar_t = KE::basic_simd; - IndexType const nx = prim_arrays.d.extent(0); - IndexType const ny = prim_arrays.d.extent(1); - IndexType const nz = prim_arrays.d.extent(2); - - T const* pd = prim_arrays.d.data_handle(); - T const* pp = prim_arrays.p.data_handle(); - T const* pu0 = prim_arrays.ux0.data_handle(); - T const* pu1 = prim_arrays.ux1.data_handle(); - T const* pu2 = prim_arrays.ux2.data_handle(); - T* cd = cons_arrays.d.data_handle(); - T* ce = cons_arrays.e.data_handle(); - T* cm0 = cons_arrays.mx0.data_handle(); - T* cm1 = cons_arrays.mx1.data_handle(); - T* cm2 = cons_arrays.mx2.data_handle(); - IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); - prim_to_cons_kernel( - exec_space, - pd, - pp, - pu0, - pu1, - pu2, - cd, - ce, - cm0, - cm1, - cm2, - IndexType(0), - vec_end, - ny, - nz, - eos); + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); if (vec_end < nx) { - prim_to_cons_kernel( - exec_space, - pd, - pp, - pu0, - pu1, - pu2, - cd, - ce, - cm0, - cm1, - cm2, - vec_end, - nx, - ny, - nz, - eos); + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); } } diff --git a/simulations/plot.py b/simulations/plot.py index e32811f..6824b1b 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -14,15 +14,9 @@ # Config # --------------------------------------------------------- FILES = { - "skx_kokkos5.0.0": "results/ruche/skx/[457139]_skx-PrimToCons_bm_ruche.json", - # "skx_rem": "results/ruche/skx/[457078]_skx-PrimToCons_bm_ruche.json", - # "skx_rem":"results/ruche/skx/[457041]_skx-PrimToCons_bm_ruche.json", - - # "skx_10": "results/ruche/skx/[453127]_cpus-10-ref_bm_ruche.json", - # "skx_20": "results/ruche/skx/[453128]_cpus-20-ref_bm_ruche.json", - # "skx_30": "results/ruche/skx/[453129]_cpus-30-ref_bm_ruche.json", - # "skx_40": "results/ruche/skx/[453130]_cpus-40-ref_bm_ruche.json", -} + "skx_ref": "results/ruche/skx/[460375]_skx-PrimToCons_bm_ruche.json", + "skx_ref2": "results/ruche/skx/[460449]_skx-PrimToCons_bm_ruche.json", + } OUT_DIR = "results/plots" @@ -34,43 +28,76 @@ def extract_label(path): +# %% +import json +import pandas as pd +import matplotlib.pyplot as plt +from pathlib import Path + +BYTES_PER_CELL = 10 * 8 +CACHE_COLORS = {1: "green", 2: "orange", 3: "red"} + + +def load_one(path): + with open(path) as f: + raw = json.load(f) + caches = { + c["level"]: c["size"] + for c in raw["context"]["caches"] + if c["type"] == "Unified" + } + rows = [] + for b in raw["benchmarks"]: + name = b["name"] + rows.append({ + "benchmark": name.split("/")[0], + "size": int(name.split("/")[-1]), + "cells_per_second": b.get("cells_per_second"), + "bytes_per_second": b.get("bytes_per_second"), + "real_time_ns": b.get("real_time"), + }) + return pd.DataFrame(rows), caches + + +def _draw_cache_lines(ax, caches): + for level, size_bytes in sorted(caches.items()): + n_cache = (size_bytes / BYTES_PER_CELL) ** (1 / 3) + color = CACHE_COLORS.get(level, "gray") + ax.axvline( + n_cache, + linestyle="--", + color=color, + alpha=0.7, + label=f"L{level} ({size_bytes // 1024} KB) → n≈{n_cache:.0f}", + ) + + +def _plot_series(ax, df_series, color, label, y_key, alpha=1.0): + aligned = df_series[df_series["size"] % 8 == 0] + unaligned = df_series[df_series["size"] % 8 != 0] + ax.plot(df_series["size"], df_series[y_key], "-", color=color, + label=label, alpha=alpha) + ax.scatter(aligned["size"], aligned[y_key], marker="o", + color=color, zorder=5, alpha=alpha) + ax.scatter(unaligned["size"], unaligned[y_key], marker="x", + color=color, zorder=5, alpha=alpha) -def plot_scalar_vs_vector(files, out_dir): - import json - import pandas as pd - import matplotlib.pyplot as plt - from pathlib import Path - - BYTES_PER_CELL = 10 * 8 - cache_colors = {1: "green", 2: "orange", 3: "red"} - - def load_one(path): - with open(path) as f: - raw = json.load(f) - caches = {c["level"]: c["size"] for c in raw["context"]["caches"] if c["type"] == "Unified"} - rows = [] - for b in raw["benchmarks"]: - name = b["name"] - rows.append({ - "benchmark": name.split("/")[0], - "size": int(name.split("/")[-1]), - "cells_per_second": b.get("cells_per_second"), - "bytes_per_second": b.get("bytes_per_second"), - }) - return pd.DataFrame(rows), caches +def plot_scalar_vs_vector(files, out_dir): out_dir = Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) - # collect all benchmark names across every file + # collect benchmark names present in every file all_names = set() for path in files.values(): df, _ = load_one(path) all_names.update(df["benchmark"].unique()) + base_names = [b for b in all_names if b + "Vectorized" in all_names] for base_name in base_names: vec_name = base_name + "Vectorized" + for environment, path in files.items(): df, caches = load_one(path) bm_label = extract_label(path) @@ -82,32 +109,73 @@ def load_one(path): print(f"skipping {base_name} for {environment}") continue - fig, ax1 = plt.subplots(figsize=(9, 5)) - ax2 = ax1.twinx() - - for df_series, color, label in [(s, "C0", "scalar"), (v, "C1", "vectorized")]: - aligned = df_series[df_series["size"] % 8 == 0] - unaligned = df_series[df_series["size"] % 8 != 0] - ax1.plot(df_series["size"], df_series["cells_per_second"], "-", color=color, label=label + " cells/s") - ax2.plot(df_series["size"], df_series["bytes_per_second"], "--", color=color, label=label + " bytes/s", alpha=0.4) - ax1.scatter(aligned["size"], aligned["cells_per_second"], marker="o", color=color, zorder=5) - ax1.scatter(unaligned["size"], unaligned["cells_per_second"], marker="x", color=color, zorder=5) - ax2.scatter(aligned["size"], aligned["bytes_per_second"], marker="o", color=color, alpha=0.4) - ax2.scatter(unaligned["size"], unaligned["bytes_per_second"], marker="x", color=color, alpha=0.4) - - for level, size_bytes in sorted(caches.items()): - n_cache = (size_bytes / BYTES_PER_CELL) ** (1/3) - color = cache_colors.get(level, "gray") - ax1.axvline(n_cache, linestyle="--", color=color, alpha=0.7, - label=f"L{level} ({size_bytes // 1024} KB) → n≈{n_cache:.0f}") - - ax1.set_title(f"{base_name} — {bm_label}") - ax1.set_xlabel("n (cube width in cells)") - ax1.set_ylabel("cells/s") - ax2.set_ylabel("bytes/s") - ax1.legend(fontsize=8) - ax1.grid(True) + fig, (ax_left, ax_right) = plt.subplots( + 1, 2, figsize=(16, 5), sharey=False + ) + fig.suptitle(f"{base_name} — {bm_label}", fontsize=12) + + # ── left plot: throughput ───────────────────────────────────── + ax_bytes = ax_left.twinx() + + for df_series, color, label in [ + (s, "C0", "scalar"), + (v, "C1", "vectorized"), + ]: + _plot_series(ax_left, df_series, color, f"{label} cells/s", + "cells_per_second") + _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", + "bytes_per_second", alpha=0.4) + + _draw_cache_lines(ax_left, caches) + + ax_left.set_xlabel("n (cube width in cells)") + ax_left.set_ylabel("cells / s") + ax_bytes.set_ylabel("bytes / s") + ax_left.set_title("Throughput") + ax_left.legend(fontsize=7) + ax_left.grid(True, alpha=0.3) + + # ── right plot: wall time + speedup ────────────────────────── + ax_speedup = ax_right.twinx() + + _plot_series(ax_right, s, "C0", "scalar ns", "real_time_ns") + _plot_series(ax_right, v, "C1", "vectorized ns", "real_time_ns") + + # speedup: scalar / vectorized on shared sizes + merged = pd.merge( + s[["size", "real_time_ns"]], + v[["size", "real_time_ns"]], + on="size", + suffixes=("_s", "_v"), + ).dropna() + merged["speedup"] = merged["real_time_ns_s"] / merged["real_time_ns_v"] + + ax_speedup.plot( + merged["size"], merged["speedup"], + "-", color="C2", label="speedup (×)", linewidth=1.5, + ) + ax_speedup.scatter( + merged["size"], merged["speedup"], + marker="D", color="C2", zorder=5, s=25, + ) + ax_speedup.axhline(1.0, linestyle=":", color="C2", alpha=0.5) + + _draw_cache_lines(ax_right, caches) + + ax_right.set_xlabel("n (cube width in cells)") + ax_right.set_ylabel("real time (ns)") + ax_speedup.set_ylabel("speedup (×)") + ax_right.set_title("Wall Time & Speedup") + + # merge legends from both right-plot axes + lines_r, labels_r = ax_right.get_legend_handles_labels() + lines_s, labels_s = ax_speedup.get_legend_handles_labels() + ax_right.legend(lines_r + lines_s, labels_r + labels_s, fontsize=7) + ax_right.grid(True, alpha=0.3) + plt.tight_layout() plt.savefig(out_dir / f"{bm_label}_{base_name}.png", dpi=200) plt.close() + + plot_scalar_vs_vector(FILES, OUT_DIR) diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index 4b919fc..f84b1ec 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -8,12 +8,12 @@ #include "utils.hpp" -TEST(PrimToCons, ScalarVsVectorized) +TEST(PrimToConsRemainder, ScalarVsVectorized) { using real_t = double; using index_t = int; - int const n = 16; + int const n = 23; Kokkos::DefaultExecutionSpace exec_space; PerfectGas eos(1.4); From 794dc3d7b83ba28182251848f6603c61563c16a9 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 1 Apr 2026 14:07:30 +0200 Subject: [PATCH 10/56] simd compatible store and load functions --- euler_operators/euler_arrays.hpp | 51 +++++++++++++++++++++++++++++++- euler_operators/prim_to_cons.hpp | 32 +++----------------- simulations/plot.py | 50 +++++++++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 4a0a719..32b6fb2 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -4,6 +4,7 @@ #include #include +#include template struct EulerPrim @@ -175,6 +176,31 @@ KOKKOS_FUNCTION EulerPrim> load( .ux1 = prim_ptrs.ux1[i], .ux2 = prim_ptrs.ux2[i]}; } +template < + class SimdType, + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class AP> +KOKKOS_FUNCTION EulerPrim load( + EulerPrimArrays, + Kokkos::layout_left, // layout_left guarantees contiguous x-stride + AP>> const& prim_arrays, + IndexType const base) noexcept +{ + namespace KE = Kokkos::Experimental; + return { + .d = SimdType(prim_arrays.d.data_handle() + base, KE::simd_flag_default), + .p = SimdType(prim_arrays.p.data_handle() + base, KE::simd_flag_default), + .ux0 = SimdType(prim_arrays.ux0.data_handle() + base, KE::simd_flag_default), + .ux1 = SimdType(prim_arrays.ux1.data_handle() + base, KE::simd_flag_default), + .ux2 = SimdType(prim_arrays.ux2.data_handle() + base, KE::simd_flag_default), + }; +} template < class ElementType, @@ -363,7 +389,30 @@ KOKKOS_FUNCTION void store( cons_ptrs.mx1[i] = cons.mx1; cons_ptrs.mx2[i] = cons.mx2; } - +template < + class SimdType, + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class AP> +KOKKOS_FUNCTION void store( + EulerCons const& cons, + EulerConsArrays, + Kokkos::layout_left, + AP>> const& cons_arrays, + IndexType const base) noexcept +{ + namespace KE = Kokkos::Experimental; + KE::simd_unchecked_store(cons.d, cons_arrays.d.data_handle() + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.e, cons_arrays.e.data_handle() + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx0, cons_arrays.mx0.data_handle() + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx1, cons_arrays.mx1.data_handle() + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx2, cons_arrays.mx2.data_handle() + base, KE::simd_flag_default); +} template std::size_t size( EulerPrimArrays> const& prim_arrays) noexcept diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 2be942d..0ac8e10 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -2,8 +2,6 @@ #include #include -#include -#include #include #include @@ -52,23 +50,12 @@ void prim_to_cons_kernel( IndexType nx_end, PerfectGas const& eos) { - namespace KE = Kokkos::Experimental; constexpr IndexType width = SimdType::size(); IndexType const nx_blocks = (nx_end - nx_begin) / width; IndexType const nx = prim_arrays.d.extent(0); IndexType const ny = prim_arrays.d.extent(1); IndexType const nz = prim_arrays.d.extent(2); - T const* pd = prim_arrays.d.data_handle(); - T const* pp = prim_arrays.p.data_handle(); - T const* pu0 = prim_arrays.ux0.data_handle(); - T const* pu1 = prim_arrays.ux1.data_handle(); - T const* pu2 = prim_arrays.ux2.data_handle(); - T* cd = cons_arrays.d.data_handle(); - T* ce = cons_arrays.e.data_handle(); - T* cm0 = cons_arrays.mx0.data_handle(); - T* cm1 = cons_arrays.mx1.data_handle(); - T* cm2 = cons_arrays.mx2.data_handle(); Kokkos::parallel_for( "prim_to_cons_kernel", @@ -77,21 +64,10 @@ void prim_to_cons_kernel( Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; - SimdType d(pd + base, KE::simd_flag_default); - SimdType p(pp + base, KE::simd_flag_default); - SimdType ux0(pu0 + base, KE::simd_flag_default); - SimdType ux1(pu1 + base, KE::simd_flag_default); - SimdType ux2(pu2 + base, KE::simd_flag_default); - SimdType m0 = d * ux0; - SimdType m1 = d * ux1; - SimdType m2 = d * ux2; - SimdType e_tot - = T(0.5) * (m0 * ux0 + m1 * ux1 + m2 * ux2) + eos.internal_energy(d, p); - KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); - KE::simd_unchecked_store(e_tot, ce + base, KE::simd_flag_default); - KE::simd_unchecked_store(m0, cm0 + base, KE::simd_flag_default); - KE::simd_unchecked_store(m1, cm1 + base, KE::simd_flag_default); - KE::simd_unchecked_store(m2, cm2 + base, KE::simd_flag_default); + + EulerPrim const prim = load(prim_arrays, base); + EulerCons const cons = to_cons(prim, eos.internal_energy(prim.d, prim.p)); + store(cons, cons_arrays, base); }); } diff --git a/simulations/plot.py b/simulations/plot.py index 6824b1b..02077f9 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -14,9 +14,13 @@ # Config # --------------------------------------------------------- FILES = { - "skx_ref": "results/ruche/skx/[460375]_skx-PrimToCons_bm_ruche.json", + "skx_store": "results/ruche/skx/[460979]_skx-PrimToCons_bm_ruche.json", "skx_ref2": "results/ruche/skx/[460449]_skx-PrimToCons_bm_ruche.json", + "skx_ptr": "results/ruche/skx/[462651]_skx-PrimToCons_bm_ruche.json", + + } + OUT_DIR = "results/plots" @@ -132,6 +136,8 @@ def plot_scalar_vs_vector(files, out_dir): ax_left.set_ylabel("cells / s") ax_bytes.set_ylabel("bytes / s") ax_left.set_title("Throughput") + ax_right.set_xscale("log") + ax_right.set_yscale("log") ax_left.legend(fontsize=7) ax_left.grid(True, alpha=0.3) @@ -177,5 +183,45 @@ def plot_scalar_vs_vector(files, out_dir): plt.savefig(out_dir / f"{bm_label}_{base_name}.png", dpi=200) plt.close() - +def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b"): + df_a, _ = load_one(path_a) + df_b, _ = load_one(path_b) + + merged = pd.merge( + df_a, + df_b, + on=["benchmark", "size"], + suffixes=(f"_{label_a}", f"_{label_b}"), + how="inner", + ) + + merged["real_time_speedup"] = ( + merged[f"real_time_ns_{label_a}"] / merged[f"real_time_ns_{label_b}"] + ) + + for col in ("cells_per_second", "bytes_per_second"): + a_col = f"{col}_{label_a}" + b_col = f"{col}_{label_b}" + if a_col in merged and b_col in merged: + merged[f"{col}_speedup"] = merged[b_col] / merged[a_col] + + mean_row = merged.mean(numeric_only=True).to_frame().T + mean_row["benchmark"] = "MEAN" + mean_row["size"] = pd.NA + merged = pd.concat([merged, mean_row], ignore_index=True) + + rounding = {c: 5 for c in merged.columns if "speedup" in c} + rounding |= {c: 5 for c in merged.columns if "time" in c} + rounding |= {c: 5 for c in merged.columns if "cells_per_second" in c or "bytes_per_second" in c} + merged = merged.round(rounding) + + + + out_csv = Path(out_csv) + out_csv.parent.mkdir(parents=True, exist_ok=True) + merged.to_csv(out_csv, index=False) + return merged +# %% plot_scalar_vs_vector(FILES, OUT_DIR) +# compare_benchmarks(FILES["skx_ref2"], FILES["skx_store"], "store.csv", "ref2", "store") + From ec4e16c6e9700b7b32433aec18ea02ecbbc2fd1d Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 2 Apr 2026 10:20:02 +0200 Subject: [PATCH 11/56] to_cons optimized, negligible slowdown comapred to fully inlined version --- euler_operators/euler_arrays.hpp | 54 +++++++++++++++----------------- euler_operators/prim_to_cons.hpp | 19 +++++++++-- setups/ruche/a100/run_sim.sh | 20 ++++++++++++ setups/ruche/skx/run_bench.sh | 2 +- setups/ruche/v100/run_sim.sh | 20 ++++++++++++ simulations/plot.py | 47 +++++++++++++++++++++------ 6 files changed, 119 insertions(+), 43 deletions(-) create mode 100644 setups/ruche/a100/run_sim.sh create mode 100644 setups/ruche/v100/run_sim.sh diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 32b6fb2..5b3c042 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -4,6 +4,7 @@ #include #include +#include #include template @@ -54,15 +55,6 @@ KOKKOS_FUNCTION constexpr T internal_energy(EulerCons const& cons) noexcept return cons.e - kinetic_energy(cons); } -template -KOKKOS_FUNCTION EulerCons to_cons(EulerPrim const& prim, T const int_e) noexcept -{ - return {.d = prim.d, - .e = kinetic_energy(prim) + int_e, - .mx0 = prim.d * prim.ux0, - .mx1 = prim.d * prim.ux1, - .mx2 = prim.d * prim.ux2}; -} template KOKKOS_FUNCTION EulerPrim to_prim(EulerCons const& cons, T const p) noexcept @@ -75,6 +67,20 @@ KOKKOS_FUNCTION EulerPrim to_prim(EulerCons const& cons, T const p) noexce .ux2 = cons.mx2 * vol_spe}; } +template +KOKKOS_FUNCTION EulerCons to_cons(EulerPrim const prim, T const int_e) noexcept +{ + T m0 = prim.d * prim.ux0; // reused below + T m1 = prim.d * prim.ux1; + T m2 = prim.d * prim.ux2; + + T e_kin = T(0.5) * (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2); + + + return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; +} + + template struct EulerPrimArrays { @@ -389,29 +395,19 @@ KOKKOS_FUNCTION void store( cons_ptrs.mx1[i] = cons.mx1; cons_ptrs.mx2[i] = cons.mx2; } -template < - class SimdType, - class ElementType, - class IndexType, - std::size_t E0, - std::size_t E1, - std::size_t E2, - class AP> -KOKKOS_FUNCTION void store( + +template +KOKKOS_FORCEINLINE_FUNCTION void store( EulerCons const& cons, - EulerConsArrays, - Kokkos::layout_left, - AP>> const& cons_arrays, - IndexType const base) noexcept + EulerConsArrays const& cons_ptrs, + std::size_t const base) noexcept { namespace KE = Kokkos::Experimental; - KE::simd_unchecked_store(cons.d, cons_arrays.d.data_handle() + base, KE::simd_flag_default); - KE::simd_unchecked_store(cons.e, cons_arrays.e.data_handle() + base, KE::simd_flag_default); - KE::simd_unchecked_store(cons.mx0, cons_arrays.mx0.data_handle() + base, KE::simd_flag_default); - KE::simd_unchecked_store(cons.mx1, cons_arrays.mx1.data_handle() + base, KE::simd_flag_default); - KE::simd_unchecked_store(cons.mx2, cons_arrays.mx2.data_handle() + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.d, cons_ptrs.d + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.e, cons_ptrs.e + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx0, cons_ptrs.mx0 + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx1, cons_ptrs.mx1 + base, KE::simd_flag_default); + KE::simd_unchecked_store(cons.mx2, cons_ptrs.mx2 + base, KE::simd_flag_default); } template std::size_t size( diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 0ac8e10..6002faa 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -57,6 +57,16 @@ void prim_to_cons_kernel( IndexType const nz = prim_arrays.d.extent(2); + + T* cd = cons_arrays.d.data_handle(); + T* ce = cons_arrays.e.data_handle(); + T* cm0 = cons_arrays.mx0.data_handle(); + T* cm1 = cons_arrays.mx1.data_handle(); + T* cm2 = cons_arrays.mx2.data_handle(); + + auto const cons_ptrs = EulerConsArrays {cd, ce, cm0, cm1, cm2}; + + Kokkos::parallel_for( "prim_to_cons_kernel", Kokkos::MDRangePolicy< @@ -65,9 +75,12 @@ void prim_to_cons_kernel( KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; - EulerPrim const prim = load(prim_arrays, base); - EulerCons const cons = to_cons(prim, eos.internal_energy(prim.d, prim.p)); - store(cons, cons_arrays, base); + EulerPrim const prim = load(prim_arrays, base); + + EulerCons const cons = to_cons(prim, eos.internal_energy(prim.d, prim.p)); + + + store(cons, cons_ptrs, base); }); } diff --git a/setups/ruche/a100/run_sim.sh b/setups/ruche/a100/run_sim.sh new file mode 100644 index 0000000..44bc2b2 --- /dev/null +++ b/setups/ruche/a100/run_sim.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#SBATCH --job-name=sim_skx +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:05:00 +#SBATCH --partition=gpua100 + +module purge +module load \ + gcc/13.4.0/gcc-15.1.0 \ + cmake/3.31.9/gcc-15.1.0 \ + cuda/12.8.1/none-none + +set -x +cd ${SLURM_SUBMIT_DIR} + +mkdir -p slurm_out results/ruche/skx + +./build-a100/simulations/euler_simulation diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index 25f4626..ef33858 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -27,4 +27,4 @@ BENCHMARK_FILTER=${1:-""} ./build-skx/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/skx/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}_bm_ruche.json" + --benchmark_out=./results/ruche/skx/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}.json" diff --git a/setups/ruche/v100/run_sim.sh b/setups/ruche/v100/run_sim.sh new file mode 100644 index 0000000..62d5158 --- /dev/null +++ b/setups/ruche/v100/run_sim.sh @@ -0,0 +1,20 @@ +#!/bin/bash +#SBATCH --job-name=sim_skx +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:05:00 +#SBATCH --partition=gpu_test + +module purge +module load \ + gcc/13.4.0/gcc-15.1.0 \ + cmake/3.31.9/gcc-15.1.0 \ + cuda/12.8.1/none-none + +set -x +cd ${SLURM_SUBMIT_DIR} + +mkdir -p slurm_out + +./build-a100/simulations/euler_simulation diff --git a/simulations/plot.py b/simulations/plot.py index 02077f9..9ce62c1 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -13,22 +13,35 @@ # --------------------------------------------------------- # Config # --------------------------------------------------------- -FILES = { - "skx_store": "results/ruche/skx/[460979]_skx-PrimToCons_bm_ruche.json", - "skx_ref2": "results/ruche/skx/[460449]_skx-PrimToCons_bm_ruche.json", - "skx_ptr": "results/ruche/skx/[462651]_skx-PrimToCons_bm_ruche.json", - - } OUT_DIR = "results/plots" +import os +import glob + +RES_DIR = "results/ruche/skx/" + +def latest_result(res_dir=RES_DIR, pattern="*.json"): + files = glob.glob(os.path.join(res_dir, pattern)) + print(files) + if not files: + raise FileNotFoundError(f"No files matching {pattern} in {res_dir}") + return max(files, key=os.path.getmtime) + +def result_by_job_id(job_id, res_dir=RES_DIR): + prefix = f"[{job_id}]" + files = os.listdir(res_dir) + for f in files: + if f.startswith(prefix): + return os.path.join(res_dir, f) + raise FileNotFoundError(f"No result found for job {job_id} in {res_dir}") def extract_label(path): name = Path(path).name label = name.split("_")[1] timestamp = name.split("[")[1].split("]")[0] - return timestamp + "_" + label + return timestamp + "_" @@ -180,10 +193,13 @@ def plot_scalar_vs_vector(files, out_dir): ax_right.grid(True, alpha=0.3) plt.tight_layout() - plt.savefig(out_dir / f"{bm_label}_{base_name}.png", dpi=200) + print("bm_label = " , bm_label) + save_name = out_dir / f"{bm_label}_{base_name}.png" + print("saving : ", save_name) + plt.savefig(save_name, dpi=200) plt.close() -def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b"): +def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): df_a, _ = load_one(path_a) df_b, _ = load_one(path_b) @@ -205,6 +221,10 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b"): if a_col in merged and b_col in merged: merged[f"{col}_speedup"] = merged[b_col] / merged[a_col] + merged = merged[merged.benchmark == "PrimToConsVectorized"] + if cols: + merged = merged[cols] + mean_row = merged.mean(numeric_only=True).to_frame().T mean_row["benchmark"] = "MEAN" mean_row["size"] = pd.NA @@ -222,6 +242,13 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b"): merged.to_csv(out_csv, index=False) return merged # %% + + +FILES = { +"skx_ref": result_by_job_id(463476), + # " skx_load" : result_by_job_id(468185) +"skx_new": latest_result(), +} plot_scalar_vs_vector(FILES, OUT_DIR) -# compare_benchmarks(FILES["skx_ref2"], FILES["skx_store"], "store.csv", "ref2", "store") +compare_benchmarks(FILES["skx_ref"], FILES["skx_new"], "store.csv", "ch", "new", cols=["benchmark", "size", "real_time_speedup"]) From d3c6b65ab9f61403c117c5ad79c811634d5bea54 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 2 Apr 2026 13:08:29 +0200 Subject: [PATCH 12/56] forcw inline for load, to_cons, store --- benchmarks/benchmark_prim_to_cons.cpp | 2 +- euler_operators/prim_to_cons.hpp | 2 +- simulations/plot.py | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index aa5e797..7d5d097 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -4,11 +4,11 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 6002faa..b87acae 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -73,7 +73,7 @@ void prim_to_cons_kernel( Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; + IndexType const base = prim_arrays.d.mapping()(nx_begin + bi * width, j, k); EulerPrim const prim = load(prim_arrays, base); diff --git a/simulations/plot.py b/simulations/plot.py index 9ce62c1..94e59bc 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -245,10 +245,8 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N FILES = { -"skx_ref": result_by_job_id(463476), - # " skx_load" : result_by_job_id(468185) "skx_new": latest_result(), } plot_scalar_vs_vector(FILES, OUT_DIR) -compare_benchmarks(FILES["skx_ref"], FILES["skx_new"], "store.csv", "ch", "new", cols=["benchmark", "size", "real_time_speedup"]) +compare_benchmarks(result_by_job_id(463476), FILES["skx_new"], "store.csv", "ch", "new", cols=["benchmark", "size", "real_time_speedup"]) From f6b1dc91575dcbd6254eff68871822ca5a294f2f Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 3 Apr 2026 14:38:24 +0200 Subject: [PATCH 13/56] inlined vectorized prim_to_cons passing tests --- euler_operators/euler_arrays.hpp | 2 +- euler_operators/prim_to_cons.hpp | 78 ++++++++++++++++++++++++++++++-- setups/ruche/a100/run.sh | 2 +- setups/ruche/a100/run_sim.sh | 7 +-- setups/ruche/a100/run_test.sh | 21 +++++++++ test/CMakeLists.txt | 2 +- test/test_prim_to_cons.cpp | 18 ++++---- 7 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 setups/ruche/a100/run_test.sh diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 7cbbaf0..535c6ac 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -74,7 +74,7 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons to_cons(EulerPrim const prim, T cons T m1 = prim.d * prim.ux1; T m2 = prim.d * prim.ux2; - T e_kin = T(0.5) * (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2); + T e_kin = (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2) * T(0.5); return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; } diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index b87acae..62542ce 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -1,10 +1,14 @@ #pragma once +#include +#include + #include #include -#include #include +#include "euler_arrays.hpp" + template void prim_to_cons( Kokkos::DefaultExecutionSpace const& exec_space, @@ -84,6 +88,71 @@ void prim_to_cons_kernel( }); } +template +void prim_to_cons_kernel_inline( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + IndexType nx_begin, + IndexType nx_end, + PerfectGas const& eos) +{ + namespace KE = Kokkos::Experimental; + constexpr IndexType width = SimdType::size(); + IndexType const nx_blocks = (nx_end - nx_begin) / width; + IndexType const nx = prim_arrays.d.extent(0); + IndexType const ny = prim_arrays.d.extent(1); + IndexType const nz = prim_arrays.d.extent(2); + + T const* pd = prim_arrays.d.data_handle(); + T const* pp = prim_arrays.p.data_handle(); + T const* pu0 = prim_arrays.ux0.data_handle(); + T const* pu1 = prim_arrays.ux1.data_handle(); + T const* pu2 = prim_arrays.ux2.data_handle(); + T* cd = cons_arrays.d.data_handle(); + T* ce = cons_arrays.e.data_handle(); + T* cm0 = cons_arrays.mx0.data_handle(); + T* cm1 = cons_arrays.mx1.data_handle(); + T* cm2 = cons_arrays.mx2.data_handle(); + + T gamma_inv_minus_one = 1 / (1.4 - 1); + + // Before the parallel_for + Kokkos::parallel_for( + "prim_to_cons_kernel", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { + IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; + SimdType d = pd[base]; + SimdType p = pp[base]; + SimdType ux0 = pu0[base]; + SimdType ux1 = pu1[base]; + SimdType ux2 = pu2[base]; + SimdType m0 = d * ux0; + SimdType m1 = d * ux1; + SimdType m2 = d * ux2; + SimdType e_int = p * SimdType(gamma_inv_minus_one); + SimdType e_kin = (m0 * ux0 + m1 * ux1 + m2 * ux2) * SimdType(0.5); + SimdType e_tot = e_int + e_kin; + + KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); + + KE::simd_unchecked_store(e_tot, ce + base, KE::simd_flag_default); + KE::simd_unchecked_store(m0, cm0 + base, KE::simd_flag_default); + KE::simd_unchecked_store(m1, cm1 + base, KE::simd_flag_default); + KE::simd_unchecked_store(m2, cm2 + base, KE::simd_flag_default); + }); + exec_space.fence(); +} + template void prim_to_cons_vec( Kokkos::DefaultExecutionSpace const& exec_space, @@ -104,9 +173,12 @@ void prim_to_cons_vec( IndexType const nx = prim_arrays.d.extent(0); IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); - prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); + + prim_to_cons_kernel_inline< + simd_t>(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); if (vec_end < nx) { - prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); + prim_to_cons_kernel_inline< + simd_scalar_t>(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); } } diff --git a/setups/ruche/a100/run.sh b/setups/ruche/a100/run.sh index 2bdefff..9c4423f 100644 --- a/setups/ruche/a100/run.sh +++ b/setups/ruche/a100/run.sh @@ -2,7 +2,7 @@ #SBATCH --job-name=benchmark_a100 #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 -#SBATCH --cpus-per-task=4 +#SBATCH --cpus-per-task=1 #SBATCH --time=00:30:00 #SBATCH --partition=gpua100 #SBATCH --gres=gpu:1 diff --git a/setups/ruche/a100/run_sim.sh b/setups/ruche/a100/run_sim.sh index 44bc2b2..6cfe062 100644 --- a/setups/ruche/a100/run_sim.sh +++ b/setups/ruche/a100/run_sim.sh @@ -1,10 +1,11 @@ #!/bin/bash -#SBATCH --job-name=sim_skx +#SBATCH --job-name=test_a100 #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=1 #SBATCH --time=00:05:00 #SBATCH --partition=gpua100 +#SBATCH --gres=gpu:1 module purge module load \ @@ -15,6 +16,6 @@ module load \ set -x cd ${SLURM_SUBMIT_DIR} -mkdir -p slurm_out results/ruche/skx +mkdir -p slurm_out results/ruche/a100 -./build-a100/simulations/euler_simulation +./build-a100/tests/eurler_test diff --git a/setups/ruche/a100/run_test.sh b/setups/ruche/a100/run_test.sh new file mode 100644 index 0000000..f4a1a11 --- /dev/null +++ b/setups/ruche/a100/run_test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +#SBATCH --job-name=test_a100 +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:01:00 +#SBATCH --partition=gpua100 +#SBATCH --gres=gpu:1 + +module purge +module load \ + gcc/13.4.0/gcc-15.1.0 \ + cmake/3.31.9/gcc-15.1.0 \ + cuda/12.8.1/none-none + +set -x +cd ${SLURM_SUBMIT_DIR} + +mkdir -p slurm_out results/ruche/a100 + +./build-a100/test/euler_tests diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f8caa84..11449eb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,4 +15,4 @@ target_sources( enable_testing() include(GoogleTest) -gtest_discover_tests(euler_tests) +gtest_discover_tests(euler_tests DISCOVERY_MODE PRE_TEST) diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index f84b1ec..c1e96c4 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -13,7 +13,7 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) using real_t = double; using index_t = int; - int const n = 23; + int const n = 16; Kokkos::DefaultExecutionSpace exec_space; PerfectGas eos(1.4); @@ -53,17 +53,17 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) auto ref_h = EulerConsArrays { .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.d), - .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx0), - .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx1), - .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx2), - .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.e)}; + .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.e), + .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx0), + .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx1), + .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx2)}; auto vec_h = EulerConsArrays { .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.d), - .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx0), - .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx1), - .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx2), - .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.e)}; + .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.e), + .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx0), + .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx1), + .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx2)}; double const tol = 1e-12; From d62f344a0235a3d33ed39a8bb8246667970aa7dc Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 3 Apr 2026 15:00:21 +0200 Subject: [PATCH 14/56] prim_to_cons_vec passing tests with remainders --- euler_operators/perfect_gas.hpp | 2 +- euler_operators/prim_to_cons.hpp | 70 +------------------------------- test/test_prim_to_cons.cpp | 2 +- 3 files changed, 4 insertions(+), 70 deletions(-) diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index b4b7b11..ce8a584 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -22,7 +22,7 @@ class PerfectGas template KOKKOS_FUNCTION S internal_energy(S const /*density*/, S const pressure) const noexcept { - return pressure * gamma_minus_one_inv; + return pressure * S(gamma_minus_one_inv); } KOKKOS_FUNCTION T pressure(T const /*density*/, T const int_e) const noexcept diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 62542ce..bd17ef4 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -88,70 +88,6 @@ void prim_to_cons_kernel( }); } -template -void prim_to_cons_kernel_inline( - Kokkos::DefaultExecutionSpace const& exec_space, - EulerPrimArrays, - Kokkos::layout_left>> const& prim_arrays, - EulerConsArrays, - Kokkos::layout_left>> const& cons_arrays, - IndexType nx_begin, - IndexType nx_end, - PerfectGas const& eos) -{ - namespace KE = Kokkos::Experimental; - constexpr IndexType width = SimdType::size(); - IndexType const nx_blocks = (nx_end - nx_begin) / width; - IndexType const nx = prim_arrays.d.extent(0); - IndexType const ny = prim_arrays.d.extent(1); - IndexType const nz = prim_arrays.d.extent(2); - - T const* pd = prim_arrays.d.data_handle(); - T const* pp = prim_arrays.p.data_handle(); - T const* pu0 = prim_arrays.ux0.data_handle(); - T const* pu1 = prim_arrays.ux1.data_handle(); - T const* pu2 = prim_arrays.ux2.data_handle(); - T* cd = cons_arrays.d.data_handle(); - T* ce = cons_arrays.e.data_handle(); - T* cm0 = cons_arrays.mx0.data_handle(); - T* cm1 = cons_arrays.mx1.data_handle(); - T* cm2 = cons_arrays.mx2.data_handle(); - - T gamma_inv_minus_one = 1 / (1.4 - 1); - - // Before the parallel_for - Kokkos::parallel_for( - "prim_to_cons_kernel", - Kokkos::MDRangePolicy< - Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, - Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), - KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const base = (nx_begin + bi * width) + nx * j + nx * ny * k; - SimdType d = pd[base]; - SimdType p = pp[base]; - SimdType ux0 = pu0[base]; - SimdType ux1 = pu1[base]; - SimdType ux2 = pu2[base]; - SimdType m0 = d * ux0; - SimdType m1 = d * ux1; - SimdType m2 = d * ux2; - SimdType e_int = p * SimdType(gamma_inv_minus_one); - SimdType e_kin = (m0 * ux0 + m1 * ux1 + m2 * ux2) * SimdType(0.5); - SimdType e_tot = e_int + e_kin; - - KE::simd_unchecked_store(d, cd + base, KE::simd_flag_default); - - KE::simd_unchecked_store(e_tot, ce + base, KE::simd_flag_default); - KE::simd_unchecked_store(m0, cm0 + base, KE::simd_flag_default); - KE::simd_unchecked_store(m1, cm1 + base, KE::simd_flag_default); - KE::simd_unchecked_store(m2, cm2 + base, KE::simd_flag_default); - }); - exec_space.fence(); -} template void prim_to_cons_vec( @@ -174,11 +110,9 @@ void prim_to_cons_vec( IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); - prim_to_cons_kernel_inline< - simd_t>(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); if (vec_end < nx) { - prim_to_cons_kernel_inline< - simd_scalar_t>(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); } } diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index c1e96c4..3bd5348 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -13,7 +13,7 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) using real_t = double; using index_t = int; - int const n = 16; + int const n = 23; Kokkos::DefaultExecutionSpace exec_space; PerfectGas eos(1.4); From cf3408b6b85cc9151643a324695ec2f6dd30e395 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 7 Apr 2026 14:15:30 +0200 Subject: [PATCH 15/56] constexp for scalar remainder, cons_to_prim implementation --- euler_operators/cons_to_prim.hpp | 76 ++++++++++++++++++++++++++++++++ euler_operators/euler_arrays.hpp | 58 ++++++++++++++++++++---- euler_operators/perfect_gas.hpp | 5 ++- euler_operators/prim_to_cons.hpp | 8 +++- test/CMakeLists.txt | 1 + test/test_cons_to_prim.cpp | 73 ++++++++++++++++++++++++++++++ test/test_prim_to_cons.cpp | 1 - 7 files changed, 209 insertions(+), 13 deletions(-) create mode 100644 test/test_cons_to_prim.cpp diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 0bf1686..8fece34 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -3,6 +3,7 @@ #include #include #include + #include "utils.hpp" template @@ -32,3 +33,78 @@ void cons_to_prim( store(prim, prim_arrays, i, j, k); }); } + +template +void cons_to_prim_kernel( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + IndexType nx_begin, + IndexType nx_end, + PerfectGas const& eos) +{ + constexpr IndexType width = SimdType::size(); + IndexType const nx_blocks = (nx_end - nx_begin) / width; + IndexType const nx = cons_arrays.d.extent(0); + IndexType const ny = cons_arrays.d.extent(1); + IndexType const nz = cons_arrays.d.extent(2); + + T* pd = prim_arrays.d.data_handle(); + T* pp = prim_arrays.p.data_handle(); + T* pu0 = prim_arrays.ux0.data_handle(); + T* pu1 = prim_arrays.ux1.data_handle(); + T* pu2 = prim_arrays.ux2.data_handle(); + + auto const prim_ptrs = EulerPrimArrays {pd, pp, pu0, pu1, pu2}; + + Kokkos::parallel_for( + "cons_to_prim_kernel", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { + IndexType const base = cons_arrays.d.mapping()(nx_begin + bi * width, j, k); + + EulerCons const cons = load(cons_arrays, base); + + SimdType e_int = internal_energy(cons); + SimdType p = eos.pressure(cons.d, e_int); + EulerPrim const prim = to_prim(cons, p); + + store(prim, prim_ptrs, base); + }); +} + + +template +void cons_to_prim_vec( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + PerfectGas const& eos) +{ + namespace KE = Kokkos::Experimental; + using simd_t = KE::simd; + using simd_scalar_t = KE::basic_simd; + + IndexType const nx = cons_arrays.d.extent(0); + IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); + + cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, IndexType(0), vec_end, eos); + + if (vec_end < nx) { + cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); + } +} diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 535c6ac..a9860d7 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -57,9 +57,9 @@ KOKKOS_FUNCTION constexpr T internal_energy(EulerCons const& cons) noexcept template -KOKKOS_FUNCTION EulerPrim to_prim(EulerCons const& cons, T const p) noexcept +KOKKOS_FORCEINLINE_FUNCTION EulerPrim to_prim(EulerCons const cons, T const p) noexcept { - T const vol_spe = 1 / cons.d; + T const vol_spe = T(1) / cons.d; return {.d = cons.d, .p = p, .ux0 = cons.mx0 * vol_spe, @@ -243,6 +243,21 @@ KOKKOS_FUNCTION void store( prim_ptrs.ux2[i] = prim.ux2; } +template +KOKKOS_FORCEINLINE_FUNCTION void store( + EulerPrim const& prim, + EulerPrimArrays const& prim_ptrs, + std::size_t const base) noexcept +{ + namespace KE = Kokkos::Experimental; + KE::simd_unchecked_store(prim.d, prim_ptrs.d + base, KE::simd_flag_default); + KE::simd_unchecked_store(prim.p, prim_ptrs.p + base, KE::simd_flag_default); + KE::simd_unchecked_store(prim.ux0, prim_ptrs.ux0 + base, KE::simd_flag_default); + KE::simd_unchecked_store(prim.ux1, prim_ptrs.ux1 + base, KE::simd_flag_default); + KE::simd_unchecked_store(prim.ux2, prim_ptrs.ux2 + base, KE::simd_flag_default); +} + + template EulerPrimArrays> create_prim_arrays_1d( Kokkos::DefaultExecutionSpace const& exec_space, @@ -310,16 +325,16 @@ EulerConsArrays data_handle( template EulerConsArrays to_mdspan( - EulerConsArrays const& prim_arrays, + EulerConsArrays const& cons_arrays, typename MdspanOut::index_type nx, typename MdspanOut::index_type ny, typename MdspanOut::index_type nz) noexcept { - return {.d = MdspanOut(prim_arrays.d.data(), nx, ny, nz), - .e = MdspanOut(prim_arrays.e.data(), nx, ny, nz), - .mx0 = MdspanOut(prim_arrays.mx0.data(), nx, ny, nz), - .mx1 = MdspanOut(prim_arrays.mx1.data(), nx, ny, nz), - .mx2 = MdspanOut(prim_arrays.mx2.data(), nx, ny, nz)}; + return {.d = MdspanOut(cons_arrays.d.data(), nx, ny, nz), + .e = MdspanOut(cons_arrays.e.data(), nx, ny, nz), + .mx0 = MdspanOut(cons_arrays.mx0.data(), nx, ny, nz), + .mx1 = MdspanOut(cons_arrays.mx1.data(), nx, ny, nz), + .mx2 = MdspanOut(cons_arrays.mx2.data(), nx, ny, nz)}; } template < @@ -356,6 +371,33 @@ KOKKOS_FUNCTION EulerCons> load( .mx1 = cons_ptrs.mx1[i], .mx2 = cons_ptrs.mx2[i]}; } +template < + class SimdType, + class ElementType, + class IndexType, + std::size_t E0, + std::size_t E1, + std::size_t E2, + class AP> +KOKKOS_FORCEINLINE_FUNCTION EulerCons load( + EulerConsArrays, + Kokkos::layout_left, // layout_left guarantees contiguous x-stride + AP>> const& cons_arrays, + IndexType const base) noexcept +{ + namespace KE = Kokkos::Experimental; + return { + .d = SimdType(cons_arrays.d.data_handle() + base, KE::simd_flag_default), + .e = SimdType(cons_arrays.e.data_handle() + base, KE::simd_flag_default), + .mx0 = SimdType(cons_arrays.mx0.data_handle() + base, KE::simd_flag_default), + .mx1 = SimdType(cons_arrays.mx1.data_handle() + base, KE::simd_flag_default), + .mx2 = SimdType(cons_arrays.mx2.data_handle() + base, KE::simd_flag_default), + }; +} + + template < class ElementType, diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index ce8a584..0ac184b 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -25,8 +25,9 @@ class PerfectGas return pressure * S(gamma_minus_one_inv); } - KOKKOS_FUNCTION T pressure(T const /*density*/, T const int_e) const noexcept + template + KOKKOS_FUNCTION S pressure(S const /*density*/, S const int_e) const noexcept { - return (m_gamma - 1) * int_e; + return S(m_gamma - 1) * int_e; } }; diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 7d5b7ff..ce4b91e 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -110,7 +110,11 @@ void prim_to_cons_vec( prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); - if (vec_end < nx) { - prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); + static constexpr bool needs_scalar_tail = (simd_t::size() > 1); + if constexpr (needs_scalar_tail) { + if (vec_end < nx) { + prim_to_cons_kernel< + simd_scalar_t>(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); + } } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 11449eb..7d1a626 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources( PRIVATE test_main.cpp test_prim_to_cons.cpp + test_cons_to_prim.cpp ) enable_testing() diff --git a/test/test_cons_to_prim.cpp b/test/test_cons_to_prim.cpp new file mode 100644 index 0000000..4ec7faa --- /dev/null +++ b/test/test_cons_to_prim.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include +#include +#include + +#include "utils.hpp" + +TEST(ConsToPrimRemainder, ScalarVsVectorized) +{ + using real_t = double; + using index_t = int; + int const n = 23; + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas eos(1.4); + + auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + // --- allocate base --- + auto prims_alloc_ref = create_prim_arrays_1d(exec_space, n * n * n); + // --- allocate vectorized --- + auto prims_alloc_vec = create_prim_arrays_1d(exec_space, n * n * n); + + auto cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + auto prim_ref = to_mdspan, + Kokkos::layout_left>>(prims_alloc_ref, n, n, n); + auto prim_vec = to_mdspan, + Kokkos::layout_left>>(prims_alloc_vec, n, n, n); + + // --- initialize with non-trivial conserved state --- + EulerCons cons {.d = 1.0, .e = 2.5, .mx0 = 0.5, .mx1 = -0.3, .mx2 = 0.1}; + init_from_state(exec_space, cons_arrays, cons); + exec_space.fence(); + + // --- run both --- + cons_to_prim(exec_space, as_const(cons_arrays), prim_ref, eos); + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_vec, eos); + exec_space.fence(); + + auto ref_h = EulerPrimArrays { + .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.d), + .p = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.p), + .ux0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux0), + .ux1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux1), + .ux2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux2)}; + auto vec_h = EulerPrimArrays { + .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.d), + .p = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.p), + .ux0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux0), + .ux1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux1), + .ux2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux2)}; + + double const tol = 1e-12; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + for (int k = 0; k < n; ++k) { + int idx = i + (n * (j + n * k)); // layout_left flattening + ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); + ASSERT_NEAR(ref_h.p(idx), vec_h.p(idx), tol); + ASSERT_NEAR(ref_h.ux0(idx), vec_h.ux0(idx), tol); + ASSERT_NEAR(ref_h.ux1(idx), vec_h.ux1(idx), tol); + ASSERT_NEAR(ref_h.ux2(idx), vec_h.ux2(idx), tol); + } + } + } +} diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp index 3bd5348..791f762 100644 --- a/test/test_prim_to_cons.cpp +++ b/test/test_prim_to_cons.cpp @@ -1,4 +1,3 @@ - #include #include From aa8f01b41cc9bdbbb97226f6cb34cfb568401a13 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 7 Apr 2026 15:58:19 +0200 Subject: [PATCH 16/56] cons_to_prim passing tests --- euler_operators/cons_to_prim.hpp | 23 ++++++++++++++--------- euler_operators/euler_arrays.hpp | 6 ++++-- euler_operators/perfect_gas.hpp | 4 ++++ test/test_main.cpp | 2 ++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 8fece34..395c96b 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -68,17 +68,19 @@ void cons_to_prim_kernel( Kokkos::MDRangePolicy< Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const base = cons_arrays.d.mapping()(nx_begin + bi * width, j, k); + IndexType const i = nx_begin + bi * width; + IndexType const base = cons_arrays.d.mapping()(i, j, k); EulerCons const cons = load(cons_arrays, base); - SimdType e_int = internal_energy(cons); - SimdType p = eos.pressure(cons.d, e_int); - EulerPrim const prim = to_prim(cons, p); - + EulerPrim prim + = to_prim(cons, eos.pressure(cons.d, internal_energy(cons))); store(prim, prim_ptrs, base); - }); + } + + ); } @@ -103,8 +105,11 @@ void cons_to_prim_vec( IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, IndexType(0), vec_end, eos); - - if (vec_end < nx) { - cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); + static constexpr bool needs_scalar_tail = (simd_t::size() > 1); + if constexpr (needs_scalar_tail) { + if (vec_end < nx) { + cons_to_prim_kernel< + simd_scalar_t>(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); + } } } diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index a9860d7..ec18ca7 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -46,16 +46,18 @@ KOKKOS_FUNCTION T kinetic_energy(EulerPrim const& prim) noexcept template KOKKOS_FUNCTION T kinetic_energy(EulerCons const& cons) noexcept { - return ((cons.mx0 * cons.mx0) + (cons.mx1 * cons.mx1) + (cons.mx2 * cons.mx2)) / cons.d / 2; + return ((cons.mx0 * cons.mx0) + (cons.mx1 * cons.mx1) + (cons.mx2 * cons.mx2)) / cons.d / T(2); } + template -KOKKOS_FUNCTION constexpr T internal_energy(EulerCons const& cons) noexcept +KOKKOS_FUNCTION T internal_energy(EulerCons const& cons) noexcept { return cons.e - kinetic_energy(cons); } + template KOKKOS_FORCEINLINE_FUNCTION EulerPrim to_prim(EulerCons const cons, T const p) noexcept { diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 0ac184b..d54616d 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -12,6 +12,10 @@ class PerfectGas public: explicit PerfectGas(T const gamma) : m_gamma(gamma), gamma_minus_one_inv(1 / (gamma - 1)) {} + T get_gamma() const noexcept + { + return m_gamma; + } KOKKOS_FUNCTION T speed_of_sound(T const density, T const pressure) const noexcept diff --git a/test/test_main.cpp b/test/test_main.cpp index 184b4d9..b8a71fe 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -11,6 +12,7 @@ int main(int argc, char** argv) int ret = -1; Kokkos::initialize(argc, argv); { + Kokkos::print_configuration(std::cout); ::testing::InitGoogleTest(&argc, argv); ret = RUN_ALL_TESTS(); } From b0022760f62143e70dec98f098ed68d5b5448210 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 7 Apr 2026 16:10:08 +0200 Subject: [PATCH 17/56] neglible difference cons_to_prim vect<->scalar --- benchmarks/benchmark_cons_to_prim.cpp | 91 ++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/benchmarks/benchmark_cons_to_prim.cpp b/benchmarks/benchmark_cons_to_prim.cpp index 1845214..67be715 100644 --- a/benchmarks/benchmark_cons_to_prim.cpp +++ b/benchmarks/benchmark_cons_to_prim.cpp @@ -4,11 +4,11 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { @@ -40,7 +40,94 @@ void ConsToPrim(benchmark::State& state) set_constant_cells_processed(state, size(prim_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); } +void ConsToPrimVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerCons const cons {.d = 1, .e = 1 / 0.4, .mx0 = 0, .mx1 = 0, .mx2 = 0}; + init_from_state(exec_space, cons_arrays, cons); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(prim_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} + +void ConsToPrimWorstRem(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerCons const cons {.d = 1, .e = 1 / 0.4, .mx0 = 0, .mx1 = 0, .mx2 = 0}; + init_from_state(exec_space, cons_arrays, cons); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(prim_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} +void ConsToPrimWorstRemVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerCons const cons {.d = 1, .e = 1 / 0.4, .mx0 = 0, .mx1 = 0, .mx2 = 0}; + init_from_state(exec_space, cons_arrays, cons); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); + exec_space.fence(); + benchmark::ClobberMemory(); + } + set_constant_cells_processed(state, size(prim_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + size_bytes(cons_arrays)); +} } // namespace -BENCHMARK(ConsToPrim)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(ConsToPrim)->DenseRange(8, 128, 8)->DenseRange(128, 320, 32); +BENCHMARK(ConsToPrimVectorized)->DenseRange(8, 128, 8)->DenseRange(128, 320, 32); +BENCHMARK(ConsToPrimWorstRem)->DenseRange(7, 128, 8)->DenseRange(127, 320, 32); +BENCHMARK(ConsToPrimWorstRemVectorized)->DenseRange(7, 128, 8)->DenseRange(127, 320, 32); From 6b77391a2dcc781d20db3a282583e1446c17b569 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 8 Apr 2026 16:02:48 +0200 Subject: [PATCH 18/56] prim_to_cons + cons_to_prim vectorized --- benchmarks/benchmark_cons_to_prim.cpp | 2 +- euler_operators/euler_arrays.hpp | 11 ++++++----- euler_operators/perfect_gas.hpp | 4 ++-- euler_operators/prim_to_cons.hpp | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/benchmarks/benchmark_cons_to_prim.cpp b/benchmarks/benchmark_cons_to_prim.cpp index 67be715..1ddc78f 100644 --- a/benchmarks/benchmark_cons_to_prim.cpp +++ b/benchmarks/benchmark_cons_to_prim.cpp @@ -89,7 +89,7 @@ void ConsToPrimWorstRem(benchmark::State& state) exec_space.fence(); for ([[maybe_unused]] auto _ : state) { - cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); + cons_to_prim(exec_space, as_const(cons_arrays), prim_arrays, eos); exec_space.fence(); benchmark::ClobberMemory(); } diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index ec18ca7..68cd7cd 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -44,14 +44,14 @@ KOKKOS_FUNCTION T kinetic_energy(EulerPrim const& prim) noexcept } template -KOKKOS_FUNCTION T kinetic_energy(EulerCons const& cons) noexcept +KOKKOS_FUNCTION constexpr T kinetic_energy(EulerCons const& cons) noexcept { - return ((cons.mx0 * cons.mx0) + (cons.mx1 * cons.mx1) + (cons.mx2 * cons.mx2)) / cons.d / T(2); + return ((cons.mx0 * cons.mx0) + (cons.mx1 * cons.mx1) + (cons.mx2 * cons.mx2)) / cons.d / 2; } template -KOKKOS_FUNCTION T internal_energy(EulerCons const& cons) noexcept +KOKKOS_FUNCTION constexpr T internal_energy(EulerCons const& cons) noexcept { return cons.e - kinetic_energy(cons); } @@ -61,7 +61,7 @@ KOKKOS_FUNCTION T internal_energy(EulerCons const& cons) noexcept template KOKKOS_FORCEINLINE_FUNCTION EulerPrim to_prim(EulerCons const cons, T const p) noexcept { - T const vol_spe = T(1) / cons.d; + T const vol_spe = 1 / cons.d; return {.d = cons.d, .p = p, .ux0 = cons.mx0 * vol_spe, @@ -76,11 +76,12 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons to_cons(EulerPrim const prim, T cons T m1 = prim.d * prim.ux1; T m2 = prim.d * prim.ux2; - T e_kin = (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2) * T(0.5); + T e_kin = (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2) * 0.5; return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; } + template struct EulerPrimArrays { diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index d54616d..14bd938 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -26,12 +26,12 @@ class PerfectGas template KOKKOS_FUNCTION S internal_energy(S const /*density*/, S const pressure) const noexcept { - return pressure * S(gamma_minus_one_inv); + return pressure * gamma_minus_one_inv; } template KOKKOS_FUNCTION S pressure(S const /*density*/, S const int_e) const noexcept { - return S(m_gamma - 1) * int_e; + return (m_gamma - 1) * int_e; } }; diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index ce4b91e..1bbaba6 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -54,7 +54,6 @@ void prim_to_cons_kernel( { constexpr IndexType width = SimdType::size(); IndexType const nx_blocks = (nx_end - nx_begin) / width; - IndexType const nx = prim_arrays.d.extent(0); IndexType const ny = prim_arrays.d.extent(1); IndexType const nz = prim_arrays.d.extent(2); From 2dfba79ef3ff875599c4c218b73503f8faea47d2 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 8 Apr 2026 16:46:18 +0200 Subject: [PATCH 19/56] first implementation time_step_vec, passing tests for skx --- benchmarks/benchmark_time_step.cpp | 26 +++++++ euler_operators/perfect_gas.hpp | 3 +- euler_operators/time_step.hpp | 107 +++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/test_time_step.cpp | 67 ++++++++++++++++++ 5 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 test/test_time_step.cpp diff --git a/benchmarks/benchmark_time_step.cpp b/benchmarks/benchmark_time_step.cpp index 609f29c..3183157 100644 --- a/benchmarks/benchmark_time_step.cpp +++ b/benchmarks/benchmark_time_step.cpp @@ -38,6 +38,32 @@ void TimeStep(benchmark::State& state) set_constant_bytes_processed(state, size_bytes(prim_arrays)); } +void TimeStepVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range()); + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + real_t dt = time_step_vec(exec_space, as_const(prim_arrays), eos, mesh); + exec_space.fence(); + benchmark::DoNotOptimize(dt); + } + + set_constant_cells_processed(state, size(prim_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays)); +} + } // namespace BENCHMARK(TimeStep)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(TimeStepVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 14bd938..d024885 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -18,7 +18,8 @@ class PerfectGas } - KOKKOS_FUNCTION T speed_of_sound(T const density, T const pressure) const noexcept + template + KOKKOS_FUNCTION S speed_of_sound(S const density, S const pressure) const noexcept { return Kokkos::sqrt(m_gamma * pressure / density); } diff --git a/euler_operators/time_step.hpp b/euler_operators/time_step.hpp index df76dbb..93b36da 100644 --- a/euler_operators/time_step.hpp +++ b/euler_operators/time_step.hpp @@ -39,3 +39,110 @@ T time_step( Kokkos::Min(dt)); return dt; } + +template +struct SimdMinReducer +{ + using reducer = SimdMinReducer; + using value_type = SimdType; + using result_view_type = Kokkos::View; + +private: + result_view_type m_value; + +public: + KOKKOS_INLINE_FUNCTION explicit SimdMinReducer(value_type& val) : m_value(&val) {} + + KOKKOS_INLINE_FUNCTION void join(value_type& dst, value_type const& src) const + { + dst = Kokkos::min(dst, src); + } + KOKKOS_INLINE_FUNCTION void init(value_type& val) const + { + val = value_type(std::numeric_limits::max()); + } + KOKKOS_INLINE_FUNCTION value_type& reference() const + { + return *m_value.data(); + } + KOKKOS_INLINE_FUNCTION result_view_type view() const + { + return m_value; + } + KOKKOS_INLINE_FUNCTION bool references_scalar() const + { + return false; + } +}; + +template +T time_step_kernel( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + PerfectGas const& eos, + UniformMesh3d const& mesh, + IndexType nx_begin, + IndexType nx_end) +{ + constexpr IndexType width = SimdType::size(); + IndexType const nx_blocks = (nx_end - nx_begin) / width; + IndexType const ny = prim_arrays.d.extent(1); + IndexType const nz = prim_arrays.d.extent(2); + + T const invdx0 = 1 / mesh.dx0(); + T const invdx1 = 1 / mesh.dx1(); + T const invdx2 = 1 / mesh.dx2(); + + SimdType dt_simd; + Kokkos::parallel_reduce( + "time_step_vec", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), + KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k, SimdType & dt_loc) { + IndexType const base = prim_arrays.d.mapping()(nx_begin + bi * width, j, k); + EulerPrim const prim = load(prim_arrays, base); + SimdType const cs = eos.speed_of_sound(prim.d, prim.p); + SimdType const cx0 = cs + Kokkos::abs(prim.ux0); + SimdType const cx1 = cs + Kokkos::abs(prim.ux1); + SimdType const cx2 = cs + Kokkos::abs(prim.ux2); + SimdType const invdt = (cx0 * invdx0) + (cx1 * invdx1) + (cx2 * invdx2); + dt_loc = Kokkos::min(dt_loc, 1 / invdt); + }, + SimdMinReducer(dt_simd)); + + return Kokkos::Experimental::reduce_min(dt_simd); +} + +template +T time_step_vec( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + PerfectGas const& eos, + UniformMesh3d const& mesh) +{ + namespace KE = Kokkos::Experimental; + using simd_t = KE::simd; + using simd_scalar_t = KE::basic_simd; + + IndexType const nx = prim_arrays.d.extent(0); + IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); + + T dt = time_step_kernel(exec_space, prim_arrays, eos, mesh, IndexType(0), vec_end); + + static constexpr bool needs_scalar_tail = (simd_t::size() > 1); + if constexpr (needs_scalar_tail) { + if (vec_end < nx) { + T const dt_tail = time_step_kernel< + simd_scalar_t>(exec_space, prim_arrays, eos, mesh, vec_end, nx); + dt = Kokkos::min(dt, dt_tail); + } + } + return dt; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7d1a626..8b04d17 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources( test_main.cpp test_prim_to_cons.cpp test_cons_to_prim.cpp + test_time_step.cpp ) enable_testing() diff --git a/test/test_time_step.cpp b/test/test_time_step.cpp new file mode 100644 index 0000000..57434ae --- /dev/null +++ b/test/test_time_step.cpp @@ -0,0 +1,67 @@ +#include + +#include +#include +#include +#include + +#include "utils.hpp" + +TEST(TimeStepRemainderWorstRem, ScalarVsVectorized) +{ + using real_t = double; + using index_t = int; + + int const n = 23; // non-multiple of SIMD width to exercise remainder path + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + + auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + + EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + real_t const dt_ref = time_step(exec_space, as_const(prim_arrays), eos, mesh); + real_t const dt_vec = time_step_vec(exec_space, as_const(prim_arrays), eos, mesh); + std::cout << "dt_ref = " << dt_ref << '\n'; + std::cout << "dt_vec = " << dt_vec << '\n'; + exec_space.fence(); + + ASSERT_NEAR(dt_ref, dt_vec, 1e-12); +} + +TEST(TimeStep, ScalarVsVectorized) +{ + using real_t = double; + using index_t = int; + + int const n = 32; + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + + auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + + EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + init_from_state(exec_space, prim_arrays, prim); + exec_space.fence(); + + real_t const dt_ref = time_step(exec_space, as_const(prim_arrays), eos, mesh); + real_t const dt_vec = time_step_vec(exec_space, as_const(prim_arrays), eos, mesh); + std::cout << "dt_ref = " << dt_ref << '\n'; + std::cout << "dt_vec = " << dt_vec << '\n'; + + exec_space.fence(); + + ASSERT_NEAR(dt_ref, dt_vec, 1e-12); +} From 29487b9833c14c29269d9cb87ab128a4e6995768 Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 10 Apr 2026 11:58:45 +0200 Subject: [PATCH 20/56] optimization: max reduction + division outside kernal loop --- euler_operators/time_step.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/euler_operators/time_step.hpp b/euler_operators/time_step.hpp index 93b36da..5ca7466 100644 --- a/euler_operators/time_step.hpp +++ b/euler_operators/time_step.hpp @@ -34,16 +34,16 @@ T time_step( T const cx1 = cs + Kokkos::abs(prim.ux1); T const cx2 = cs + Kokkos::abs(prim.ux2); T const invdt = (cx0 * invdx0) + (cx1 * invdx1) + (cx2 * invdx2); - dt_loc = Kokkos::min(dt_loc, 1 / invdt); + dt_loc = Kokkos::max(dt_loc, invdt); }, - Kokkos::Min(dt)); - return dt; + Kokkos::Max(dt)); + return 1 / dt; } template -struct SimdMinReducer +struct SimdMaxReducer { - using reducer = SimdMinReducer; + using reducer = SimdMaxReducer; using value_type = SimdType; using result_view_type = Kokkos::View; @@ -51,15 +51,15 @@ struct SimdMinReducer result_view_type m_value; public: - KOKKOS_INLINE_FUNCTION explicit SimdMinReducer(value_type& val) : m_value(&val) {} + KOKKOS_INLINE_FUNCTION explicit SimdMaxReducer(value_type& val) : m_value(&val) {} KOKKOS_INLINE_FUNCTION void join(value_type& dst, value_type const& src) const { - dst = Kokkos::min(dst, src); + dst = Kokkos::max(dst, src); } KOKKOS_INLINE_FUNCTION void init(value_type& val) const { - val = value_type(std::numeric_limits::max()); + val = value_type(std::numeric_limits::min()); } KOKKOS_INLINE_FUNCTION value_type& reference() const { @@ -110,11 +110,11 @@ T time_step_kernel( SimdType const cx1 = cs + Kokkos::abs(prim.ux1); SimdType const cx2 = cs + Kokkos::abs(prim.ux2); SimdType const invdt = (cx0 * invdx0) + (cx1 * invdx1) + (cx2 * invdx2); - dt_loc = Kokkos::min(dt_loc, 1 / invdt); + dt_loc = Kokkos::max(dt_loc, invdt); }, - SimdMinReducer(dt_simd)); + SimdMaxReducer(dt_simd)); - return Kokkos::Experimental::reduce_min(dt_simd); + return Kokkos::Experimental::reduce_max(dt_simd); } template @@ -141,8 +141,8 @@ T time_step_vec( if (vec_end < nx) { T const dt_tail = time_step_kernel< simd_scalar_t>(exec_space, prim_arrays, eos, mesh, vec_end, nx); - dt = Kokkos::min(dt, dt_tail); + dt = Kokkos::max(dt, dt_tail); } } - return dt; + return 1 / dt; } From e7c31f8800635d43ce33ade121a3dcdc21e35dc3 Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 10 Apr 2026 15:47:11 +0200 Subject: [PATCH 21/56] use reduction_idenitiy time step cuda compatibilty --- euler_operators/time_step.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/euler_operators/time_step.hpp b/euler_operators/time_step.hpp index 5ca7466..bc6a9eb 100644 --- a/euler_operators/time_step.hpp +++ b/euler_operators/time_step.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -59,7 +60,9 @@ struct SimdMaxReducer } KOKKOS_INLINE_FUNCTION void init(value_type& val) const { - val = value_type(std::numeric_limits::min()); + using scalar_t = typename SimdType::value_type; + val = value_type(Kokkos::reduction_identity::max()); + // val = SimdType(std::numeric_limits::lowest()); } KOKKOS_INLINE_FUNCTION value_type& reference() const { From 5ce1655059c763663a4b6ab3bbcab1f3c6ad40bd Mon Sep 17 00:00:00 2001 From: Timothy Date: Fri, 10 Apr 2026 17:51:45 +0200 Subject: [PATCH 22/56] vectorized godunov with benchmark + test --- benchmarks/benchmark_godunov.cpp | 33 +++++- euler_operators/euler_arrays.hpp | 14 +++ euler_operators/godunov.hpp | 157 ++++++++++++++++++++++++++++ euler_operators/hllc.hpp | 65 ++++++++++++ test/CMakeLists.txt | 1 + test/test_godunov.cpp | 170 +++++++++++++++++++++++++++++++ 6 files changed, 439 insertions(+), 1 deletion(-) create mode 100644 test/test_godunov.cpp diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index 7aa999a..e0e8f9a 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -6,11 +6,11 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { @@ -45,7 +45,38 @@ void Godunov(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); } +void GodunovVectorized(benchmark::State& state) +{ + auto const n = int_cast(state.range() + 2); + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + real_t const dt = 1E-9; + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); + exec_space.fence(); + for ([[maybe_unused]] auto _ : state) { + godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc_vec(), dt); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); +} } // namespace BENCHMARK(Godunov)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(GodunovVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 68cd7cd..489c797 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -399,6 +399,20 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons load( .mx2 = SimdType(cons_arrays.mx2.data_handle() + base, KE::simd_flag_default), }; } +template +KOKKOS_FORCEINLINE_FUNCTION EulerCons load( + EulerConsArrays const& cons_arrays, + IndexType const base) noexcept +{ + namespace KE = Kokkos::Experimental; + return { + .d = SimdType(cons_arrays.d + base, KE::simd_flag_default), + .e = SimdType(cons_arrays.e + base, KE::simd_flag_default), + .mx0 = SimdType(cons_arrays.mx0 + base, KE::simd_flag_default), + .mx1 = SimdType(cons_arrays.mx1 + base, KE::simd_flag_default), + .mx2 = SimdType(cons_arrays.mx2 + base, KE::simd_flag_default), + }; +} diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 1363fde..4ee2028 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -90,3 +90,160 @@ void godunov( store(cons, cons_arrays, i, j, k); }); } +template +void godunov_kernel( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + IndexType nx_begin, + IndexType nx_end, + PerfectGas const& eos, + UniformMesh3d const& mesh, + hllc_vec const& riemann_solver, + T const dt) +{ + constexpr IndexType width = SimdType::size(); + IndexType const nx_blocks = (nx_end - nx_begin) / width; + IndexType const ny = prim_arrays.d.extent(1); + IndexType const nz = prim_arrays.d.extent(2); + + // layout_left strides: stride in y = extent(0), stride in z = extent(0)*extent(1) + IndexType const stride_y = prim_arrays.d.extent(0); + IndexType const stride_z = prim_arrays.d.extent(0) * prim_arrays.d.extent(1); + + Kokkos::Array const ds = {mesh.ds0(), mesh.ds1(), mesh.ds2()}; + T const dtodv = dt / mesh.dv(); + + T* cd = cons_arrays.d.data_handle(); + T* ce = cons_arrays.e.data_handle(); + T* cm0 = cons_arrays.mx0.data_handle(); + T* cm1 = cons_arrays.mx1.data_handle(); + T* cm2 = cons_arrays.mx2.data_handle(); + + auto const cons_ptrs = EulerConsArrays {cd, ce, cm0, cm1, cm2}; + + Kokkos::parallel_for( + "godunov_kernel", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>( + exec_space, + {0, 1, 1}, + {nx_blocks, ny - 1, nz - 1}), // nx_begin already acouting for ghost cells + KOKKOS_LAMBDA(IndexType const bi, IndexType const j, IndexType const k) { + IndexType const base = prim_arrays.d.mapping()(nx_begin + bi * width, j, k); + + EulerPrim const prim = load(prim_arrays, base); + EulerFlux flux {}; + + { + EulerPrim const prim_L = load(prim_arrays, base - 1); + EulerPrim const prim_R = load(prim_arrays, base + 1); + EulerFlux const flux_L + = riemann_solver(dir_t<0>(), eos, prim_L, prim); + EulerFlux const flux_R + = riemann_solver(dir_t<0>(), eos, prim, prim_R); + flux.d += ds[0] * (flux_R.d - flux_L.d); + flux.e += ds[0] * (flux_R.e - flux_L.e); + flux.mx0 += ds[0] * (flux_R.mx0 - flux_L.mx0); + flux.mx1 += ds[0] * (flux_R.mx1 - flux_L.mx1); + flux.mx2 += ds[0] * (flux_R.mx2 - flux_L.mx2); + } + { + EulerPrim const prim_L = load(prim_arrays, base - stride_y); + EulerPrim const prim_R = load(prim_arrays, base + stride_y); + EulerFlux const flux_L + = riemann_solver(dir_t<1>(), eos, prim_L, prim); + EulerFlux const flux_R + = riemann_solver(dir_t<1>(), eos, prim, prim_R); + flux.d += ds[1] * (flux_R.d - flux_L.d); + flux.e += ds[1] * (flux_R.e - flux_L.e); + flux.mx0 += ds[1] * (flux_R.mx0 - flux_L.mx0); + flux.mx1 += ds[1] * (flux_R.mx1 - flux_L.mx1); + flux.mx2 += ds[1] * (flux_R.mx2 - flux_L.mx2); + } + { + EulerPrim const prim_L = load(prim_arrays, base - stride_z); + EulerPrim const prim_R = load(prim_arrays, base + stride_z); + EulerFlux const flux_L + = riemann_solver(dir_t<2>(), eos, prim_L, prim); + EulerFlux const flux_R + = riemann_solver(dir_t<2>(), eos, prim, prim_R); + flux.d += ds[2] * (flux_R.d - flux_L.d); + flux.e += ds[2] * (flux_R.e - flux_L.e); + flux.mx0 += ds[2] * (flux_R.mx0 - flux_L.mx0); + flux.mx1 += ds[2] * (flux_R.mx1 - flux_L.mx1); + flux.mx2 += ds[2] * (flux_R.mx2 - flux_L.mx2); + } + + EulerCons cons = load(cons_ptrs, base); + cons.d -= dtodv * flux.d; + cons.e -= dtodv * flux.e; + cons.mx0 -= dtodv * flux.mx0; + cons.mx1 -= dtodv * flux.mx1; + cons.mx2 -= dtodv * flux.mx2; + store(cons, cons_ptrs, base); + }); +} + + +template +void godunov_vec( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + PerfectGas const& eos, + UniformMesh3d const& mesh, + hllc_vec const& riemann_solver, + T const dt) +{ + namespace KE = Kokkos::Experimental; + using simd_t = KE::simd; + using simd_scalar_t = KE::basic_simd; + + // Interior x-range is [1, nx-1) + IndexType const nx = prim_arrays.d.extent(0); + IndexType const nx_begin = 1; + IndexType const nx_inner = nx - 2; // number of interior cells + IndexType const vec_end = nx_begin + (nx_inner / simd_t::size()) * simd_t::size(); + IndexType const nx_end = nx - 1; + + godunov_kernel( + exec_space, + prim_arrays, + cons_arrays, + nx_begin, + vec_end, + eos, + mesh, + riemann_solver, + dt); + + static constexpr bool needs_scalar_tail = (simd_t::size() > 1); + if constexpr (needs_scalar_tail) { + if (vec_end < nx_end) { + godunov_kernel( + exec_space, + prim_arrays, + cons_arrays, + vec_end, + nx_end, + eos, + mesh, + riemann_solver, + dt); + } + } +} diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index f1e9dea..7216c93 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -103,3 +103,68 @@ struct hllc return flux; } }; +struct hllc_vec +{ + template + KOKKOS_FUNCTION EulerFlux operator()( + std::integral_constant dir, + PerfectGas const& eos, + EulerPrim const& q_L, + EulerPrim const& q_R) const noexcept + { + static_assert(Dir < 3); + + T const un_L = get(dir, q_L); + T const un_R = get(dir, q_R); + + T const c_L = eos.speed_of_sound(q_L.d, q_L.p); + T const c_R = eos.speed_of_sound(q_R.d, q_R.p); + + T const S_L = Kokkos::min(un_L, un_R) - Kokkos::max(c_L, c_R); + T const S_R = Kokkos::max(un_L, un_R) + Kokkos::max(c_L, c_R); + + T const rc_L = q_L.d * (S_L - un_L); + T const rc_R = q_R.d * (S_R - un_R); + + // Compute acoustic star states + T const ustar = (q_R.p - q_L.p + rc_L * un_L - rc_R * un_R) / (rc_L - rc_R); + T const pstar = static_cast(0.5) + * (q_L.p + q_R.p + rc_L * (ustar - un_L) + rc_R * (ustar - un_R)); + + // vectorize conditionals with masks + namespace KE = Kokkos::Experimental; + auto const mask_ustar = ustar > T(0); + auto const mask_SR_pos = S_L * S_R > T(0); + + T const S = KE::condition(mask_ustar, S_L, S_R); + EulerPrim q; + q.d = KE::condition(mask_ustar, q_L.d, q_R.d); + q.p = KE::condition(mask_ustar, q_L.p, q_R.p); + q.ux0 = KE::condition(mask_ustar, q_L.ux0, q_R.ux0); + q.ux1 = KE::condition(mask_ustar, q_L.ux1, q_R.ux1); + q.ux2 = KE::condition(mask_ustar, q_L.ux2, q_R.ux2); + T const un = get(dir, q); + T const etot = eos.internal_energy(q.d, q.p) + kinetic_energy(q); + T const un_o = KE::condition(mask_SR_pos, un, ustar); + T const ptot_o = KE::condition(mask_SR_pos, q.p, pstar); + + T const d_o = (S - un) / (S - un_o) * q.d; + T const etot_o + = ((S - un) / (S - un_o) * etot) + ((ptot_o * un_o - q.p * un) / (S - ustar)); + + EulerFlux flux {}; + flux.d = d_o * un_o; + flux.e = (etot_o + ptot_o) * un_o; + flux.mx0 = d_o * un_o * q.ux0; + flux.mx1 = d_o * un_o * q.ux1; + flux.mx2 = d_o * un_o * q.ux2; + if constexpr (Dir == 0) { + flux.mx0 = (d_o * un_o * un_o) + ptot_o; + } else if constexpr (Dir == 1) { + flux.mx1 = (d_o * un_o * un_o) + ptot_o; + } else if constexpr (Dir == 2) { + flux.mx2 = (d_o * un_o * un_o) + ptot_o; + } + return flux; + } +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8b04d17..db60c4a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,7 @@ target_sources( test_prim_to_cons.cpp test_cons_to_prim.cpp test_time_step.cpp + test_godunov.cpp ) enable_testing() diff --git a/test/test_godunov.cpp b/test/test_godunov.cpp new file mode 100644 index 0000000..deb1620 --- /dev/null +++ b/test/test_godunov.cpp @@ -0,0 +1,170 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" + +namespace { + +// Runs both kernels from the same initial state and returns copies of the +// resulting cons arrays so the caller can compare them field-by-field. +struct GodunovResults +{ + std::vector d, e, mx0, mx1, mx2; +}; + +template +GodunovResults to_host(EulerConsArrays const& cons_arrays) +{ + // Each field's data_handle() points into a flat 1-D device allocation. + // We wrap it in an unmanaged View so we can use Kokkos deep_copy. + std::size_t const n = cons_arrays.d.mapping().required_span_size(); + + auto copy_field = [&](auto* ptr) { + Kokkos::View< + double*, + Kokkos::DefaultExecutionSpace, + Kokkos::MemoryTraits> + device_view(ptr, n); + auto host_mirror = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace {}, device_view); + return std::vector(host_mirror.data(), host_mirror.data() + n); + }; + + return GodunovResults { + .d = copy_field(cons_arrays.d.data_handle()), + .e = copy_field(cons_arrays.e.data_handle()), + .mx0 = copy_field(cons_arrays.mx0.data_handle()), + .mx1 = copy_field(cons_arrays.mx1.data_handle()), + .mx2 = copy_field(cons_arrays.mx2.data_handle()), + }; +} +GodunovResults run_scalar( + Kokkos::DefaultExecutionSpace& exec_space, + int n, + EulerPrim const& prim, + PerfectGas const& eos, + UniformMesh3d const& mesh, + double dt) +{ + auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + + init_from_state(exec_space, prim_arrays, prim); + init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); + exec_space.fence(); + + godunov(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); + exec_space.fence(); + + return to_host(cons_arrays); // assumed helper mirroring prim/cons to std::vector +} + +GodunovResults run_vec( + Kokkos::DefaultExecutionSpace& exec_space, + int n, + EulerPrim const& prim, + PerfectGas const& eos, + UniformMesh3d const& mesh, + double dt) +{ + auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + + init_from_state(exec_space, prim_arrays, prim); + init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); + exec_space.fence(); + + godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc_vec(), dt); + exec_space.fence(); + + return to_host(cons_arrays); +} + +void assert_cons_near(GodunovResults const& ref, GodunovResults const& vec, double tol) +{ + ASSERT_EQ(ref.d.size(), vec.d.size()); + for (std::size_t idx = 0; idx < ref.d.size(); ++idx) { + EXPECT_NEAR(ref.d[idx], vec.d[idx], tol) << "d mismatch at flat index " << idx; + EXPECT_NEAR(ref.e[idx], vec.e[idx], tol) << "e mismatch at flat index " << idx; + EXPECT_NEAR(ref.mx0[idx], vec.mx0[idx], tol) << "mx0 mismatch at flat index " << idx; + EXPECT_NEAR(ref.mx1[idx], vec.mx1[idx], tol) << "mx1 mismatch at flat index " << idx; + EXPECT_NEAR(ref.mx2[idx], vec.mx2[idx], tol) << "mx2 mismatch at flat index " << idx; + } +} + +} // namespace + +TEST(GodunovRemainderWorstRem, ScalarVsVectorized) +{ + using real_t = double; + int const n = 23; + double const dt = 1e-9; + + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + + auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); + auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); + + assert_cons_near(ref, vec, 1e-12); +} + +TEST(Godunov, ScalarVsVectorized) +{ + using real_t = double; + int const n = 32; + double const dt = 1e-9; + + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + + auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); + auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); + + assert_cons_near(ref, vec, 1e-12); +} + +// Non-trivial flow: shock-like state with large density contrast across the domain. +// Exercises the Riemann solver more aggressively than the uniform-state tests. +TEST(GodunovShockLike, ScalarVsVectorized) +{ + using real_t = double; + int const n = 33; // odd — also hits remainder + double const dt = 1e-10; // smaller dt for stability with high-pressure ratio + + Kokkos::DefaultExecutionSpace exec_space; + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + // High-pressure, high-density state; non-zero velocities in all directions + EulerPrim const prim {.d = 4.0, .p = 10.0, .ux0 = 1.5, .ux1 = -0.8, .ux2 = 0.3}; + + auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); + auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); + + assert_cons_near(ref, vec, 1e-12); +} From 344a166943021f5079adcaf9256cf3315cb2b9ce Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 13 Apr 2026 16:09:17 +0200 Subject: [PATCH 23/56] hllc select for conditionals --- benchmarks/benchmark_euler_simulation.cpp | 50 ++++- benchmarks/benchmark_godunov.cpp | 4 +- euler_operators/godunov.hpp | 9 +- euler_operators/hllc.hpp | 111 ++++------- test/test_godunov.cpp | 233 ++++++++++------------ 5 files changed, 212 insertions(+), 195 deletions(-) diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index f73832e..d7e1aa7 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -11,11 +11,11 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { @@ -66,6 +66,54 @@ void EulerSimulation(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); } +void EulerSimulationVectorized(benchmark::State& state) +{ + auto const nx = int_cast(state.range()); + real_t const cfl_factor = 0.49; + real_t const gamma = 1.4; + + real_t const dx = 1. / static_cast(nx); + PerfectGas const eos(gamma); + UniformMesh3d const mesh(dx, dx, dx); + hllc const riemann_solver; + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, (nx + 2) * (nx + 2) * (nx + 2)); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, nx + 2, nx + 2, nx + 2); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, (nx + 2) * (nx + 2) * (nx + 2)); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, nx + 2, nx + 2, nx + 2); + + init_implode(exec_space, prim_arrays, mesh); + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_arrays, eos); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + real_t const dt = time_step(exec_space, as_const(prim_arrays), eos, mesh); + + godunov_vec( + exec_space, + as_const(prim_arrays), + cons_arrays, + eos, + mesh, + riemann_solver, + cfl_factor * dt); + + boundary_conditions_periodic(exec_space, cons_arrays, 1); + + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); + exec_space.fence(); + } + set_constant_cells_processed(state, size(cons_arrays)); +} } // namespace BENCHMARK(EulerSimulation)->DenseRange(16, 320, 32); +BENCHMARK(EulerSimulationVectorized)->DenseRange(16, 320, 32); diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index e0e8f9a..cb6797c 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -45,6 +45,8 @@ void Godunov(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); } + + void GodunovVectorized(benchmark::State& state) { auto const n = int_cast(state.range() + 2); @@ -68,7 +70,7 @@ void GodunovVectorized(benchmark::State& state) exec_space.fence(); for ([[maybe_unused]] auto _ : state) { - godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc_vec(), dt); + godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); exec_space.fence(); benchmark::ClobberMemory(); } diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 4ee2028..1e27659 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -28,9 +29,13 @@ void godunov( hllc const& riemann_solver, T const dt) { + namespace KE = Kokkos::Experimental; + using simd_t = KE::simd; + Kokkos::Array const ds = {mesh.ds0(), mesh.ds1(), mesh.ds2()}; T const dtodv = dt / mesh.dv(); + Kokkos::parallel_for( "godunov", Kokkos::MDRangePolicy< @@ -105,7 +110,7 @@ void godunov_kernel( IndexType nx_end, PerfectGas const& eos, UniformMesh3d const& mesh, - hllc_vec const& riemann_solver, + hllc const& riemann_solver, T const dt) { constexpr IndexType width = SimdType::size(); @@ -206,7 +211,7 @@ void godunov_vec( Kokkos::layout_left>> const& cons_arrays, PerfectGas const& eos, UniformMesh3d const& mesh, - hllc_vec const& riemann_solver, + hllc const& riemann_solver, T const dt) { namespace KE = Kokkos::Experimental; diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 7216c93..216203b 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -5,8 +5,9 @@ #include #include -#include -#include + +#include "euler_arrays.hpp" +#include "perfect_gas.hpp" template KOKKOS_FUNCTION T get(std::integral_constant /*unused*/, EulerPrim const& prim) @@ -47,63 +48,26 @@ KOKKOS_FUNCTION T get(std::integral_constant /*unused*/, Euler } } -struct hllc -{ - template - KOKKOS_FUNCTION EulerFlux operator()( - std::integral_constant dir, - PerfectGas const& eos, - EulerPrim const& q_L, - EulerPrim const& q_R) const noexcept - { - static_assert(Dir < 3); - - T const un_L = get(dir, q_L); - T const un_R = get(dir, q_R); - - T const c_L = eos.speed_of_sound(q_L.d, q_L.p); - T const c_R = eos.speed_of_sound(q_R.d, q_R.p); - - T const S_L = Kokkos::min(un_L, un_R) - Kokkos::max(c_L, c_R); - T const S_R = Kokkos::max(un_L, un_R) + Kokkos::max(c_L, c_R); - - T const rc_L = q_L.d * (S_L - un_L); - T const rc_R = q_R.d * (S_R - un_R); +namespace detail { - // Compute acoustic star states - T const ustar = (q_R.p - q_L.p + rc_L * un_L - rc_R * un_R) / (rc_L - rc_R); - T const pstar = static_cast(0.5) - * (q_L.p + q_R.p + rc_L * (ustar - un_L) + rc_R * (ustar - un_R)); +// scalar +template +KOKKOS_FUNCTION T select(bool cond, T const& a, T const& b) +{ + return cond ? a : b; +} - T const S = ustar > 0 ? S_L : S_R; - EulerPrim const q = ustar > 0 ? q_L : q_R; +// simd +template +KOKKOS_FUNCTION T select(Mask const& mask, T const& a, T const& b) +{ + return Kokkos::Experimental::condition(mask, a, b); +} - T const un = get(dir, q); - T const etot = eos.internal_energy(q.d, q.p) + kinetic_energy(q); +} // namespace detail - T const un_o = S_L * S_R > 0 ? un : ustar; - T const ptot_o = S_L * S_R > 0 ? q.p : pstar; - T const d_o = (S - un) / (S - un_o) * q.d; - T const etot_o - = ((S - un) / (S - un_o) * etot) + ((ptot_o * un_o - q.p * un) / (S - ustar)); - EulerFlux flux {}; - flux.d = d_o * un_o; - flux.e = (etot_o + ptot_o) * un_o; - flux.mx0 = d_o * un_o * q.ux0; - flux.mx1 = d_o * un_o * q.ux1; - flux.mx2 = d_o * un_o * q.ux2; - if constexpr (Dir == 0) { - flux.mx0 = (d_o * un_o * un_o) + ptot_o; - } else if constexpr (Dir == 1) { - flux.mx1 = (d_o * un_o * un_o) + ptot_o; - } else if constexpr (Dir == 2) { - flux.mx2 = (d_o * un_o * un_o) + ptot_o; - } - return flux; - } -}; -struct hllc_vec +struct hllc { template KOKKOS_FUNCTION EulerFlux operator()( @@ -114,6 +78,8 @@ struct hllc_vec { static_assert(Dir < 3); + using detail::select; + T const un_L = get(dir, q_L); T const un_R = get(dir, q_R); @@ -126,45 +92,54 @@ struct hllc_vec T const rc_L = q_L.d * (S_L - un_L); T const rc_R = q_R.d * (S_R - un_R); - // Compute acoustic star states + // Star region T const ustar = (q_R.p - q_L.p + rc_L * un_L - rc_R * un_R) / (rc_L - rc_R); T const pstar = static_cast(0.5) * (q_L.p + q_R.p + rc_L * (ustar - un_L) + rc_R * (ustar - un_R)); - // vectorize conditionals with masks - namespace KE = Kokkos::Experimental; - auto const mask_ustar = ustar > T(0); - auto const mask_SR_pos = S_L * S_R > T(0); + // Conditions (scalar -> bool, SIMD -> mask) + auto const cond_ustar = ustar > T(0); + auto const cond_SR = S_L * S_R > T(0); + + // Select wave speed and state + T const S = select(cond_ustar, S_L, S_R); - T const S = KE::condition(mask_ustar, S_L, S_R); EulerPrim q; - q.d = KE::condition(mask_ustar, q_L.d, q_R.d); - q.p = KE::condition(mask_ustar, q_L.p, q_R.p); - q.ux0 = KE::condition(mask_ustar, q_L.ux0, q_R.ux0); - q.ux1 = KE::condition(mask_ustar, q_L.ux1, q_R.ux1); - q.ux2 = KE::condition(mask_ustar, q_L.ux2, q_R.ux2); + q.d = select(cond_ustar, q_L.d, q_R.d); + q.p = select(cond_ustar, q_L.p, q_R.p); + q.ux0 = select(cond_ustar, q_L.ux0, q_R.ux0); + q.ux1 = select(cond_ustar, q_L.ux1, q_R.ux1); + q.ux2 = select(cond_ustar, q_L.ux2, q_R.ux2); + T const un = get(dir, q); T const etot = eos.internal_energy(q.d, q.p) + kinetic_energy(q); - T const un_o = KE::condition(mask_SR_pos, un, ustar); - T const ptot_o = KE::condition(mask_SR_pos, q.p, pstar); + + // Output states + T const un_o = select(cond_SR, un, ustar); + T const ptot_o = select(cond_SR, q.p, pstar); T const d_o = (S - un) / (S - un_o) * q.d; + T const etot_o = ((S - un) / (S - un_o) * etot) + ((ptot_o * un_o - q.p * un) / (S - ustar)); EulerFlux flux {}; + flux.d = d_o * un_o; flux.e = (etot_o + ptot_o) * un_o; + flux.mx0 = d_o * un_o * q.ux0; flux.mx1 = d_o * un_o * q.ux1; flux.mx2 = d_o * un_o * q.ux2; + if constexpr (Dir == 0) { flux.mx0 = (d_o * un_o * un_o) + ptot_o; } else if constexpr (Dir == 1) { flux.mx1 = (d_o * un_o * un_o) + ptot_o; - } else if constexpr (Dir == 2) { + } else { flux.mx2 = (d_o * un_o * un_o) + ptot_o; } + return flux; } }; diff --git a/test/test_godunov.cpp b/test/test_godunov.cpp index deb1620..1455e5a 100644 --- a/test/test_godunov.cpp +++ b/test/test_godunov.cpp @@ -11,160 +11,147 @@ namespace { -// Runs both kernels from the same initial state and returns copies of the -// resulting cons arrays so the caller can compare them field-by-field. -struct GodunovResults +struct Results { std::vector d, e, mx0, mx1, mx2; }; -template -GodunovResults to_host(EulerConsArrays const& cons_arrays) +template +Results to_host(EulerConsArrays const& a) { - // Each field's data_handle() points into a flat 1-D device allocation. - // We wrap it in an unmanaged View so we can use Kokkos deep_copy. - std::size_t const n = cons_arrays.d.mapping().required_span_size(); + std::size_t const n = a.d.mapping().required_span_size(); - auto copy_field = [&](auto* ptr) { + auto copy = [&](auto* ptr) { Kokkos::View< double*, Kokkos::DefaultExecutionSpace, Kokkos::MemoryTraits> - device_view(ptr, n); - auto host_mirror = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace {}, device_view); - return std::vector(host_mirror.data(), host_mirror.data() + n); + v(ptr, n); + auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace {}, v); + return std::vector(h.data(), h.data() + n); }; - return GodunovResults { - .d = copy_field(cons_arrays.d.data_handle()), - .e = copy_field(cons_arrays.e.data_handle()), - .mx0 = copy_field(cons_arrays.mx0.data_handle()), - .mx1 = copy_field(cons_arrays.mx1.data_handle()), - .mx2 = copy_field(cons_arrays.mx2.data_handle()), - }; -} -GodunovResults run_scalar( - Kokkos::DefaultExecutionSpace& exec_space, - int n, - EulerPrim const& prim, - PerfectGas const& eos, - UniformMesh3d const& mesh, - double dt) -{ - auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); - auto prim_arrays = to_mdspan, - Kokkos::layout_left>>(prims_alloc, n, n, n); - auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); - auto cons_arrays = to_mdspan, - Kokkos::layout_left>>(cons_alloc, n, n, n); - - init_from_state(exec_space, prim_arrays, prim); - init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); - exec_space.fence(); - - godunov(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); - exec_space.fence(); - - return to_host(cons_arrays); // assumed helper mirroring prim/cons to std::vector + return {copy(a.d.data_handle()), + copy(a.e.data_handle()), + copy(a.mx0.data_handle()), + copy(a.mx1.data_handle()), + copy(a.mx2.data_handle())}; } -GodunovResults run_vec( - Kokkos::DefaultExecutionSpace& exec_space, +template +Results run( + Kokkos::DefaultExecutionSpace& exec, int n, EulerPrim const& prim, PerfectGas const& eos, UniformMesh3d const& mesh, - double dt) + double dt, + Kernel kernel) { - auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); - auto prim_arrays = to_mdspan, - Kokkos::layout_left>>(prims_alloc, n, n, n); - auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); - auto cons_arrays = to_mdspan, - Kokkos::layout_left>>(cons_alloc, n, n, n); - - init_from_state(exec_space, prim_arrays, prim); - init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); - exec_space.fence(); - - godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc_vec(), dt); - exec_space.fence(); - - return to_host(cons_arrays); -} + auto prims = create_prim_arrays_1d(exec, n * n * n); + auto cons = create_cons_arrays_1d(exec, n * n * n); -void assert_cons_near(GodunovResults const& ref, GodunovResults const& vec, double tol) -{ - ASSERT_EQ(ref.d.size(), vec.d.size()); - for (std::size_t idx = 0; idx < ref.d.size(); ++idx) { - EXPECT_NEAR(ref.d[idx], vec.d[idx], tol) << "d mismatch at flat index " << idx; - EXPECT_NEAR(ref.e[idx], vec.e[idx], tol) << "e mismatch at flat index " << idx; - EXPECT_NEAR(ref.mx0[idx], vec.mx0[idx], tol) << "mx0 mismatch at flat index " << idx; - EXPECT_NEAR(ref.mx1[idx], vec.mx1[idx], tol) << "mx1 mismatch at flat index " << idx; - EXPECT_NEAR(ref.mx2[idx], vec.mx2[idx], tol) << "mx2 mismatch at flat index " << idx; - } -} + auto P = to_mdspan< + Kokkos::mdspan, Kokkos::layout_left>>(prims, n, n, n); + auto U = to_mdspan< + Kokkos::mdspan, Kokkos::layout_left>>(cons, n, n, n); -} // namespace - -TEST(GodunovRemainderWorstRem, ScalarVsVectorized) -{ - using real_t = double; - int const n = 23; - double const dt = 1e-9; + init_from_state(exec, P, prim); + init_from_state(exec, U, to_cons(prim, eos.internal_energy(prim.d, prim.p))); - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas const eos(1.4); - UniformMesh3d const mesh(1., 1., 1.); - EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + exec.fence(); + kernel(exec, as_const(P), U, eos, mesh, hllc {}, dt); + exec.fence(); - auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); - auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); - - assert_cons_near(ref, vec, 1e-12); + return to_host(U); } -TEST(Godunov, ScalarVsVectorized) +void assert_near(Results const& a, Results const& b, double tol) { - using real_t = double; - int const n = 32; - double const dt = 1e-9; + ASSERT_EQ(a.d.size(), b.d.size()); + for (std::size_t i = 0; i < a.d.size(); ++i) { + EXPECT_NEAR(a.d[i], b.d[i], tol); + EXPECT_NEAR(a.e[i], b.e[i], tol); + EXPECT_NEAR(a.mx0[i], b.mx0[i], tol); + EXPECT_NEAR(a.mx1[i], b.mx1[i], tol); + EXPECT_NEAR(a.mx2[i], b.mx2[i], tol); + } +} - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas const eos(1.4); - UniformMesh3d const mesh(1., 1., 1.); - EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; +void run_case(int n, double dt, EulerPrim const& prim) +{ + Kokkos::DefaultExecutionSpace exec; + PerfectGas eos(1.4); + UniformMesh3d mesh(1., 1., 1.); + + // auto ref = run(exec, n, prim, eos, mesh, dt, godunov); + Results ref + = run(exec, + n, + prim, + eos, + mesh, + dt, + [](auto& exec, + auto const& P, + auto& U, + auto const& eos, + auto const& mesh, + auto solver, + double dt) { godunov(exec, P, U, eos, mesh, solver, dt); }); + + Results vec + = run(exec, + n, + prim, + eos, + mesh, + dt, + [](auto& exec, + auto const& P, + auto& U, + auto const& eos, + auto const& mesh, + auto solver, + double dt) { godunov_vec(exec, P, U, eos, mesh, solver, dt); }); + + assert_near(ref, vec, 1e-12); +} - auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); - auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); +} // namespace - assert_cons_near(ref, vec, 1e-12); -} -// Non-trivial flow: shock-like state with large density contrast across the domain. -// Exercises the Riemann solver more aggressively than the uniform-state tests. -TEST(GodunovShockLike, ScalarVsVectorized) +struct Case { - using real_t = double; - int const n = 33; // odd — also hits remainder - double const dt = 1e-10; // smaller dt for stability with high-pressure ratio - - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas const eos(1.4); - UniformMesh3d const mesh(1., 1., 1.); - // High-pressure, high-density state; non-zero velocities in all directions - EulerPrim const prim {.d = 4.0, .p = 10.0, .ux0 = 1.5, .ux1 = -0.8, .ux2 = 0.3}; + int n; + double dt; + EulerPrim prim; +}; - auto const ref = run_scalar(exec_space, n, prim, eos, mesh, dt); - auto const vec = run_vec(exec_space, n, prim, eos, mesh, dt); +class GodunovTest : public ::testing::TestWithParam +{ +}; - assert_cons_near(ref, vec, 1e-12); +TEST_P(GodunovTest, ScalarVsVectorized) +{ + auto const& c = GetParam(); + run_case(c.n, c.dt, c.prim); } + +INSTANTIATE_TEST_SUITE_P( + All, + GodunovTest, + ::testing::Values( + Case {23, + 1e-9, + {.d = 1.0, + .p = 1.0, + .ux0 = 0.5, + .ux1 = -0.3, + .ux2 = 0.1}}, // remainder stress + Case {32, + 1e-9, + {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}}, // clean SIMD + Case {33, 1e-10, {.d = 4.0, .p = 10.0, .ux0 = 1.5, .ux1 = -0.8, .ux2 = 0.3}} + // shock-like + )); From 0a41e435336dbfe8a7ae4edda27fa356c34bcbed Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 15 Apr 2026 10:49:29 +0200 Subject: [PATCH 24/56] remove constexpr for remainders, add godunov remainder test --- benchmarks/benchmark_godunov.cpp | 64 ++++++++++++++++++++++++++++++++ euler_operators/cons_to_prim.hpp | 8 +--- euler_operators/godunov.hpp | 27 ++++++-------- euler_operators/prim_to_cons.hpp | 8 +--- euler_operators/time_step.hpp | 11 ++---- 5 files changed, 84 insertions(+), 34 deletions(-) diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index cb6797c..b503cdd 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -45,6 +45,37 @@ void Godunov(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); } +void GodunovWorstRem(benchmark::State& state) +{ + auto const n = int_cast(state.range() + 2); + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + real_t const dt = 1E-9; + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + godunov(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); +} void GodunovVectorized(benchmark::State& state) @@ -78,7 +109,40 @@ void GodunovVectorized(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); } +void GodunovVectorizedWorstRem(benchmark::State& state) +{ + auto const n = int_cast(state.range() + 2); + PerfectGas const eos(1.4); + UniformMesh3d const mesh(1., 1., 1.); + real_t const dt = 1E-9; + Kokkos::DefaultExecutionSpace const exec_space; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prim_arrays = to_mdspan, + Kokkos::layout_left>>(prims_alloc, n, n, n); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_arrays = to_mdspan, + Kokkos::layout_left>>(cons_alloc, n, n, n); + EulerPrim const prim {.d = 1, .p = 1, .ux0 = 0, .ux1 = 0, .ux2 = 0}; + init_from_state(exec_space, prim_arrays, prim); + init_from_state(exec_space, cons_arrays, to_cons(prim, eos.internal_energy(prim.d, prim.p))); + exec_space.fence(); + + for ([[maybe_unused]] auto _ : state) { + godunov_vec(exec_space, as_const(prim_arrays), cons_arrays, eos, mesh, hllc(), dt); + exec_space.fence(); + benchmark::ClobberMemory(); + } + + set_constant_cells_processed(state, size(cons_arrays)); + set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); +} } // namespace BENCHMARK(Godunov)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); BENCHMARK(GodunovVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(GodunovWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); +BENCHMARK(GodunovVectorizedWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 395c96b..2071ba1 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -105,11 +105,7 @@ void cons_to_prim_vec( IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, IndexType(0), vec_end, eos); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx) { - cons_to_prim_kernel< - simd_scalar_t>(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); - } + if (vec_end < nx) { + cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); } } diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 1e27659..e6923a9 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -218,7 +218,7 @@ void godunov_vec( using simd_t = KE::simd; using simd_scalar_t = KE::basic_simd; - // Interior x-range is [1, nx-1) + // interior x-range is [1, nx-1) IndexType const nx = prim_arrays.d.extent(0); IndexType const nx_begin = 1; IndexType const nx_inner = nx - 2; // number of interior cells @@ -236,19 +236,16 @@ void godunov_vec( riemann_solver, dt); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx_end) { - godunov_kernel( - exec_space, - prim_arrays, - cons_arrays, - vec_end, - nx_end, - eos, - mesh, - riemann_solver, - dt); - } + if (vec_end < nx_end) { + godunov_kernel( + exec_space, + prim_arrays, + cons_arrays, + vec_end, + nx_end, + eos, + mesh, + riemann_solver, + dt); } } diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 1bbaba6..d7162d0 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -109,11 +109,7 @@ void prim_to_cons_vec( prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx) { - prim_to_cons_kernel< - simd_scalar_t>(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); - } + if (vec_end < nx) { + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); } } diff --git a/euler_operators/time_step.hpp b/euler_operators/time_step.hpp index bc6a9eb..5a64072 100644 --- a/euler_operators/time_step.hpp +++ b/euler_operators/time_step.hpp @@ -139,13 +139,10 @@ T time_step_vec( T dt = time_step_kernel(exec_space, prim_arrays, eos, mesh, IndexType(0), vec_end); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx) { - T const dt_tail = time_step_kernel< - simd_scalar_t>(exec_space, prim_arrays, eos, mesh, vec_end, nx); - dt = Kokkos::max(dt, dt_tail); - } + if (vec_end < nx) { + T const dt_tail + = time_step_kernel(exec_space, prim_arrays, eos, mesh, vec_end, nx); + dt = Kokkos::max(dt, dt_tail); } return 1 / dt; } From 5c18d59b61d8ddb25bb457df36ae1ef2d52e930a Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 15 Apr 2026 10:54:44 +0200 Subject: [PATCH 25/56] update setup scripts a100 skx, vectorized simulation, global utils --- setups/prepare-laptop.sh | 22 ++++-- setups/ruche/a100/prepare.sh | 67 +++++++++++-------- setups/ruche/a100/run_bench.sh | 9 ++- setups/ruche/a100/run_test.sh | 2 +- setups/ruche/skx/prepare.sh | 12 ++-- setups/ruche/skx/run_bench.sh | 12 ++-- simulations/display_results.py | 2 +- simulations/euler_simulation.cpp | 9 +-- simulations/plot.py | 111 +++++++++++++++++++++++++------ utils/utils.hpp | 3 +- 10 files changed, 179 insertions(+), 70 deletions(-) mode change 100644 => 100755 setups/prepare-laptop.sh diff --git a/setups/prepare-laptop.sh b/setups/prepare-laptop.sh old mode 100644 new mode 100755 index 1b6d311..e8e5eee --- a/setups/prepare-laptop.sh +++ b/setups/prepare-laptop.sh @@ -1,11 +1,12 @@ #!/bin/bash -export CC=gcc-10 -export CXX=g++-10 +export CC=gcc-13 +export CXX=g++-13 -export install_dir=$PWD/opt +export install_dir=$PWD/opt/local export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git cmake \ @@ -18,7 +19,7 @@ cmake --build build-benchmark --parallel 4 cmake --install build-benchmark --prefix $benchmark_ROOT rm -rf build-benchmark benchmark -git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git +git clone --branch 5.0.0 --depth 1 https://github.com/kokkos/kokkos.git cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ @@ -31,3 +32,16 @@ cmake \ cmake --build build-kokkos --parallel 4 cmake --install build-kokkos --prefix $Kokkos_ROOT rm -rf build-kokkos kokkos + +git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest +cmake --install build-gtest --prefix $gtest_ROOT +rm -rf build-gtest googletest + +cmake -D CMAKE_BUILD_TYPE=Release -B build-skx +cmake --build build-local diff --git a/setups/ruche/a100/prepare.sh b/setups/ruche/a100/prepare.sh index 128361c..d10733b 100755 --- a/setups/ruche/a100/prepare.sh +++ b/setups/ruche/a100/prepare.sh @@ -10,33 +10,48 @@ module load \ export install_dir=$PWD/opt/a100 export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest -rm -rf build-benchmark benchmark build-kokkos kokkos build-a100 - -git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git -cmake \ - -D BENCHMARK_ENABLE_TESTING=OFF \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-benchmark \ - -S benchmark -cmake --build build-benchmark -cmake --install build-benchmark --prefix $benchmark_ROOT -rm -rf build-benchmark benchmark - -git clone --branch 5.0.0 --depth 1 https://github.com/kokkos/kokkos.git -cmake \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -D Kokkos_ARCH_AMPERE80=ON \ - -D Kokkos_ENABLE_CUDA=ON \ - -D Kokkos_ENABLE_DEPRECATED_CODE_4=OFF \ - -D Kokkos_ENABLE_DEPRECATION_WARNINGS=OFF \ - -B build-kokkos \ - -S kokkos -cmake --build build-kokkos -cmake --install build-kokkos --prefix $Kokkos_ROOT -rm -rf build-kokkos kokkos +# rm -rf build-benchmark benchmark build-kokkos kokkos build-a100 + +# git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git +# cmake \ +# -D BENCHMARK_ENABLE_TESTING=OFF \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-benchmark \ +# -S benchmark +# cmake --build build-benchmark +# cmake --install build-benchmark --prefix $benchmark_ROOT +# rm -rf build-benchmark benchmark + +# git clone https://github.com/kokkos/kokkos.git +# cd kokkos +# git checkout 7f8988b4d +# cd .. + +# cmake \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -D Kokkos_ARCH_AMPERE80=ON \ +# -D Kokkos_ENABLE_CUDA=ON \ +# -D Kokkos_ENABLE_DEPRECATED_CODE_4=OFF \ +# -D Kokkos_ENABLE_DEPRECATION_WARNINGS=OFF \ +# -B build-kokkos \ +# -S kokkos +# cmake --build build-kokkos +# cmake --install build-kokkos --prefix $Kokkos_ROOT +# rm -rf build-kokkos kokkos + +# git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +# cmake \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-gtest \ +# -S googletest +# cmake --build build-gtest +cmake --install build-gtest --prefix $gtest_ROOT +rm -rf build-gtest googletest cmake -D CMAKE_BUILD_TYPE=Release -B build-a100 cmake --build build-a100 diff --git a/setups/ruche/a100/run_bench.sh b/setups/ruche/a100/run_bench.sh index f5c3bd1..ddb3933 100644 --- a/setups/ruche/a100/run_bench.sh +++ b/setups/ruche/a100/run_bench.sh @@ -1,9 +1,9 @@ #!/bin/bash -#SBATCH --job-name=bench_skx +#SBATCH --job-name=bench_a100 #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=1 -#SBATCH --time=00:04:00 +#SBATCH --time=00:10:00 #SBATCH --partition=gpua100 #SBATCH--gres=gpu:1 @@ -27,7 +27,6 @@ BENCHMARK_FILTER=${1:-""} ./build-a100/benchmarks/euler_benchmarks \ \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/a100/"[${SLURM_JOB_ID}]_a100-${BENCHMARK_FILTER}.json" # --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out=./results/ruche/a100/"[${SLURM_JOB_ID}]_a100-${BENCHMARK_FILTER}.json" -##SBATCH --exclusive -##SBATCH --hint=nomultithread +# --benchmark_filter="${BENCHMARK_FILTER}" \ diff --git a/setups/ruche/a100/run_test.sh b/setups/ruche/a100/run_test.sh index f4a1a11..106399d 100644 --- a/setups/ruche/a100/run_test.sh +++ b/setups/ruche/a100/run_test.sh @@ -3,7 +3,7 @@ #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 #SBATCH --cpus-per-task=1 -#SBATCH --time=00:01:00 +#SBATCH --time=00:04:00 #SBATCH --partition=gpua100 #SBATCH --gres=gpu:1 diff --git a/setups/ruche/skx/prepare.sh b/setups/ruche/skx/prepare.sh index 7d129a5..04b0351 100755 --- a/setups/ruche/skx/prepare.sh +++ b/setups/ruche/skx/prepare.sh @@ -22,8 +22,12 @@ cmake --build build-benchmark cmake --install build-benchmark --prefix $benchmark_ROOT rm -rf build-benchmark benchmark -# git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git -git clone --branch 5.0.0 --depth 1 https://github.com/kokkos/kokkos.git +rm -rf build-kokkos kokkos + +git clone https://github.com/kokkos/kokkos.git +cd kokkos +git checkout 7f8988b4d +cd .. cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ @@ -33,7 +37,7 @@ cmake \ -D Kokkos_ENABLE_OPENMP=ON \ -B build-kokkos \ -S kokkos -cmake --build build-kokkos +cmake --build build-kokkos --parallel cmake --install build-kokkos --prefix $Kokkos_ROOT rm -rf build-kokkos kokkos @@ -48,4 +52,4 @@ cmake --install build-gtest --prefix $gtest_ROOT rm -rf build-gtest googletest cmake -D CMAKE_BUILD_TYPE=Release -B build-skx -cmake --build build-skx +cmake --build build-skx --parallel diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index ef33858..509b66b 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -2,12 +2,14 @@ #SBATCH --job-name=bench_skx #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 -#SBATCH --cpus-per-task=1 -#SBATCH --time=00:10:00 +#SBATCH --cpus-per-task=20 +#SBATCH --time=00:15:00 #SBATCH --partition=cpu_short #SBATCH --exclusive #SBATCH --hint=nomultithread +#SBATCH --nodes=1 + module purge module load \ gcc/13.4.0/gcc-15.1.0 \ @@ -19,7 +21,9 @@ cd ${SLURM_SUBMIT_DIR} mkdir -p slurm_out results/ruche/skx export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK} -export OMP_PROC_BIND=true +# export OMP_PROC_BIND=true +export OMP_PROC_BIND=close +export OMP_PLACES=cores BENCHMARK_FILTER=${1:-""} @@ -27,4 +31,4 @@ BENCHMARK_FILTER=${1:-""} ./build-skx/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/skx/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}.json" + --benchmark_out=./results/ruche/skx/all/"[${SLURM_JOB_ID}]_m20-${BENCHMARK_FILTER}.json" diff --git a/simulations/display_results.py b/simulations/display_results.py index f88fe37..53da592 100644 --- a/simulations/display_results.py +++ b/simulations/display_results.py @@ -28,7 +28,7 @@ def main(): def main_loop(): parser = argparse.ArgumentParser(description="Display a sequence of .npy files.") parser.add_argument("path", type=str, help="Directory or glob pattern (e.g. './*.npy')") - parser.add_argument("--delay", type=float, default=0.5, help="Delay between frames (seconds)") + parser.add_argument("--delay", type=float, default=0.1, help="Delay between frames (seconds)") args = parser.parse_args() # Resolve files diff --git a/simulations/euler_simulation.cpp b/simulations/euler_simulation.cpp index 6388487..ae31629 100644 --- a/simulations/euler_simulation.cpp +++ b/simulations/euler_simulation.cpp @@ -49,15 +49,16 @@ int main(int argc, char** argv) Kokkos::layout_left>>(cons_alloc, nx + 2, nx + 2, nx + 2); init_implode(exec_space, prim_arrays, mesh); - prim_to_cons(exec_space, as_const(prim_arrays), cons_arrays, eos); + prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_arrays, eos); exec_space.fence(); auto const start = std::chrono::steady_clock::now(); int it = 0; while (it < nt) { - real_t const dt = time_step(exec_space, as_const(prim_arrays), eos, mesh); + real_t const dt = time_step_vec(exec_space, as_const(prim_arrays), eos, mesh); - godunov(exec_space, + godunov_vec( + exec_space, as_const(prim_arrays), cons_arrays, eos, @@ -67,7 +68,7 @@ int main(int argc, char** argv) boundary_conditions_periodic(exec_space, cons_arrays, 1); - cons_to_prim(exec_space, as_const(cons_arrays), prim_arrays, eos); + cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_arrays, eos); ++it; diff --git a/simulations/plot.py b/simulations/plot.py index 94e59bc..02265c3 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -15,12 +15,12 @@ # --------------------------------------------------------- -OUT_DIR = "results/plots" +OUT_DIR = "results/plots/" import os import glob -RES_DIR = "results/ruche/skx/" +RES_DIR = "results/ruche/" def latest_result(res_dir=RES_DIR, pattern="*.json"): files = glob.glob(os.path.join(res_dir, pattern)) @@ -113,6 +113,8 @@ def plot_scalar_vs_vector(files, out_dir): base_names = [b for b in all_names if b + "Vectorized" in all_names] for base_name in base_names: + if "Godunov" not in base_name: + continue vec_name = base_name + "Vectorized" for environment, path in files.items(): @@ -199,10 +201,51 @@ def plot_scalar_vs_vector(files, out_dir): plt.savefig(save_name, dpi=200) plt.close() +# def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): +# df_a, _ = load_one(path_a) +# df_b, _ = load_one(path_b) + +# merged = pd.merge( +# df_a, +# df_b, +# on=["benchmark", "size"], +# suffixes=(f"_{label_a}", f"_{label_b}"), +# how="inner", +# ) + + +# merged = merged[ +# merged.benchmark.isin(["GodunovVectorized", "Godunov"]) +# ] + +# col ="real_time_ns" +# a_col = f"{col}_{label_a}" +# b_col = f"{col}_{label_b}" +# if a_col in merged and b_col in merged: +# merged[f"{col}_speedup"] = merged[a_col] / merged[b_col] + +# if cols: +# merged = merged[cols] + +# mean_row = merged.mean(numeric_only=True).to_frame().T +# mean_row["benchmark"] = "MEAN" +# mean_row["size"] = pd.NA +# merged = pd.concat([merged, mean_row], ignore_index=True) + +# rounding = {c: 5 for c in merged.columns if "speedup" in c} +# rounding |= {c: 5 for c in merged.columns if "time" in c} +# rounding |= {c: 5 for c in merged.columns if "cells_per_second" in c or "bytes_per_second" in c} +# merged = merged.round(rounding) + + + +# out_csv = Path(out_csv) +# out_csv.parent.mkdir(parents=True, exist_ok=True) +# merged.to_csv(out_csv, index=False) +# return merged def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): df_a, _ = load_one(path_a) df_b, _ = load_one(path_b) - merged = pd.merge( df_a, df_b, @@ -210,18 +253,22 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N suffixes=(f"_{label_a}", f"_{label_b}"), how="inner", ) + merged = merged[ + merged.benchmark.isin(["GodunovVectorized", "Godunov"]) + ] + + # time speedup: lower is better, so a/b (b is faster if > 1) + time_col = "real_time_ns" + a_t, b_t = f"{time_col}_{label_a}", f"{time_col}_{label_b}" + if a_t in merged and b_t in merged: + merged[f"{time_col}_speedup"] = merged[a_t] / merged[b_t] + + # bytes/s speedup: higher is better, so b/a (b is faster if > 1) + bw_col = "bytes_per_second" + a_bw, b_bw = f"{bw_col}_{label_a}", f"{bw_col}_{label_b}" + if a_bw in merged and b_bw in merged: + merged[f"{bw_col}_speedup"] = merged[b_bw] / merged[a_bw] - merged["real_time_speedup"] = ( - merged[f"real_time_ns_{label_a}"] / merged[f"real_time_ns_{label_b}"] - ) - - for col in ("cells_per_second", "bytes_per_second"): - a_col = f"{col}_{label_a}" - b_col = f"{col}_{label_b}" - if a_col in merged and b_col in merged: - merged[f"{col}_speedup"] = merged[b_col] / merged[a_col] - - merged = merged[merged.benchmark == "PrimToConsVectorized"] if cols: merged = merged[cols] @@ -235,8 +282,6 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N rounding |= {c: 5 for c in merged.columns if "cells_per_second" in c or "bytes_per_second" in c} merged = merged.round(rounding) - - out_csv = Path(out_csv) out_csv.parent.mkdir(parents=True, exist_ok=True) merged.to_csv(out_csv, index=False) @@ -245,8 +290,36 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N FILES = { -"skx_new": latest_result(), + "skx_val": f"{RES_DIR}skx/[511571]_skx-PrimToCons_by_val.json", + "skx_ref": f"{RES_DIR}skx/[511590]_skx-PrimToCons_by_ref.json", + "skx_val2": f"{RES_DIR}skx/[512006]_skx-PrimToCons_by_val2.json", + "skx_ref2": f"{RES_DIR}skx/[512028]_skx-PrimToCons_by_ref2.json", + "skx_val3": f"{RES_DIR}skx/[512105]_skx-PrimToCons_by_val3.json", + "skx_ref3": f"{RES_DIR}skx/[512082]_skx-PrimToCons_by_ref3.json", + "skx_ref_ref": f"{RES_DIR}skx/[512246]_skx-PrimToCons_ref_ref.json", +} + + +f = { + # "skx-time_step": f"results/ruche/skx/[519954]_skx-TimeStep.json", + "skx-time_step-opti": f"results/ruche/skx/[519988]_skx-TimeStep.json", + "skx-time_step-opti2": f"results/ruche/skx/[520836]_skx-TimeStep.json", +} + + +g = { + "def" : "results/ruche/skx/tiles/[539688]_tile-def-Godunov.json", + "2s.8.1" : "results/ruche/skx/tiles/[539697]_tile-2s.8.1-Godunov.json", + "s.2.2" : "results/ruche/skx/tiles/[539704]_tile-s.2.2-Godunov.json" + } -plot_scalar_vs_vector(FILES, OUT_DIR) -compare_benchmarks(result_by_job_id(463476), FILES["skx_new"], "store.csv", "ch", "new", cols=["benchmark", "size", "real_time_speedup"]) +COLS = ["benchmark", "size", "real_time_ns_speedup", "bytes_per_second_speedup"] + +# godunov compare skx single thread - a100 +# compare_benchmarks( latest_result(RES_DIR + "skx/all/"),latest_result(RES_DIR + "a100/all/") , "skx-a100.csv", "skx", "a100", cols=COLS) + + +# compare_benchmarks( result_by_job_id(556118, RES_DIR + "skx/all/"),latest_result(RES_DIR + "skx/all/") , "skxm1-skxm20.csv", "skx", "a100", cols=COLS) + +plot_scalar_vs_vector({"a100-god" : latest_result(RES_DIR + "a100/all/")}, OUT_DIR + "a100/all/") diff --git a/utils/utils.hpp b/utils/utils.hpp index 287941b..652ea83 100644 --- a/utils/utils.hpp +++ b/utils/utils.hpp @@ -1,7 +1,6 @@ #pragma once #include - -#include "euler_arrays.hpp" +#include template < class ElementType, From 45a86fe3e87cac31bb2afc1264a3068b073f421d Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 16 Apr 2026 10:55:11 +0200 Subject: [PATCH 26/56] remove constexpr for remainder, to create PR --- euler_operators/cons_to_prim.hpp | 8 ++------ euler_operators/prim_to_cons.hpp | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 395c96b..2071ba1 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -105,11 +105,7 @@ void cons_to_prim_vec( IndexType const vec_end = (nx / simd_t::size()) * simd_t::size(); cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, IndexType(0), vec_end, eos); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx) { - cons_to_prim_kernel< - simd_scalar_t>(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); - } + if (vec_end < nx) { + cons_to_prim_kernel(exec_space, cons_arrays, prim_arrays, vec_end, nx, eos); } } diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index 1bbaba6..d7162d0 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -109,11 +109,7 @@ void prim_to_cons_vec( prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, IndexType(0), vec_end, eos); - static constexpr bool needs_scalar_tail = (simd_t::size() > 1); - if constexpr (needs_scalar_tail) { - if (vec_end < nx) { - prim_to_cons_kernel< - simd_scalar_t>(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); - } + if (vec_end < nx) { + prim_to_cons_kernel(exec_space, prim_arrays, cons_arrays, vec_end, nx, eos); } } From 1465cf64a2780c028914da4cb0cf920e854671a4 Mon Sep 17 00:00:00 2001 From: Timothy Date: Mon, 20 Apr 2026 13:52:31 +0200 Subject: [PATCH 27/56] only use OMP_PLACES=numa_domains --- benchmarks/benchmark_godunov.cpp | 8 +- benchmarks/benchmark_utils.hpp | 2 + setups/ruche/skx/run_bench.sh | 37 +++-- simulations/plot.py | 227 ++++++++++++++++++------------- 4 files changed, 160 insertions(+), 114 deletions(-) diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index b503cdd..05382e0 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -142,7 +142,7 @@ void GodunovVectorizedWorstRem(benchmark::State& state) } } // namespace -BENCHMARK(Godunov)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); -BENCHMARK(GodunovVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); -BENCHMARK(GodunovWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); -BENCHMARK(GodunovVectorizedWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); +BENCHMARK_RT(Godunov)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK_RT(GodunovVectorized)->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK_RT(GodunovWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); +BENCHMARK_RT(GodunovVectorizedWorstRem)->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); diff --git a/benchmarks/benchmark_utils.hpp b/benchmarks/benchmark_utils.hpp index 6894e3d..f8c043e 100644 --- a/benchmarks/benchmark_utils.hpp +++ b/benchmarks/benchmark_utils.hpp @@ -5,6 +5,8 @@ #include +#define BENCHMARK_RT(func) BENCHMARK(func)->UseRealTime() + template R int_cast(T t) diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index 509b66b..fd3bbfd 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -2,33 +2,40 @@ #SBATCH --job-name=bench_skx #SBATCH --output=./slurm_out/%x.o%j #SBATCH --ntasks=1 -#SBATCH --cpus-per-task=20 -#SBATCH --time=00:15:00 +#SBATCH --cpus-per-task=40 +#SBATCH --time=00:20:00 #SBATCH --partition=cpu_short #SBATCH --exclusive #SBATCH --hint=nomultithread - #SBATCH --nodes=1 module purge -module load \ - gcc/13.4.0/gcc-15.1.0 \ - cmake/3.31.9/gcc-15.1.0 - +module load gcc/13.4.0/gcc-15.1.0 cmake/3.31.9/gcc-15.1.0 numactl/2.0.19/gcc-15.1.0 set -x cd ${SLURM_SUBMIT_DIR} - mkdir -p slurm_out results/ruche/skx +BENCHMARK_FILTER=${1:-""} -export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK} -# export OMP_PROC_BIND=true -export OMP_PROC_BIND=close -export OMP_PLACES=cores +# # Single-threaded baseline +# echo "========== RUNNING WITH 1 THREAD (baseline) ==========" +# export OMP_NUM_THREADS=1 +# unset OMP_PROC_BIND +# unset OMP_PLACES +# ./build-skx/benchmarks/euler_benchmarks \ +# --benchmark_filter="${BENCHMARK_FILTER}" \ +# --benchmark_out_format=json \ +# --benchmark_out=./results/ruche/skx/mt/"[${SLURM_JOB_ID}]_T1_baseline_${BENCHMARK_FILTER}.json" -BENCHMARK_FILTER=${1:-""} +# 20 threads on one socket +echo "========== RUNNING WITH 20 THREADS (socket 0) ==========" +export OMP_NUM_THREADS=20 +export OMP_PROC_BIND=close +# export OMP_PLACES=cores +export OMP_PLACES=numa_domains -# include SLURM_JOB_ID in the JSON output filename ./build-skx/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/skx/all/"[${SLURM_JOB_ID}]_m20-${BENCHMARK_FILTER}.json" + --benchmark_out=./results/ruche/skx/mt/"[${SLURM_JOB_ID}]_T20-debug${BENCHMARK_FILTER}.json" + +numastat -m -n $(pidof euler_benchmarks) diff --git a/simulations/plot.py b/simulations/plot.py index 02265c3..ec372ff 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -15,19 +15,33 @@ # --------------------------------------------------------- -OUT_DIR = "results/plots/" +OUT_DIR = "results/plots/skx/mt/" import os import glob -RES_DIR = "results/ruche/" +RES_DIR = "results/ruche/skx/mt/" -def latest_result(res_dir=RES_DIR, pattern="*.json"): +# def latest_result(res_dir=RES_DIR, pattern="*.json", rank=1): +# files = glob.glob(os.path.join(res_dir, pattern)) +# if not files: +# raise FileNotFoundError(f"No files matching {pattern} in {res_dir}") +# # return max(files, key=os.path.getmtime) +# return max(files, key=os.path.getmtime) + +def latest_result(res_dir, pattern="*.json", rank=1): files = glob.glob(os.path.join(res_dir, pattern)) - print(files) + if not files: raise FileNotFoundError(f"No files matching {pattern} in {res_dir}") - return max(files, key=os.path.getmtime) + + # sort by modification time (newest first) + files = sorted(files, key=os.path.getmtime, reverse=True) + + if rank < 1 or rank > len(files): + raise IndexError(f"rank={rank} out of range (1..{len(files)})") + + return files[rank - 1] def result_by_job_id(job_id, res_dir=RES_DIR): prefix = f"[{job_id}]" @@ -68,10 +82,11 @@ def load_one(path): name = b["name"] rows.append({ "benchmark": name.split("/")[0], - "size": int(name.split("/")[-1]), + "size": int(name.split("/")[-2 if "real_time" in name else -1]), "cells_per_second": b.get("cells_per_second"), "bytes_per_second": b.get("bytes_per_second"), "real_time_ns": b.get("real_time"), + "cpu_time_ns": b.get("cpu_time"), }) return pd.DataFrame(rows), caches @@ -89,18 +104,25 @@ def _draw_cache_lines(ax, caches): ) -def _plot_series(ax, df_series, color, label, y_key, alpha=1.0): +def _plot_series(ax, df_series, color, label, y_key, alpha=1.0, linestyle="-"): aligned = df_series[df_series["size"] % 8 == 0] unaligned = df_series[df_series["size"] % 8 != 0] - ax.plot(df_series["size"], df_series[y_key], "-", color=color, - label=label, alpha=alpha) - ax.scatter(aligned["size"], aligned[y_key], marker="o", - color=color, zorder=5, alpha=alpha) - ax.scatter(unaligned["size"], unaligned[y_key], marker="x", - color=color, zorder=5, alpha=alpha) + ax.plot( + df_series["size"], + df_series[y_key], + linestyle, + color=color, + label=label, + alpha=alpha, + ) -def plot_scalar_vs_vector(files, out_dir): + ax.scatter(aligned["size"], aligned[y_key], + marker="o", color=color, zorder=5, alpha=alpha) + ax.scatter(unaligned["size"], unaligned[y_key], + marker="x", color=color, zorder=5, alpha=alpha) + +def plot_scalar_vs_vector(files, out_dir, draw_caches=True): out_dir = Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) @@ -113,8 +135,9 @@ def plot_scalar_vs_vector(files, out_dir): base_names = [b for b in all_names if b + "Vectorized" in all_names] for base_name in base_names: - if "Godunov" not in base_name: - continue + + # if "Godunov" not in base_name: + # continue vec_name = base_name + "Vectorized" for environment, path in files.items(): @@ -145,7 +168,8 @@ def plot_scalar_vs_vector(files, out_dir): _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", "bytes_per_second", alpha=0.4) - _draw_cache_lines(ax_left, caches) + if draw_caches: + _draw_cache_lines(ax_left, caches) ax_left.set_xlabel("n (cube width in cells)") ax_left.set_ylabel("cells / s") @@ -159,8 +183,12 @@ def plot_scalar_vs_vector(files, out_dir): # ── right plot: wall time + speedup ────────────────────────── ax_speedup = ax_right.twinx() - _plot_series(ax_right, s, "C0", "scalar ns", "real_time_ns") - _plot_series(ax_right, v, "C1", "vectorized ns", "real_time_ns") + + _plot_series(ax_right, s, "C0", "scalar real time", "real_time_ns", linestyle="-") + _plot_series(ax_right, v, "C1", "vectorized real time", "real_time_ns", linestyle="-") + + _plot_series(ax_right, s, "C0", "scalar cpu time", "cpu_time_ns", linestyle="--") + _plot_series(ax_right, v, "C1", "vectorized cpu time", "cpu_time_ns", linestyle="--") # speedup: scalar / vectorized on shared sizes merged = pd.merge( @@ -181,7 +209,8 @@ def plot_scalar_vs_vector(files, out_dir): ) ax_speedup.axhline(1.0, linestyle=":", color="C2", alpha=0.5) - _draw_cache_lines(ax_right, caches) + if draw_caches: + _draw_cache_lines(ax_right, caches) ax_right.set_xlabel("n (cube width in cells)") ax_right.set_ylabel("real time (ns)") @@ -201,49 +230,7 @@ def plot_scalar_vs_vector(files, out_dir): plt.savefig(save_name, dpi=200) plt.close() -# def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): -# df_a, _ = load_one(path_a) -# df_b, _ = load_one(path_b) - -# merged = pd.merge( -# df_a, -# df_b, -# on=["benchmark", "size"], -# suffixes=(f"_{label_a}", f"_{label_b}"), -# how="inner", -# ) - - -# merged = merged[ -# merged.benchmark.isin(["GodunovVectorized", "Godunov"]) -# ] - -# col ="real_time_ns" -# a_col = f"{col}_{label_a}" -# b_col = f"{col}_{label_b}" -# if a_col in merged and b_col in merged: -# merged[f"{col}_speedup"] = merged[a_col] / merged[b_col] - -# if cols: -# merged = merged[cols] - -# mean_row = merged.mean(numeric_only=True).to_frame().T -# mean_row["benchmark"] = "MEAN" -# mean_row["size"] = pd.NA -# merged = pd.concat([merged, mean_row], ignore_index=True) - -# rounding = {c: 5 for c in merged.columns if "speedup" in c} -# rounding |= {c: 5 for c in merged.columns if "time" in c} -# rounding |= {c: 5 for c in merged.columns if "cells_per_second" in c or "bytes_per_second" in c} -# merged = merged.round(rounding) - - - -# out_csv = Path(out_csv) -# out_csv.parent.mkdir(parents=True, exist_ok=True) -# merged.to_csv(out_csv, index=False) -# return merged -def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): +def compare_benchmarks(path_a, path_b, out_csv,label_a="a", label_b="b",cols=None): df_a, _ = load_one(path_a) df_b, _ = load_one(path_b) merged = pd.merge( @@ -253,15 +240,16 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N suffixes=(f"_{label_a}", f"_{label_b}"), how="inner", ) - merged = merged[ - merged.benchmark.isin(["GodunovVectorized", "Godunov"]) - ] + # merged = merged[ + # merged.benchmark.isin(["GodunovVectorized", "Godunov"]) + # ] # time speedup: lower is better, so a/b (b is faster if > 1) - time_col = "real_time_ns" - a_t, b_t = f"{time_col}_{label_a}", f"{time_col}_{label_b}" - if a_t in merged and b_t in merged: - merged[f"{time_col}_speedup"] = merged[a_t] / merged[b_t] + time_cols = ["real_time_ns", "cpu_time_ns"] + for time_col in time_cols: + a_t, b_t = f"{time_col}_{label_a}", f"{time_col}_{label_b}" + if a_t in merged and b_t in merged: + merged[f"{time_col}_speedup"] = merged[a_t] / merged[b_t] # bytes/s speedup: higher is better, so b/a (b is faster if > 1) bw_col = "bytes_per_second" @@ -288,38 +276,87 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N return merged # %% +COLS = ["benchmark", "size", "real_time_ns_speedup" , "cpu_time_ns_speedup"] -FILES = { - "skx_val": f"{RES_DIR}skx/[511571]_skx-PrimToCons_by_val.json", - "skx_ref": f"{RES_DIR}skx/[511590]_skx-PrimToCons_by_ref.json", - "skx_val2": f"{RES_DIR}skx/[512006]_skx-PrimToCons_by_val2.json", - "skx_ref2": f"{RES_DIR}skx/[512028]_skx-PrimToCons_by_ref2.json", - "skx_val3": f"{RES_DIR}skx/[512105]_skx-PrimToCons_by_val3.json", - "skx_ref3": f"{RES_DIR}skx/[512082]_skx-PrimToCons_by_ref3.json", - "skx_ref_ref": f"{RES_DIR}skx/[512246]_skx-PrimToCons_ref_ref.json", -} +plot_scalar_vs_vector({"skx" : latest_result(RES_DIR)}, OUT_DIR + "numa/") +# plot_scalar_vs_vector({"skx" : latest_result(RES_DIR , rank=2)}, OUT_DIR + "numa/") +# compare_benchmarks( result_by_job_id(644750, RES_DIR), result_by_job_id(646848, RES_DIR), OUT_DIR + "mt1-mt40.csv", cols=COLS) +# compare_benchmarks( result_by_job_id(644750, RES_DIR), latest_result(RES_DIR), OUT_DIR + "spread_mt1-mt20.csv", cols=COLS) +# +# -f = { - # "skx-time_step": f"results/ruche/skx/[519954]_skx-TimeStep.json", - "skx-time_step-opti": f"results/ruche/skx/[519988]_skx-TimeStep.json", - "skx-time_step-opti2": f"results/ruche/skx/[520836]_skx-TimeStep.json", -} +def extract_threads(filename: str) -> int: + match = re.search(r"mt1-mt(\d+)", filename) + if not match: + raise ValueError(f"Cannot parse thread count from {filename}") + return int(match.group(1)) -g = { - "def" : "results/ruche/skx/tiles/[539688]_tile-def-Godunov.json", - "2s.8.1" : "results/ruche/skx/tiles/[539697]_tile-2s.8.1-Godunov.json", - "s.2.2" : "results/ruche/skx/tiles/[539704]_tile-s.2.2-Godunov.json" - -} -COLS = ["benchmark", "size", "real_time_ns_speedup", "bytes_per_second_speedup"] +def plot_speedup_from_dir(directory: str): + csv_files = sorted(glob.glob(os.path.join(directory, "*.csv"))) -# godunov compare skx single thread - a100 -# compare_benchmarks( latest_result(RES_DIR + "skx/all/"),latest_result(RES_DIR + "a100/all/") , "skx-a100.csv", "skx", "a100", cols=COLS) + if not csv_files: + raise ValueError(f"No CSV files found in {directory}") + + # Map thread count -> color + thread_counts = sorted( + extract_threads(os.path.basename(f)) for f in csv_files + ) + + cmap = plt.get_cmap("tab10") + color_map = { + t: cmap(i % 10) for i, t in enumerate(thread_counts) + } + + plt.figure() + + for filepath in csv_files: + df = pd.read_csv(filepath) + + df = df[df["benchmark"] != "MEAN"] + df = df.dropna(subset=["size", "real_time_ns_speedup"]) + + filename = os.path.basename(filepath).replace(".csv", "") + threads = extract_threads(filename) + if threads < 16: + continue + color = color_map[threads] + + for bench_name, group in df.groupby("benchmark"): + group = group.sort_values("size") + + # Style based on kernel + if bench_name == "Godunov": + linestyle = "-" + marker = "o" + elif bench_name == "GodunovVectorized": + linestyle = "--" + marker = "s" + else: + linestyle = ":" + marker = "x" + + label = f"{threads} threads - {bench_name}" + + plt.plot( + group["size"], + group["real_time_ns_speedup"], + color=color, + linestyle=linestyle, + marker=marker, + label=label + ) + plt.xlabel("Problem Size") + plt.ylabel("Real Time Speedup") + plt.title("Speedup vs Size (Color = Threads, Style = Kernel)") -# compare_benchmarks( result_by_job_id(556118, RES_DIR + "skx/all/"),latest_result(RES_DIR + "skx/all/") , "skxm1-skxm20.csv", "skx", "a100", cols=COLS) + plt.legend(ncol=2, fontsize=8) + plt.grid(True) + plt.tight_layout() + # plt.show() + plt.savefig("godunov_mt_speedup_threshold", dpi=200) -plot_scalar_vs_vector({"a100-god" : latest_result(RES_DIR + "a100/all/")}, OUT_DIR + "a100/all/") +# plot_speedup_from_dir(OUT_DIR) From 644d90d7d8db3f7bb4f502a8ec7cb13743789667 Mon Sep 17 00:00:00 2001 From: tim-pearson <90312373+tim-pearson@users.noreply.github.com> Date: Mon, 20 Apr 2026 14:00:15 +0200 Subject: [PATCH 28/56] Update euler_operators/cons_to_prim.hpp Co-authored-by: Thomas Padioleau --- euler_operators/cons_to_prim.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index 0a8f8c8..ba02058 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -4,7 +4,6 @@ #include #include - template void cons_to_prim( Kokkos::DefaultExecutionSpace const& exec_space, From ab840315ca9b16cec7031830924bb4ecae100050 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 21 Apr 2026 14:22:47 +0200 Subject: [PATCH 29/56] remove test/ dir duplicate, clang tidy + format --- benchmarks/benchmark_cons_to_prim.cpp | 22 ++++-- benchmarks/benchmark_euler_simulation.cpp | 1 - benchmarks/benchmark_godunov.cpp | 2 +- benchmarks/benchmark_prim_to_cons.cpp | 22 ++++-- euler_operators/cons_to_prim.hpp | 4 +- euler_operators/euler_arrays.hpp | 2 +- euler_operators/prim_to_cons.hpp | 2 +- test/CMakeLists.txt | 12 ---- test/test_cons_to_prim.cpp | 73 -------------------- test/test_main.cpp | 21 ------ test/test_prim_to_cons.cpp | 84 ----------------------- tests/test_cons_to_prim.cpp | 19 +++-- tests/test_prim_to_cons.cpp | 19 +++-- 13 files changed, 59 insertions(+), 224 deletions(-) delete mode 100644 test/CMakeLists.txt delete mode 100644 test/test_cons_to_prim.cpp delete mode 100644 test/test_main.cpp delete mode 100644 test/test_prim_to_cons.cpp diff --git a/benchmarks/benchmark_cons_to_prim.cpp b/benchmarks/benchmark_cons_to_prim.cpp index 4821437..46927f3 100644 --- a/benchmarks/benchmark_cons_to_prim.cpp +++ b/benchmarks/benchmark_cons_to_prim.cpp @@ -1,14 +1,16 @@ +#include + #include #include #include #include #include +#include #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" -#include "utils.hpp" namespace { @@ -47,12 +49,14 @@ void ConsToPrimVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, @@ -76,12 +80,14 @@ void ConsToPrimWorstRem(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, @@ -104,12 +110,14 @@ void ConsToPrimWorstRemVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index 67895ce..9644bb6 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -11,7 +11,6 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index 947a6a3..45a090a 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -6,11 +6,11 @@ #include #include #include -#include "utils.hpp" #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" +#include "utils.hpp" namespace { diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index db25b24..49612b4 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -1,14 +1,16 @@ +#include + #include #include #include #include #include +#include #include "benchmark_utils.hpp" #include "index_type.hpp" #include "real_type.hpp" -#include "utils.hpp" namespace { @@ -48,12 +50,14 @@ void PrimToConsVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, @@ -76,12 +80,14 @@ void PrimToConsWorstRem(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, @@ -105,12 +111,14 @@ void PrimToConsWorstRemVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + EulerPrimArrays const prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + EulerConsArrays const cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); EulerConsArrays const cons_arrays = to_mdspan, diff --git a/euler_operators/cons_to_prim.hpp b/euler_operators/cons_to_prim.hpp index ba02058..aad6580 100644 --- a/euler_operators/cons_to_prim.hpp +++ b/euler_operators/cons_to_prim.hpp @@ -68,12 +68,12 @@ void cons_to_prim_kernel( Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const i = nx_begin + bi * width; + IndexType const i = nx_begin + (bi * width); IndexType const base = cons_arrays.d.mapping()(i, j, k); EulerCons const cons = load(cons_arrays, base); - EulerPrim prim + EulerPrim const prim = to_prim(cons, eos.pressure(cons.d, internal_energy(cons))); store(prim, prim_ptrs, base); } diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 68cd7cd..8404aed 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -76,7 +76,7 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons to_cons(EulerPrim const prim, T cons T m1 = prim.d * prim.ux1; T m2 = prim.d * prim.ux2; - T e_kin = (m0 * prim.ux0 + m1 * prim.ux1 + m2 * prim.ux2) * 0.5; + T e_kin = ((m0 * prim.ux0) + (m1 * prim.ux1) + (m2 * prim.ux2)) * 0.5; return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; } diff --git a/euler_operators/prim_to_cons.hpp b/euler_operators/prim_to_cons.hpp index d7162d0..7e964b3 100644 --- a/euler_operators/prim_to_cons.hpp +++ b/euler_operators/prim_to_cons.hpp @@ -74,7 +74,7 @@ void prim_to_cons_kernel( Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, Kokkos::IndexType>(exec_space, {0, 0, 0}, {nx_blocks, ny, nz}), KOKKOS_LAMBDA(IndexType bi, IndexType j, IndexType k) { - IndexType const base = prim_arrays.d.mapping()(nx_begin + bi * width, j, k); + IndexType const base = prim_arrays.d.mapping()(nx_begin + (bi * width), j, k); EulerPrim const prim = load(prim_arrays, base); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index c8cf226..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -find_package(GTest REQUIRED) - -add_executable(euler_tests) -target_link_libraries( - euler_tests - PRIVATE GTest::gtest_main euler_operators Kokkos::kokkos euler_utils -) -target_sources(euler_tests PRIVATE test_main.cpp test_prim_to_cons.cpp test_cons_to_prim.cpp) - -enable_testing() -include(GoogleTest) -gtest_discover_tests(euler_tests DISCOVERY_MODE PRE_TEST) diff --git a/test/test_cons_to_prim.cpp b/test/test_cons_to_prim.cpp deleted file mode 100644 index 4ec7faa..0000000 --- a/test/test_cons_to_prim.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "utils.hpp" - -TEST(ConsToPrimRemainder, ScalarVsVectorized) -{ - using real_t = double; - using index_t = int; - int const n = 23; - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas eos(1.4); - - auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); - // --- allocate base --- - auto prims_alloc_ref = create_prim_arrays_1d(exec_space, n * n * n); - // --- allocate vectorized --- - auto prims_alloc_vec = create_prim_arrays_1d(exec_space, n * n * n); - - auto cons_arrays = to_mdspan, - Kokkos::layout_left>>(cons_alloc, n, n, n); - auto prim_ref = to_mdspan, - Kokkos::layout_left>>(prims_alloc_ref, n, n, n); - auto prim_vec = to_mdspan, - Kokkos::layout_left>>(prims_alloc_vec, n, n, n); - - // --- initialize with non-trivial conserved state --- - EulerCons cons {.d = 1.0, .e = 2.5, .mx0 = 0.5, .mx1 = -0.3, .mx2 = 0.1}; - init_from_state(exec_space, cons_arrays, cons); - exec_space.fence(); - - // --- run both --- - cons_to_prim(exec_space, as_const(cons_arrays), prim_ref, eos); - cons_to_prim_vec(exec_space, as_const(cons_arrays), prim_vec, eos); - exec_space.fence(); - - auto ref_h = EulerPrimArrays { - .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.d), - .p = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.p), - .ux0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux0), - .ux1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux1), - .ux2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_ref.ux2)}; - auto vec_h = EulerPrimArrays { - .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.d), - .p = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.p), - .ux0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux0), - .ux1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux1), - .ux2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), prims_alloc_vec.ux2)}; - - double const tol = 1e-12; - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - for (int k = 0; k < n; ++k) { - int idx = i + (n * (j + n * k)); // layout_left flattening - ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); - ASSERT_NEAR(ref_h.p(idx), vec_h.p(idx), tol); - ASSERT_NEAR(ref_h.ux0(idx), vec_h.ux0(idx), tol); - ASSERT_NEAR(ref_h.ux1(idx), vec_h.ux1(idx), tol); - ASSERT_NEAR(ref_h.ux2(idx), vec_h.ux2(idx), tol); - } - } - } -} diff --git a/test/test_main.cpp b/test/test_main.cpp deleted file mode 100644 index b8a71fe..0000000 --- a/test/test_main.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -#include - -#include -#include - - -int main(int argc, char** argv) -{ - int ret = -1; - Kokkos::initialize(argc, argv); - { - Kokkos::print_configuration(std::cout); - ::testing::InitGoogleTest(&argc, argv); - ret = RUN_ALL_TESTS(); - } - Kokkos::finalize(); - return ret; -} diff --git a/test/test_prim_to_cons.cpp b/test/test_prim_to_cons.cpp deleted file mode 100644 index 791f762..0000000 --- a/test/test_prim_to_cons.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "utils.hpp" - -TEST(PrimToConsRemainder, ScalarVsVectorized) -{ - using real_t = double; - using index_t = int; - - int const n = 23; - - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas eos(1.4); - - auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); - // --- allocate base --- - auto cons_alloc_ref = create_cons_arrays_1d(exec_space, n * n * n); - - // --- allocate vectorized --- - auto cons_alloc_vec = create_cons_arrays_1d(exec_space, n * n * n); - - auto prim_arrays = to_mdspan, - Kokkos::layout_left>>(prims_alloc, n, n, n); - - auto cons_ref = to_mdspan, - Kokkos::layout_left>>(cons_alloc_ref, n, n, n); - - auto cons_vec = to_mdspan, - Kokkos::layout_left>>(cons_alloc_vec, n, n, n); - - // --- initialize with non-trivial state --- - EulerPrim prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; - - init_from_state(exec_space, prim_arrays, prim); - exec_space.fence(); - - // --- run both --- - prim_to_cons(exec_space, as_const(prim_arrays), cons_ref, eos); - prim_to_cons_vec(exec_space, as_const(prim_arrays), cons_vec, eos); - exec_space.fence(); - - auto ref_h = EulerConsArrays { - .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.d), - .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.e), - .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx0), - .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx1), - .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_ref.mx2)}; - - auto vec_h = EulerConsArrays { - .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.d), - .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.e), - .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx0), - .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx1), - .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_alloc_vec.mx2)}; - - - double const tol = 1e-12; - - - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - for (int k = 0; k < n; ++k) { - int idx = i + (n * (j + n * k)); // layout_left flattening - - ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); - ASSERT_NEAR(ref_h.mx0(idx), vec_h.mx0(idx), tol); - ASSERT_NEAR(ref_h.mx1(idx), vec_h.mx1(idx), tol); - ASSERT_NEAR(ref_h.mx2(idx), vec_h.mx2(idx), tol); - ASSERT_NEAR(ref_h.e(idx), vec_h.e(idx), tol); - } - } - } -} diff --git a/tests/test_cons_to_prim.cpp b/tests/test_cons_to_prim.cpp index 4ec7faa..184c0fa 100644 --- a/tests/test_cons_to_prim.cpp +++ b/tests/test_cons_to_prim.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -12,14 +14,17 @@ TEST(ConsToPrimRemainder, ScalarVsVectorized) using real_t = double; using index_t = int; int const n = 23; - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + PerfectGas const eos(1.4); - auto cons_alloc = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_alloc + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); // --- allocate base --- - auto prims_alloc_ref = create_prim_arrays_1d(exec_space, n * n * n); + auto prims_alloc_ref + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); // --- allocate vectorized --- - auto prims_alloc_vec = create_prim_arrays_1d(exec_space, n * n * n); + auto prims_alloc_vec + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); auto cons_arrays = to_mdspan>(prims_alloc_vec, n, n, n); // --- initialize with non-trivial conserved state --- - EulerCons cons {.d = 1.0, .e = 2.5, .mx0 = 0.5, .mx1 = -0.3, .mx2 = 0.1}; + EulerCons const cons {.d = 1.0, .e = 2.5, .mx0 = 0.5, .mx1 = -0.3, .mx2 = 0.1}; init_from_state(exec_space, cons_arrays, cons); exec_space.fence(); @@ -61,7 +66,7 @@ TEST(ConsToPrimRemainder, ScalarVsVectorized) for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { - int idx = i + (n * (j + n * k)); // layout_left flattening + int const idx = i + (n * (j + (n * k))); // layout_left flattening ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); ASSERT_NEAR(ref_h.p(idx), vec_h.p(idx), tol); ASSERT_NEAR(ref_h.ux0(idx), vec_h.ux0(idx), tol); diff --git a/tests/test_prim_to_cons.cpp b/tests/test_prim_to_cons.cpp index 791f762..0254f22 100644 --- a/tests/test_prim_to_cons.cpp +++ b/tests/test_prim_to_cons.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -14,15 +16,18 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) int const n = 23; - Kokkos::DefaultExecutionSpace exec_space; - PerfectGas eos(1.4); + Kokkos::DefaultExecutionSpace const exec_space; + PerfectGas const eos(1.4); - auto prims_alloc = create_prim_arrays_1d(exec_space, n * n * n); + auto prims_alloc + = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); // --- allocate base --- - auto cons_alloc_ref = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_alloc_ref + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); // --- allocate vectorized --- - auto cons_alloc_vec = create_cons_arrays_1d(exec_space, n * n * n); + auto cons_alloc_vec + = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); auto prim_arrays = to_mdspan>(cons_alloc_vec, n, n, n); // --- initialize with non-trivial state --- - EulerPrim prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; + EulerPrim const prim {.d = 1.0, .p = 1.0, .ux0 = 0.5, .ux1 = -0.3, .ux2 = 0.1}; init_from_state(exec_space, prim_arrays, prim); exec_space.fence(); @@ -71,7 +76,7 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { - int idx = i + (n * (j + n * k)); // layout_left flattening + int const idx = i + (n * (j + (n * k))); // layout_left flattening ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); ASSERT_NEAR(ref_h.mx0(idx), vec_h.mx0(idx), tol); From bdcc0319cd53b382076534a97fca68842ed9e747 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 21 Apr 2026 16:36:21 +0200 Subject: [PATCH 30/56] shellcheck, pylint --- euler_operators/euler_arrays.hpp | 3 +- euler_operators/perfect_gas.hpp | 4 - setups/ruche/a100/run_bench.sh | 2 +- setups/ruche/a100/run_sim.sh | 2 +- setups/ruche/a100/run_test.sh | 2 +- setups/ruche/skx/prepare.sh | 2 +- setups/ruche/skx/run_bench.sh | 2 +- setups/ruche/skx/run_sim.sh | 2 +- setups/ruche/skx/run_test.sh | 2 +- setups/ruche/v100/run_sim.sh | 2 +- simulations/display_results.py | 72 +----- simulations/plot.py | 427 ++++++++++++++++++++----------- 12 files changed, 297 insertions(+), 225 deletions(-) diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 8404aed..15031b4 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -75,8 +75,9 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons to_cons(EulerPrim const prim, T cons T m0 = prim.d * prim.ux0; T m1 = prim.d * prim.ux1; T m2 = prim.d * prim.ux2; + constexpr T half = 0.5; - T e_kin = ((m0 * prim.ux0) + (m1 * prim.ux1) + (m2 * prim.ux2)) * 0.5; + T e_kin = ((m0 * prim.ux0) + (m1 * prim.ux1) + (m2 * prim.ux2)) * half; return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; } diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 14bd938..c758d88 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -12,10 +12,6 @@ class PerfectGas public: explicit PerfectGas(T const gamma) : m_gamma(gamma), gamma_minus_one_inv(1 / (gamma - 1)) {} - T get_gamma() const noexcept - { - return m_gamma; - } KOKKOS_FUNCTION T speed_of_sound(T const density, T const pressure) const noexcept diff --git a/setups/ruche/a100/run_bench.sh b/setups/ruche/a100/run_bench.sh index f5c3bd1..92ffeb5 100644 --- a/setups/ruche/a100/run_bench.sh +++ b/setups/ruche/a100/run_bench.sh @@ -14,7 +14,7 @@ module load \ cuda/12.8.1/none-none set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/a100 diff --git a/setups/ruche/a100/run_sim.sh b/setups/ruche/a100/run_sim.sh index 6cfe062..7f84c9b 100644 --- a/setups/ruche/a100/run_sim.sh +++ b/setups/ruche/a100/run_sim.sh @@ -14,7 +14,7 @@ module load \ cuda/12.8.1/none-none set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/a100 diff --git a/setups/ruche/a100/run_test.sh b/setups/ruche/a100/run_test.sh index f4a1a11..505f4dd 100644 --- a/setups/ruche/a100/run_test.sh +++ b/setups/ruche/a100/run_test.sh @@ -14,7 +14,7 @@ module load \ cuda/12.8.1/none-none set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/a100 diff --git a/setups/ruche/skx/prepare.sh b/setups/ruche/skx/prepare.sh index 1dd87e6..44cd587 100755 --- a/setups/ruche/skx/prepare.sh +++ b/setups/ruche/skx/prepare.sh @@ -44,7 +44,7 @@ cmake \ -B build-gtest \ -S googletest cmake --build build-gtest -cmake --install build-gtest --prefix $gtest_ROOT +cmake --install build-gtest --prefix "$gtest_ROOT" rm -rf build-gtest googletest cmake -D CMAKE_BUILD_TYPE=Release -B build-skx diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index ef33858..ee1c8a5 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -14,7 +14,7 @@ module load \ cmake/3.31.9/gcc-15.1.0 set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/skx diff --git a/setups/ruche/skx/run_sim.sh b/setups/ruche/skx/run_sim.sh index ccf57eb..ade3671 100644 --- a/setups/ruche/skx/run_sim.sh +++ b/setups/ruche/skx/run_sim.sh @@ -12,7 +12,7 @@ module load \ cmake/3.31.9/gcc-15.1.0 set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/skx diff --git a/setups/ruche/skx/run_test.sh b/setups/ruche/skx/run_test.sh index 1692b7d..b2492d5 100644 --- a/setups/ruche/skx/run_test.sh +++ b/setups/ruche/skx/run_test.sh @@ -12,7 +12,7 @@ module load \ cmake/3.31.9/gcc-15.1.0 set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out results/ruche/skx diff --git a/setups/ruche/v100/run_sim.sh b/setups/ruche/v100/run_sim.sh index 62d5158..7968f62 100644 --- a/setups/ruche/v100/run_sim.sh +++ b/setups/ruche/v100/run_sim.sh @@ -13,7 +13,7 @@ module load \ cuda/12.8.1/none-none set -x -cd ${SLURM_SUBMIT_DIR} +cd "${SLURM_SUBMIT_DIR}" || exit mkdir -p slurm_out diff --git a/simulations/display_results.py b/simulations/display_results.py index fa3985e..d9e316c 100644 --- a/simulations/display_results.py +++ b/simulations/display_results.py @@ -11,8 +11,9 @@ """ import argparse -import numpy as np + import matplotlib.pyplot as plt +import numpy as np def main(): @@ -40,72 +41,5 @@ def main(): plt.show() -import argparse -import numpy as np -import matplotlib.pyplot as plt -import glob -import os - -def main_loop(): - parser = argparse.ArgumentParser(description="Display a sequence of .npy files.") - parser.add_argument("path", type=str, help="Directory or glob pattern (e.g. './*.npy')") - parser.add_argument("--delay", type=float, default=0.5, help="Delay between frames (seconds)") - args = parser.parse_args() - - # Resolve files - if os.path.isdir(args.path): - files = glob.glob(os.path.join(args.path, "*.npy")) - else: - files = glob.glob(args.path) - - if not files: - raise RuntimeError("No .npy files found") - - # Sort numerically based on timestep in filename - files.sort() - - print(f"Found {len(files)} files") - - plt.ion() # interactive mode - fig, ax = plt.subplots() - - im = None - i = 0 - while True: - i = i % len(files) - f = files[i] - - - print(f"Loading {f}") - - try: - arr = np.load(f) - except Exception as e: - print(f"Skipping {f}: {e}") - continue - - slice_ = arr[arr.shape[0] // 2] - print(f"{f}: shape={arr.shape}, min={arr.min()}, max={arr.max()}, any NaN={np.isnan(arr).any()}") - - slice_ = arr[arr.shape[0] // 2] - - print(f"Slice {arr.shape[0]//2}: min={slice_.min()}, max={slice_.max()}, any NaN={np.isnan(slice_).any()}") - - if im is None: - im = ax.imshow(slice_, origin="lower") - plt.colorbar(im, ax=ax) - else: - im.set_data(slice_) - im.set_clim(vmin=slice_.min(), vmax=slice_.max()) - - ax.set_title(os.path.basename(f)) - plt.pause(args.delay) - i += 1 - - plt.ioff() - plt.show() - - if __name__ == "__main__": - main_loop() - # main() + main() diff --git a/simulations/plot.py b/simulations/plot.py index 94e59bc..d391519 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -1,35 +1,67 @@ -#!/usr/bin/env python3 +""" +Plotting script for plotting/comparing Google Benchmark JSON files. +This script has two main functionalities: + - plotting scalar vs vectorized benchmark outputs + - creating a csv file comparing two different benchmarks +""" + +import glob import json -import re -import pandas as pd -import matplotlib.pyplot as plt +import os from pathlib import Path -KERNEL_BENCHMARKS = ["Godunov", "TimeStep", "ConsToPrim", "PrimToConsVectorized","PrimToCons" ] +import matplotlib.pyplot as plt +import pandas as pd + +KERNEL_BENCHMARKS = [ + "Godunov", + "TimeStep", + "ConsToPrim", + "PrimToConsVectorized", + "PrimToCons", +] ALL_BENCHMARKS = KERNEL_BENCHMARKS ALL_BENCHMARKS.append("EulerSimulation") -# --------------------------------------------------------- -# Config -# --------------------------------------------------------- - OUT_DIR = "results/plots" - -import os -import glob - RES_DIR = "results/ruche/skx/" + def latest_result(res_dir=RES_DIR, pattern="*.json"): + """Find and return the most recently modified benchmark JSON file. + + Args: + res_dir: Directory to search for benchmark files (default: RES_DIR). + pattern: Glob pattern to match files (default: "*.json"). + + Returns: + Path to the most recently modified file matching the pattern. + + Raises: + FileNotFoundError: If no files matching the pattern are found. + """ files = glob.glob(os.path.join(res_dir, pattern)) print(files) if not files: raise FileNotFoundError(f"No files matching {pattern} in {res_dir}") return max(files, key=os.path.getmtime) + def result_by_job_id(job_id, res_dir=RES_DIR): + """Retrieve a benchmark result file by job ID. + + Args: + job_id: The job ID to search for (used as filename prefix). + res_dir: Directory to search for benchmark files (default: RES_DIR). + + Returns: + Path to the result file for the given job ID. + + Raises: + FileNotFoundError: If no result file is found for the given job ID. + """ prefix = f"[{job_id}]" files = os.listdir(res_dir) for f in files: @@ -37,46 +69,64 @@ def result_by_job_id(job_id, res_dir=RES_DIR): return os.path.join(res_dir, f) raise FileNotFoundError(f"No result found for job {job_id} in {res_dir}") + def extract_label(path): + """Extract a timestamp label from a benchmark result file path. + + Args: + path: Path to the benchmark result file. + + Returns: + A timestamp string extracted from the filename, with a trailing underscore. + """ name = Path(path).name - label = name.split("_")[1] timestamp = name.split("[")[1].split("]")[0] - return timestamp + "_" - + return timestamp + "_" # %% -import json -import pandas as pd -import matplotlib.pyplot as plt -from pathlib import Path BYTES_PER_CELL = 10 * 8 CACHE_COLORS = {1: "green", 2: "orange", 3: "red"} def load_one(path): - with open(path) as f: + """Load and parse a Google Benchmark JSON file. + + Args: + path: Path to the JSON benchmark file. + + Returns: + A tuple of (DataFrame, caches_dict) where: + - DataFrame contains benchmark data with columns: benchmark, size, + cells_per_second, bytes_per_second, real_time_ns + - caches_dict is a mapping of cache level to size in bytes + """ + with open(path, encoding="utf-8") as f: raw = json.load(f) - caches = { - c["level"]: c["size"] - for c in raw["context"]["caches"] - if c["type"] == "Unified" - } + caches = {c["level"]: c["size"] for c in raw["context"]["caches"] if c["type"] == "Unified"} rows = [] for b in raw["benchmarks"]: name = b["name"] - rows.append({ - "benchmark": name.split("/")[0], - "size": int(name.split("/")[-1]), - "cells_per_second": b.get("cells_per_second"), - "bytes_per_second": b.get("bytes_per_second"), - "real_time_ns": b.get("real_time"), - }) + rows.append( + { + "benchmark": name.split("/")[0], + "size": int(name.split("/")[-1]), + "cells_per_second": b.get("cells_per_second"), + "bytes_per_second": b.get("bytes_per_second"), + "real_time_ns": b.get("real_time"), + } + ) return pd.DataFrame(rows), caches def _draw_cache_lines(ax, caches): + """Draw vertical lines on a plot indicating cache level boundaries. + + Args: + ax: Matplotlib axis object to draw on. + caches: Dictionary mapping cache level to size in bytes. + """ for level, size_bytes in sorted(caches.items()): n_cache = (size_bytes / BYTES_PER_CELL) ** (1 / 3) color = CACHE_COLORS.get(level, "gray") @@ -89,117 +139,205 @@ def _draw_cache_lines(ax, caches): ) -def _plot_series(ax, df_series, color, label, y_key, alpha=1.0): - aligned = df_series[df_series["size"] % 8 == 0] +def _plot_series(ax, df_series, color, label, y_key): + """Plot a benchmark series with aligned and unaligned data points. + + Args: + ax: Matplotlib axis object to plot on. + df_series: DataFrame containing the series data. + color: Color for the plot line and markers. + label: Label for the series. + y_key: Column name to plot on the y-axis. + """ + aligned = df_series[df_series["size"] % 8 == 0] unaligned = df_series[df_series["size"] % 8 != 0] - ax.plot(df_series["size"], df_series[y_key], "-", color=color, - label=label, alpha=alpha) - ax.scatter(aligned["size"], aligned[y_key], marker="o", - color=color, zorder=5, alpha=alpha) - ax.scatter(unaligned["size"], unaligned[y_key], marker="x", - color=color, zorder=5, alpha=alpha) + ax.plot(df_series["size"], df_series[y_key], "-", color=color, label=label, alpha=0.5) + ax.scatter(aligned["size"], aligned[y_key], marker="o", color=color, zorder=5, alpha=0.5) + ax.scatter( + unaligned["size"], + unaligned[y_key], + marker="x", + color=color, + zorder=5, + alpha=0.5, + ) -def plot_scalar_vs_vector(files, out_dir): - out_dir = Path(out_dir) - out_dir.mkdir(parents=True, exist_ok=True) +def plot_time_and_speedup(ax_right, ax_speedup, s, v, caches): + """Plot wall time and speedup comparison between scalar and vectorized implementations. + + Args: + ax_right: Matplotlib axis for wall time plot. + ax_speedup: Secondary axis for speedup overlay. + s: DataFrame of scalar benchmark results. + v: DataFrame of vectorized benchmark results. + caches: Dictionary mapping cache level to size in bytes. + """ + _plot_series(ax_right, s, "C0", "scalar ns", "real_time_ns") + _plot_series(ax_right, v, "C1", "vectorized ns", "real_time_ns") + + merged = pd.merge( + s[["size", "real_time_ns"]], + v[["size", "real_time_ns"]], + on="size", + suffixes=("_s", "_v"), + ).dropna() + + merged["speedup"] = merged["real_time_ns_s"] / merged["real_time_ns_v"] + + ax_speedup.plot( + merged["size"], + merged["speedup"], + "-", + color="C2", + label="speedup (×)", + linewidth=1.5, + ) + ax_speedup.scatter( + merged["size"], + merged["speedup"], + marker="D", + color="C2", + zorder=5, + s=25, + ) + ax_speedup.axhline(1.0, linestyle=":", color="C2", alpha=0.5) - # collect benchmark names present in every file + _draw_cache_lines(ax_right, caches) + + +def plot_throughput(ax_left, ax_bytes, s, v, caches): + """Plot throughput comparison (cells/s and bytes/s) between scalar and vectorized. + + Args: + ax_left: Matplotlib axis for cells per second plot. + ax_bytes: Secondary axis for bytes per second overlay. + s: DataFrame of scalar benchmark results. + v: DataFrame of vectorized benchmark results. + caches: Dictionary mapping cache level to size in bytes. + """ + for df_series, color, label in [ + (s, "C0", "scalar"), + (v, "C1", "vectorized"), + ]: + _plot_series(ax_left, df_series, color, f"{label} cells/s", "cells_per_second") + _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", "bytes_per_second") + + _draw_cache_lines(ax_left, caches) + + +def plot_pair(s, v, caches, base_name, bm_label, out_dir): + """Create a two-panel figure comparing scalar vs vectorized performance metrics. + + Args: + s: DataFrame of scalar benchmark results. + v: DataFrame of vectorized benchmark results. + caches: Dictionary mapping cache level to size in bytes. + base_name: Base name of the benchmark (without "Vectorized" suffix). + bm_label: Label for the benchmark (used in filename and title). + out_dir: Output directory path for saving the figure. + """ + fig, (ax_left, ax_right) = plt.subplots(1, 2, figsize=(16, 5)) + + ax_bytes = ax_left.twinx() + ax_speedup = ax_right.twinx() + + fig.suptitle(f"{base_name} — {bm_label}", fontsize=12) + + plot_throughput(ax_left, ax_bytes, s, v, caches) + plot_time_and_speedup(ax_right, ax_speedup, s, v, caches) + + ax_left.set_xlabel("n (cube width in cells)") + ax_right.set_xlabel("n (cube width in cells)") + + ax_left.set_title("Throughput") + ax_right.set_title("Wall Time & Speedup") + + plt.tight_layout() + plt.savefig(out_dir / f"{bm_label}_{base_name}.png", dpi=200) + plt.close() + + +def get_scalar_vector(df, base_name): + """Extract scalar and vectorized benchmark data for a given base benchmark name. + + Args: + df: DataFrame containing benchmark results. + base_name: Base name of the benchmark (without "Vectorized" suffix). + + Returns: + A tuple of (scalar_df, vectorized_df) sorted by size. + """ + s = df[df["benchmark"] == base_name].sort_values("size") + v = df[df["benchmark"] == base_name + "Vectorized"].sort_values("size") + return s, v + + +def collect_all_benchmarks(files): + """Collect all unique benchmark names from a set of result files. + + Args: + files: Dictionary mapping environment names to file paths. + + Returns: + A set of unique benchmark names found across all files. + """ all_names = set() for path in files.values(): df, _ = load_one(path) all_names.update(df["benchmark"].unique()) + return all_names + +def process_base_name(files, out_dir, base_name): + """Process and plot scalar vs vectorized comparisons for a single benchmark. + + Args: + files: Dictionary mapping environment names to file paths. + out_dir: Output directory for saving plots. + base_name: Base name of the benchmark to process. + """ + for environment, path in files.items(): + df, caches = load_one(path) + bm_label = extract_label(path) + + s, v = get_scalar_vector(df, base_name) + if s.empty or v.empty: + print(f"skipping {base_name} for {environment}") + continue + + plot_pair(s, v, caches, base_name, bm_label, out_dir) + + +def plot_scalar_vs_vector(files, out_dir): + """Generate scalar vs vectorized comparison plots for all benchmarks. + + Args: + files: Dictionary mapping environment names to benchmark result file paths. + out_dir: Output directory for saving generated plots. + """ + out_dir = Path(out_dir) + out_dir.mkdir(parents=True, exist_ok=True) + + all_names = collect_all_benchmarks(files) base_names = [b for b in all_names if b + "Vectorized" in all_names] for base_name in base_names: - vec_name = base_name + "Vectorized" - - for environment, path in files.items(): - df, caches = load_one(path) - bm_label = extract_label(path) - - s = df[df["benchmark"] == base_name].sort_values("size") - v = df[df["benchmark"] == vec_name].sort_values("size") - - if s.empty or v.empty: - print(f"skipping {base_name} for {environment}") - continue - - fig, (ax_left, ax_right) = plt.subplots( - 1, 2, figsize=(16, 5), sharey=False - ) - fig.suptitle(f"{base_name} — {bm_label}", fontsize=12) - - # ── left plot: throughput ───────────────────────────────────── - ax_bytes = ax_left.twinx() - - for df_series, color, label in [ - (s, "C0", "scalar"), - (v, "C1", "vectorized"), - ]: - _plot_series(ax_left, df_series, color, f"{label} cells/s", - "cells_per_second") - _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", - "bytes_per_second", alpha=0.4) - - _draw_cache_lines(ax_left, caches) - - ax_left.set_xlabel("n (cube width in cells)") - ax_left.set_ylabel("cells / s") - ax_bytes.set_ylabel("bytes / s") - ax_left.set_title("Throughput") - ax_right.set_xscale("log") - ax_right.set_yscale("log") - ax_left.legend(fontsize=7) - ax_left.grid(True, alpha=0.3) - - # ── right plot: wall time + speedup ────────────────────────── - ax_speedup = ax_right.twinx() - - _plot_series(ax_right, s, "C0", "scalar ns", "real_time_ns") - _plot_series(ax_right, v, "C1", "vectorized ns", "real_time_ns") - - # speedup: scalar / vectorized on shared sizes - merged = pd.merge( - s[["size", "real_time_ns"]], - v[["size", "real_time_ns"]], - on="size", - suffixes=("_s", "_v"), - ).dropna() - merged["speedup"] = merged["real_time_ns_s"] / merged["real_time_ns_v"] - - ax_speedup.plot( - merged["size"], merged["speedup"], - "-", color="C2", label="speedup (×)", linewidth=1.5, - ) - ax_speedup.scatter( - merged["size"], merged["speedup"], - marker="D", color="C2", zorder=5, s=25, - ) - ax_speedup.axhline(1.0, linestyle=":", color="C2", alpha=0.5) - - _draw_cache_lines(ax_right, caches) - - ax_right.set_xlabel("n (cube width in cells)") - ax_right.set_ylabel("real time (ns)") - ax_speedup.set_ylabel("speedup (×)") - ax_right.set_title("Wall Time & Speedup") - - # merge legends from both right-plot axes - lines_r, labels_r = ax_right.get_legend_handles_labels() - lines_s, labels_s = ax_speedup.get_legend_handles_labels() - ax_right.legend(lines_r + lines_s, labels_r + labels_s, fontsize=7) - ax_right.grid(True, alpha=0.3) - - plt.tight_layout() - print("bm_label = " , bm_label) - save_name = out_dir / f"{bm_label}_{base_name}.png" - print("saving : ", save_name) - plt.savefig(save_name, dpi=200) - plt.close() - -def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=None): + process_base_name(files, out_dir, base_name) + + +def compare_benchmarks(path_a, path_b, out_csv, cols=None): + """Compare two benchmark results and generate a CSV with speedup metrics. + + Args: + path_a: Path to the first benchmark result JSON file. + path_b: Path to the second benchmark result JSON file. + out_csv: Path to the output CSV file. + cols: Optional list of columns to include in the output CSV. + + Returns: + DataFrame containing the merged and computed comparison results. + """ df_a, _ = load_one(path_a) df_b, _ = load_one(path_b) @@ -207,17 +345,15 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N df_a, df_b, on=["benchmark", "size"], - suffixes=(f"_{label_a}", f"_{label_b}"), + suffixes=("_a", "_b"), how="inner", ) - merged["real_time_speedup"] = ( - merged[f"real_time_ns_{label_a}"] / merged[f"real_time_ns_{label_b}"] - ) + merged["real_time_speedup"] = merged["real_time_ns_a"] / merged["real_time_ns_b"] for col in ("cells_per_second", "bytes_per_second"): - a_col = f"{col}_{label_a}" - b_col = f"{col}_{label_b}" + a_col = f"{col}_a" + b_col = f"{col}_b" if a_col in merged and b_col in merged: merged[f"{col}_speedup"] = merged[b_col] / merged[a_col] @@ -235,18 +371,23 @@ def compare_benchmarks(path_a, path_b, out_csv, label_a="a", label_b="b", cols=N rounding |= {c: 5 for c in merged.columns if "cells_per_second" in c or "bytes_per_second" in c} merged = merged.round(rounding) - - out_csv = Path(out_csv) out_csv.parent.mkdir(parents=True, exist_ok=True) merged.to_csv(out_csv, index=False) return merged + + # %% FILES = { -"skx_new": latest_result(), + "skx_new": latest_result("."), } plot_scalar_vs_vector(FILES, OUT_DIR) -compare_benchmarks(result_by_job_id(463476), FILES["skx_new"], "store.csv", "ch", "new", cols=["benchmark", "size", "real_time_speedup"]) - +COLS = ["benchmark", "size", "real_time_speedup"] +compare_benchmarks( + FILES["skx_new"], + FILES["skx_new"], + "store.csv", + cols=COLS, +) From d8e94c5640b001cc41b932b6695d09c3126f1b08 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 22 Apr 2026 10:23:47 +0200 Subject: [PATCH 31/56] compare helper for cons and prim arrays in tests --- euler_operators/euler_arrays.hpp | 2 +- simulations/plot.py | 9 +++++---- tests/test_cons_to_prim.cpp | 9 +++------ tests/test_prim_to_cons.cpp | 16 ++++++---------- tests/test_utils.hpp | 26 ++++++++++++++++++++++++++ 5 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 tests/test_utils.hpp diff --git a/euler_operators/euler_arrays.hpp b/euler_operators/euler_arrays.hpp index 15031b4..612af03 100644 --- a/euler_operators/euler_arrays.hpp +++ b/euler_operators/euler_arrays.hpp @@ -75,7 +75,7 @@ KOKKOS_FORCEINLINE_FUNCTION EulerCons to_cons(EulerPrim const prim, T cons T m0 = prim.d * prim.ux0; T m1 = prim.d * prim.ux1; T m2 = prim.d * prim.ux2; - constexpr T half = 0.5; + T const half = T(0.5); T e_kin = ((m0 * prim.ux0) + (m1 * prim.ux1) + (m2 * prim.ux2)) * half; return {.d = prim.d, .e = e_kin + int_e, .mx0 = m0, .mx1 = m1, .mx2 = m2}; diff --git a/simulations/plot.py b/simulations/plot.py index d391519..1de320b 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -122,6 +122,7 @@ def load_one(path): def _draw_cache_lines(ax, caches): """Draw vertical lines on a plot indicating cache level boundaries. + (Read directly from Google Benchmark => only relevant for cpu. Args: ax: Matplotlib axis object to draw on. @@ -226,17 +227,17 @@ def plot_throughput(ax_left, ax_bytes, s, v, caches): _draw_cache_lines(ax_left, caches) -def plot_pair(s, v, caches, base_name, bm_label, out_dir): +def plot_pair(benchmarks, caches, base_name, bm_label, out_dir): """Create a two-panel figure comparing scalar vs vectorized performance metrics. Args: - s: DataFrame of scalar benchmark results. - v: DataFrame of vectorized benchmark results. + benchmarks: Pair of DataFrames of scalar and vectorized benchmark results. caches: Dictionary mapping cache level to size in bytes. base_name: Base name of the benchmark (without "Vectorized" suffix). bm_label: Label for the benchmark (used in filename and title). out_dir: Output directory path for saving the figure. """ + s, v = benchmarks fig, (ax_left, ax_right) = plt.subplots(1, 2, figsize=(16, 5)) ax_bytes = ax_left.twinx() @@ -306,7 +307,7 @@ def process_base_name(files, out_dir, base_name): print(f"skipping {base_name} for {environment}") continue - plot_pair(s, v, caches, base_name, bm_label, out_dir) + plot_pair((s, v), caches, base_name, bm_label, out_dir) def plot_scalar_vs_vector(files, out_dir): diff --git a/tests/test_cons_to_prim.cpp b/tests/test_cons_to_prim.cpp index 184c0fa..3d96082 100644 --- a/tests/test_cons_to_prim.cpp +++ b/tests/test_cons_to_prim.cpp @@ -6,8 +6,9 @@ #include #include #include +#include -#include "utils.hpp" +#include "test_utils.hpp" TEST(ConsToPrimRemainder, ScalarVsVectorized) { @@ -67,11 +68,7 @@ TEST(ConsToPrimRemainder, ScalarVsVectorized) for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { int const idx = i + (n * (j + (n * k))); // layout_left flattening - ASSERT_NEAR(ref_h.d(idx), vec_h.d(idx), tol); - ASSERT_NEAR(ref_h.p(idx), vec_h.p(idx), tol); - ASSERT_NEAR(ref_h.ux0(idx), vec_h.ux0(idx), tol); - ASSERT_NEAR(ref_h.ux1(idx), vec_h.ux1(idx), tol); - ASSERT_NEAR(ref_h.ux2(idx), vec_h.ux2(idx), tol); + compare(ref_h, vec_h, tol, idx); } } } diff --git a/tests/test_prim_to_cons.cpp b/tests/test_prim_to_cons.cpp index 0254f22..4da7b2f 100644 --- a/tests/test_prim_to_cons.cpp +++ b/tests/test_prim_to_cons.cpp @@ -6,8 +6,9 @@ #include #include #include +#include -#include "utils.hpp" +#include "test_utils.hpp" TEST(PrimToConsRemainder, ScalarVsVectorized) { @@ -20,14 +21,14 @@ TEST(PrimToConsRemainder, ScalarVsVectorized) PerfectGas const eos(1.4); auto prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + = create_prim_arrays_1d(exec_space, static_cast(1UL * n * n * n)); // --- allocate base --- auto cons_alloc_ref - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + = create_cons_arrays_1d(exec_space, static_cast(1UL * n * n * n)); // --- allocate vectorized --- auto cons_alloc_vec - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + = create_cons_arrays_1d(exec_space, static_cast(1UL * n * n * n)); auto prim_arrays = to_mdspan + +#include + + +template +void compare(EulerPrimArrays const& ref, EulerPrimArrays const& vec, double tol, int idx) +{ + ASSERT_NEAR(ref.d(idx), vec.d(idx), tol); + ASSERT_NEAR(ref.p(idx), vec.p(idx), tol); + ASSERT_NEAR(ref.ux0(idx), vec.ux0(idx), tol); + ASSERT_NEAR(ref.ux1(idx), vec.ux1(idx), tol); + ASSERT_NEAR(ref.ux2(idx), vec.ux2(idx), tol); +} + +template +void compare(EulerConsArrays const& ref, EulerConsArrays const& vec, double tol, int idx) +{ + ASSERT_NEAR(ref.d(idx), vec.d(idx), tol); + ASSERT_NEAR(ref.e(idx), vec.e(idx), tol); + ASSERT_NEAR(ref.mx0(idx), vec.mx0(idx), tol); + ASSERT_NEAR(ref.mx1(idx), vec.mx1(idx), tol); + ASSERT_NEAR(ref.mx2(idx), vec.mx2(idx), tol); +} From 3c51ebe051c3c17361a7d4b9cf51938a277d2ac3 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 22 Apr 2026 11:30:52 +0200 Subject: [PATCH 32/56] (pylint):pip install pandas, (clang-tidy): widening cast fix --- .github/workflows/python-checks.yaml | 2 +- benchmarks/benchmark_cons_to_prim.cpp | 28 +++++++++++++++------------ benchmarks/benchmark_prim_to_cons.cpp | 26 +++++++++++++------------ tests/test_cons_to_prim.cpp | 13 +++++++------ tests/test_prim_to_cons.cpp | 14 ++++++++------ 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/.github/workflows/python-checks.yaml b/.github/workflows/python-checks.yaml index 4221fcc..b499fd7 100644 --- a/.github/workflows/python-checks.yaml +++ b/.github/workflows/python-checks.yaml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-python@v6 with: python-version: '3.13' - - run: pip install matplotlib numpy pylint~=4.0 + - run: pip install pandas matplotlib numpy pylint~=4.0 - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') diff --git a/benchmarks/benchmark_cons_to_prim.cpp b/benchmarks/benchmark_cons_to_prim.cpp index 46927f3..5053564 100644 --- a/benchmarks/benchmark_cons_to_prim.cpp +++ b/benchmarks/benchmark_cons_to_prim.cpp @@ -49,14 +49,16 @@ void ConsToPrimVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, @@ -80,14 +82,15 @@ void ConsToPrimWorstRem(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, @@ -110,14 +113,15 @@ void ConsToPrimWorstRemVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, diff --git a/benchmarks/benchmark_prim_to_cons.cpp b/benchmarks/benchmark_prim_to_cons.cpp index 49612b4..b184500 100644 --- a/benchmarks/benchmark_prim_to_cons.cpp +++ b/benchmarks/benchmark_prim_to_cons.cpp @@ -50,14 +50,14 @@ void PrimToConsVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, @@ -80,14 +80,15 @@ void PrimToConsWorstRem(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, @@ -111,14 +112,15 @@ void PrimToConsWorstRemVectorized(benchmark::State& state) auto const n = int_cast(state.range()); PerfectGas const eos(1.4); Kokkos::DefaultExecutionSpace const exec_space; - EulerPrimArrays const prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + EulerPrimArrays const prims_alloc = create_prim_arrays_1d(exec_space, n3); EulerPrimArrays const prim_arrays = to_mdspan, Kokkos::layout_left>>(prims_alloc, n, n, n); - EulerConsArrays const cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + EulerConsArrays const cons_alloc = create_cons_arrays_1d(exec_space, n3); EulerConsArrays const cons_arrays = to_mdspan, diff --git a/tests/test_cons_to_prim.cpp b/tests/test_cons_to_prim.cpp index 3d96082..df5fd1f 100644 --- a/tests/test_cons_to_prim.cpp +++ b/tests/test_cons_to_prim.cpp @@ -18,14 +18,15 @@ TEST(ConsToPrimRemainder, ScalarVsVectorized) Kokkos::DefaultExecutionSpace const exec_space; PerfectGas const eos(1.4); - auto cons_alloc - = create_cons_arrays_1d(exec_space, static_cast(n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + + auto cons_alloc = create_cons_arrays_1d(exec_space, n3); // --- allocate base --- - auto prims_alloc_ref - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto prims_alloc_ref = create_prim_arrays_1d(exec_space, n3); // --- allocate vectorized --- - auto prims_alloc_vec - = create_prim_arrays_1d(exec_space, static_cast(n * n * n)); + auto prims_alloc_vec = create_prim_arrays_1d(exec_space, n3); auto cons_arrays = to_mdspan const eos(1.4); - auto prims_alloc - = create_prim_arrays_1d(exec_space, static_cast(1UL * n * n * n)); + auto nn = static_cast(n); + std::size_t const n3 = nn * nn * nn; + + + + auto prims_alloc = create_prim_arrays_1d(exec_space, n3); // --- allocate base --- - auto cons_alloc_ref - = create_cons_arrays_1d(exec_space, static_cast(1UL * n * n * n)); + auto cons_alloc_ref = create_cons_arrays_1d(exec_space, n3); // --- allocate vectorized --- - auto cons_alloc_vec - = create_cons_arrays_1d(exec_space, static_cast(1UL * n * n * n)); + auto cons_alloc_vec = create_cons_arrays_1d(exec_space, n3); auto prim_arrays = to_mdspan Date: Mon, 27 Apr 2026 11:22:59 +0200 Subject: [PATCH 33/56] Udpate adastra setup scripts and plot.py --- setups/adastra/genoa/prepare.sh | 44 ++++++--- setups/adastra/genoa/run_bench.sh | 40 ++++++++ setups/adastra/mi300a/prepare.sh | 27 ++++-- setups/adastra/mi300a/run_bench.sh | 37 ++++++++ simulations/plot.py | 147 ++++++++++++++++++----------- 5 files changed, 221 insertions(+), 74 deletions(-) mode change 100644 => 100755 setups/adastra/genoa/prepare.sh create mode 100644 setups/adastra/genoa/run_bench.sh mode change 100644 => 100755 setups/adastra/mi300a/prepare.sh create mode 100755 setups/adastra/mi300a/run_bench.sh diff --git a/setups/adastra/genoa/prepare.sh b/setups/adastra/genoa/prepare.sh old mode 100644 new mode 100755 index 4bdc320..ecc2da1 --- a/setups/adastra/genoa/prepare.sh +++ b/setups/adastra/genoa/prepare.sh @@ -3,25 +3,29 @@ module purge module load \ - gcc-native/13.2 \ - cmake/3.27.9 + gcc-native/13.2 \ + cmake/3.27.9 export install_dir=$PWD/opt/genoa export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest -git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git -cmake \ - -D BENCHMARK_ENABLE_TESTING=OFF \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-benchmark \ - -S benchmark -cmake --build build-benchmark --parallel 8 -cmake --install build-benchmark --prefix "$benchmark_ROOT" -rm -rf build-benchmark benchmark +# git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git +# cmake \ +# -D BENCHMARK_ENABLE_TESTING=OFF \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-benchmark \ +# -S benchmark +# cmake --build build-benchmark --parallel 8 +# cmake --install build-benchmark --prefix "$benchmark_ROOT" +# rm -rf build-benchmark benchmark -git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git +git clone https://github.com/kokkos/kokkos.git +cd kokkos || exit +git checkout 7f8988b4d +cd .. || exit cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ @@ -31,9 +35,19 @@ cmake \ -D Kokkos_ENABLE_OPENMP=ON \ -B build-kokkos \ -S kokkos -cmake --build build-kokkos --parallel 8 +cmake --build build-kokkos --parallel cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos -cmake -D CMAKE_BUILD_TYPE=Release -B build-genoa +# git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +# cmake \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-gtest \ +# -S googletest +# cmake --build build-gtest +# cmake --install build-gtest --prefix "$gtest_ROOT" +# rm -rf build-gtest googletest + +cmake -DGTest_ROOT="$gtest_ROOT" -D CMAKE_BUILD_TYPE=Release -B build-genoa cmake --build build-genoa --parallel 8 diff --git a/setups/adastra/genoa/run_bench.sh b/setups/adastra/genoa/run_bench.sh new file mode 100644 index 0000000..2414505 --- /dev/null +++ b/setups/adastra/genoa/run_bench.sh @@ -0,0 +1,40 @@ +#!/bin/bash +#SBATCH --account=cad16293 +#SBATCH --job-name=bench_genoa +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --nodes=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:20:00 +#SBATCH --exclusive +#SBATCH --hint=nomultithread +#SBATCH --constraint=GENOA +#SBATCH --threads-per-core=1 + +module purge + +module load cpe/24.07 +module load craype-x86-genoa +module load PrgEnv-cray + +set -x +cd "${SLURM_SUBMIT_DIR}" || exit + +mkdir -p slurm_out results/adastra/genoa/mt/ +BENCHMARK_FILTER=${1:-""} + +export OMP_NUM_THREADS=1 +# export OMP_PROC_BIND=CLOSE +# export OMP_PLACES=THREADS + +# export OMP_DISPLAY_AFFINITY=TRUE +# export OMP_AFFINITY_FORMAT="thread %0.3n -> cpu %A" +# numactl -H + +# srun bash -c 'echo $SLURM_CPUS_PER_TASK; grep Cpus_allowed_list /proc/self/status' + +./build-genoa/benchmarks/euler_benchmarks \ + --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out_format=json \ + --benchmark_out=./results/adastra/genoa/bm_json/"[${SLURM_JOB_ID}]_BASE_${BENCHMARK_FILTER}.json" +# --benchmark_out=./results/adastra/genoa/bm_json/mt/"[${SLURM_JOB_ID}]_T${OMP_NUM_THREADS}-${OMP_PROC_BIND}-${OMP_PLACES}_${BENCHMARK_FILTER}.json" diff --git a/setups/adastra/mi300a/prepare.sh b/setups/adastra/mi300a/prepare.sh old mode 100644 new mode 100755 index cfb1118..73f2e08 --- a/setups/adastra/mi300a/prepare.sh +++ b/setups/adastra/mi300a/prepare.sh @@ -3,9 +3,9 @@ module purge module load \ - gcc-native/13.2 \ - cmake/3.27.9 \ - rocm/6.3.3 + gcc-native/13.2 \ + cmake/3.27.9 \ + rocm/6.3.3 export CC=hipcc export CXX=hipcc @@ -13,6 +13,7 @@ export CXX=hipcc export install_dir=$PWD/opt/mi300a export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git cmake \ @@ -25,7 +26,11 @@ cmake --build build-benchmark --parallel 8 cmake --install build-benchmark --prefix "$benchmark_ROOT" rm -rf build-benchmark benchmark -git clone --branch fix-simd-from-4.7.1 --depth 1 https://github.com/tpadioleau/kokkos.git +git clone https://github.com/kokkos/kokkos.git +cd kokkos || exit +git checkout 7f8988b4d +cd .. || exit + cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ @@ -41,5 +46,15 @@ cmake --build build-kokkos --parallel 8 cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos -cmake -D CMAKE_BUILD_TYPE=Release -B build-mi300a -cmake --build build-mi300a --parallel 8 +git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest +cmake --install build-gtest --prefix "$gtest_ROOT" +rm -rf build-gtest googletest + +cmake -DGTest_ROOT="$gtest_ROOT" -D CMAKE_BUILD_TYPE=Release -B build-mi300 +cmake --build build-mi300 --parallel 8 diff --git a/setups/adastra/mi300a/run_bench.sh b/setups/adastra/mi300a/run_bench.sh new file mode 100755 index 0000000..b65bc70 --- /dev/null +++ b/setups/adastra/mi300a/run_bench.sh @@ -0,0 +1,37 @@ +#!/bin/bash +#SBATCH --account=cad16293 +#SBATCH --job-name=bench_mi300 +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --nodes=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:10:00 +#SBATCH --exclusive +#SBATCH --hint=nomultithread +#SBATCH --constraint=MI300 +#SBATCH --threads-per-core=1 + +module purge + +# A CrayPE environment version +module load cpe/24.07 +# An architecture +module load craype-accel-amd-gfx942 craype-x86-mi300 +# A compiler to target the architecture +module load PrgEnv-cray +# Some architecture related libraries and tools +module load amd-mixed + +set -x +cd "${SLURM_SUBMIT_DIR}" || exit + +mkdir -p slurm_out results/adastra/mi300/bm_json/ +BENCHMARK_FILTER=${1:-""} + +export OMP_NUM_THREADS=1 + +./build-mi300/benchmarks/euler_benchmarks \ + --benchmark_dry_run \ + --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out_format=json \ + --benchmark_out=./results/adastra/mi300/bm_json/"[${SLURM_JOB_ID}]_${BENCHMARK_FILTER}.json" diff --git a/simulations/plot.py b/simulations/plot.py index 6379d8e..71e0f20 100644 --- a/simulations/plot.py +++ b/simulations/plot.py @@ -12,7 +12,7 @@ from pathlib import Path import matplotlib.pyplot as plt -import pandas as pd +# import pandas as pd KERNEL_BENCHMARKS = [ "Godunov", @@ -25,8 +25,11 @@ ALL_BENCHMARKS.append("EulerSimulation") -OUT_DIR = "results/plots" -RES_DIR = "results/ruche/skx/" +OUT_DIR = "results/adastra/genoa/plots" +RES_DIR = "results/adastra/genoa/bm_json/" +# OUT_DIR = "results/adastra/mi300/plots" +# RES_DIR = "results/adastra/mi300/bm_json/" + def latest_result(res_dir=RES_DIR, pattern="*.json"): @@ -50,7 +53,7 @@ def latest_result(res_dir=RES_DIR, pattern="*.json"): # sort by modification time (newest first) files = sorted(files, key=os.path.getmtime, reverse=True) - return files[-1] + return files[0] def result_by_job_id(job_id, res_dir=RES_DIR): @@ -115,10 +118,11 @@ def load_one(path): rows.append( { "benchmark": name.split("/")[0], - "size": int(name.split("/")[-1]), + "size": int(name.split("/")[-2]), "cells_per_second": b.get("cells_per_second"), "bytes_per_second": b.get("bytes_per_second"), "real_time_ns": b.get("real_time"), + "cpu_time_ns": b.get("cpu_time"), } ) return pd.DataFrame(rows), caches @@ -176,19 +180,65 @@ def _plot_series(ax, df_series, color, label, y_key): ) -def plot_time_and_speedup(ax_right, ax_speedup, s, v, caches): - """Plot wall time and speedup comparison between scalar and vectorized implementations. + +def plot_throughput(ax_left, ax_bytes, bm_dfs, caches): + """Plot throughput comparison (cells/s and bytes/s) between scalar and vectorized. Args: - ax_right: Matplotlib axis for wall time plot. - ax_speedup: Secondary axis for speedup overlay. - s: DataFrame of scalar benchmark results. - v: DataFrame of vectorized benchmark results. + ax_left: Matplotlib axis for cells per second plot. + ax_bytes: Secondary axis for bytes per second overlay. + bm_dfs: Tuple of (scalar_df, vectorized_df). caches: Dictionary mapping cache level to size in bytes. """ - _plot_series(ax_right, s, "C0", "scalar ns", "real_time_ns") - _plot_series(ax_right, v, "C1", "vectorized ns", "real_time_ns") + s, v = bm_dfs + + for df_series, color, label in [ + (s, "C0", "scalar"), + (v, "C1", "vectorized"), + ]: + _plot_series(ax_left, df_series, color, f"{label} cells/s", "cells_per_second") + _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", "bytes_per_second") + _draw_cache_lines(ax_left, caches) + + # --- Axis labels --- + ax_left.set_ylabel("Cells per second") + ax_bytes.set_ylabel("Bytes per second") + + # --- Combined legend (both axes) --- + handles_left, labels_left = ax_left.get_legend_handles_labels() + + ax_left.legend( + handles_left, + labels_left, + loc="best", + fontsize=9, + ) + + +def plot_time_and_speedup(ax_right, ax_speedup, bm_dfs, caches, plot_cpu_time=True): + """Plot wall time and (optionally) CPU time + speedup comparison.""" + + s, v = bm_dfs + + # --- Wall time (solid lines) --- + _plot_series(ax_right, s, "C0", "scalar (wall ns)", "real_time_ns") + _plot_series(ax_right, v, "C1", "vectorized (wall ns)", "real_time_ns") + + # --- CPU time (dashed lines, optional) --- + if plot_cpu_time: + ax_right.plot( + s["size"], s["cpu_time_ns"], + linestyle="--", color="C0", + label="scalar (cpu ns)", linewidth=1.2 + ) + ax_right.plot( + v["size"], v["cpu_time_ns"], + linestyle="--", color="C1", + label="vectorized (cpu ns)", linewidth=1.2 + ) + + # --- Speedup --- merged = pd.merge( s[["size", "real_time_ns"]], v[["size", "real_time_ns"]], @@ -216,29 +266,23 @@ def plot_time_and_speedup(ax_right, ax_speedup, s, v, caches): ) ax_speedup.axhline(1.0, linestyle=":", color="C2", alpha=0.5) + # --- Cache markers --- _draw_cache_lines(ax_right, caches) + # --- Axis labels --- + ax_right.set_ylabel("Time (ns)") + ax_speedup.set_ylabel("Speedup (×)") -def plot_throughput(ax_left, ax_bytes, s, v, caches): - """Plot throughput comparison (cells/s and bytes/s) between scalar and vectorized. - - Args: - ax_left: Matplotlib axis for cells per second plot. - ax_bytes: Secondary axis for bytes per second overlay. - s: DataFrame of scalar benchmark results. - v: DataFrame of vectorized benchmark results. - caches: Dictionary mapping cache level to size in bytes. - """ - for df_series, color, label in [ - (s, "C0", "scalar"), - (v, "C1", "vectorized"), - ]: - _plot_series(ax_left, df_series, color, f"{label} cells/s", "cells_per_second") - _plot_series(ax_bytes, df_series, color, f"{label} bytes/s", "bytes_per_second") - - _draw_cache_lines(ax_left, caches) - + # --- Combined legend (both axes) --- + handles_r, labels_r = ax_right.get_legend_handles_labels() + handles_s, labels_s = ax_speedup.get_legend_handles_labels() + ax_right.legend( + handles_r + handles_s, + labels_r + labels_s, + loc="best", + fontsize=9, + ) def plot_pair(benchmarks, caches, base_name, bm_label, out_dir): """Create a two-panel figure comparing scalar vs vectorized performance metrics. @@ -257,8 +301,9 @@ def plot_pair(benchmarks, caches, base_name, bm_label, out_dir): fig.suptitle(f"{base_name} — {bm_label}", fontsize=12) - plot_throughput(ax_left, ax_bytes, s, v, caches) - plot_time_and_speedup(ax_right, ax_speedup, s, v, caches) + bm_dfs = (s,v) + plot_throughput(ax_left, ax_bytes, bm_dfs, caches) + plot_time_and_speedup(ax_right, ax_speedup, bm_dfs, caches) ax_left.set_xlabel("n (cube width in cells)") ax_right.set_xlabel("n (cube width in cells)") @@ -322,21 +367,21 @@ def process_base_name(files, out_dir, base_name): plot_pair((s, v), caches, base_name, bm_label, out_dir) -def plot_scalar_vs_vector(files, out_dir): - """Generate scalar vs vectorized comparison plots for all benchmarks. + def plot_scalar_vs_vector(files, out_dir): + """Generate scalar vs vectorized comparison plots for all benchmarks. - Args: - files: Dictionary mapping environment names to benchmark result file paths. - out_dir: Output directory for saving generated plots. - """ - out_dir = Path(out_dir) - out_dir.mkdir(parents=True, exist_ok=True) + Args: + files: Dictionary mapping environment names to benchmark result file paths. + out_dir: Output directory for saving generated plots. + """ + out_dir = Path(out_dir) + out_dir.mkdir(parents=True, exist_ok=True) - all_names = collect_all_benchmarks(files) - base_names = [b for b in all_names if b + "Vectorized" in all_names] + all_names = collect_all_benchmarks(files) + base_names = [b for b in all_names if b + "Vectorized" in all_names] - for base_name in base_names: - process_base_name(files, out_dir, base_name) + for base_name in base_names: + process_base_name(files, out_dir, base_name) def compare_benchmarks(path_a, path_b, out_csv, cols=None): @@ -396,12 +441,8 @@ def compare_benchmarks(path_a, path_b, out_csv, cols=None): COLS = ["benchmark", "size", "real_time_ns_speedup", "cpu_time_ns_speedup"] FILES = { - "skx_new": latest_result("."), + "genoa": latest_result(RES_DIR) + # "mi300a": latest_result(RES_DIR) } plot_scalar_vs_vector(FILES, OUT_DIR) -compare_benchmarks( - FILES["skx_new"], - FILES["skx_new"], - "store.csv", - cols=COLS, -) + From 66d5ce267d3a7095f470a59a620816e7f831b67b Mon Sep 17 00:00:00 2001 From: Timothy Date: Mon, 27 Apr 2026 14:23:11 +0200 Subject: [PATCH 34/56] MI300 4871378 benchmark with current run_bench.sh --- benchmarks/benchmark_godunov.cpp | 15 +++++++++-- setups/adastra/mi300a/prepare.sh | 40 +++++++++++++++--------------- setups/adastra/mi300a/run_bench.sh | 18 ++++++-------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index 4e3de9d..6c9c316 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -161,7 +161,18 @@ void GodunovVectorizedWorstRem(benchmark::State& state) } } // namespace -BENCHMARK(Godunov)->UseRealTime()->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); -BENCHMARK(GodunovVectorized)->UseRealTime()->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(Godunov) + ->UseRealTime() + ->DenseRange(8, 31, 8) + ->DenseRange(32, 127, 16) // MI300 specific + ->DenseRange(128, 320, 32); + + +BENCHMARK(GodunovVectorized) + ->UseRealTime() + ->DenseRange(8, 31, 8) + ->DenseRange(32, 127, 16) // MI300 specific + ->DenseRange(128, 320, 32); + BENCHMARK(GodunovWorstRem)->UseRealTime()->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); BENCHMARK(GodunovVectorizedWorstRem)->UseRealTime()->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); diff --git a/setups/adastra/mi300a/prepare.sh b/setups/adastra/mi300a/prepare.sh index 73f2e08..1a54df5 100755 --- a/setups/adastra/mi300a/prepare.sh +++ b/setups/adastra/mi300a/prepare.sh @@ -15,16 +15,16 @@ export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark export gtest_ROOT=$install_dir/gtest -git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git -cmake \ - -D BENCHMARK_ENABLE_TESTING=OFF \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-benchmark \ - -S benchmark -cmake --build build-benchmark --parallel 8 -cmake --install build-benchmark --prefix "$benchmark_ROOT" -rm -rf build-benchmark benchmark +# git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git +# cmake \ +# -D BENCHMARK_ENABLE_TESTING=OFF \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-benchmark \ +# -S benchmark +# cmake --build build-benchmark --parallel 8 +# cmake --install build-benchmark --prefix "$benchmark_ROOT" +# rm -rf build-benchmark benchmark git clone https://github.com/kokkos/kokkos.git cd kokkos || exit @@ -35,7 +35,7 @@ cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ -D Kokkos_ARCH_ZEN4=ON \ - -D Kokkos_ARCH_AMD_GFX942_APU=ON \ + -D Kokkos_ARCH_AMD_GFX942=ON \ -D Kokkos_ENABLE_DEPRECATED_CODE_4=OFF \ -D Kokkos_ENABLE_DEPRECATION_WARNINGS=OFF \ -D Kokkos_ENABLE_HIP=ON \ @@ -46,15 +46,15 @@ cmake --build build-kokkos --parallel 8 cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos -git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git -cmake \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-gtest \ - -S googletest -cmake --build build-gtest -cmake --install build-gtest --prefix "$gtest_ROOT" -rm -rf build-gtest googletest +# git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +# cmake \ +# -D CMAKE_BUILD_TYPE=Release \ +# -D CMAKE_CXX_STANDARD=20 \ +# -B build-gtest \ +# -S googletest +# cmake --build build-gtest +# cmake --install build-gtest --prefix "$gtest_ROOT" +# rm -rf build-gtest googletest cmake -DGTest_ROOT="$gtest_ROOT" -D CMAKE_BUILD_TYPE=Release -B build-mi300 cmake --build build-mi300 --parallel 8 diff --git a/setups/adastra/mi300a/run_bench.sh b/setups/adastra/mi300a/run_bench.sh index b65bc70..a1cb25b 100755 --- a/setups/adastra/mi300a/run_bench.sh +++ b/setups/adastra/mi300a/run_bench.sh @@ -12,15 +12,9 @@ #SBATCH --threads-per-core=1 module purge - -# A CrayPE environment version module load cpe/24.07 -# An architecture -module load craype-accel-amd-gfx942 craype-x86-mi300 -# A compiler to target the architecture -module load PrgEnv-cray -# Some architecture related libraries and tools -module load amd-mixed +module load PrgEnv-amd +module load craype-accel-amd-gfx942 set -x cd "${SLURM_SUBMIT_DIR}" || exit @@ -29,9 +23,13 @@ mkdir -p slurm_out results/adastra/mi300/bm_json/ BENCHMARK_FILTER=${1:-""} export OMP_NUM_THREADS=1 +export HSA_XNACK=1 +export CXX=hipcc +SAFE_FILTER=$(echo "$BENCHMARK_FILTER" | sed 's/[()|^\/]/_/g') ./build-mi300/benchmarks/euler_benchmarks \ - --benchmark_dry_run \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/adastra/mi300/bm_json/"[${SLURM_JOB_ID}]_${BENCHMARK_FILTER}.json" + --benchmark_out=./results/adastra/mi300/bm_json/"[${SLURM_JOB_ID}]_${SAVE_FILTER}.json" + +# --benchmark_dry_run \ From d4faebd1654a7ad77f6988c80ea1971074ed4f7b Mon Sep 17 00:00:00 2001 From: Timothy Date: Mon, 27 Apr 2026 16:15:43 +0200 Subject: [PATCH 35/56] Add run_test.sh for mi300 --- setups/adastra/mi300a/run_test.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100755 setups/adastra/mi300a/run_test.sh diff --git a/setups/adastra/mi300a/run_test.sh b/setups/adastra/mi300a/run_test.sh new file mode 100755 index 0000000..a43efda --- /dev/null +++ b/setups/adastra/mi300a/run_test.sh @@ -0,0 +1,28 @@ +#!/bin/bash +#SBATCH --account=cad16293 +#SBATCH --job-name=test_mi300 +#SBATCH --output=./slurm_out/%x.o%j +#SBATCH --ntasks=1 +#SBATCH --nodes=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=00:10:00 +#SBATCH --exclusive +#SBATCH --hint=nomultithread +#SBATCH --constraint=MI300 +#SBATCH --threads-per-core=1 + +module purge +module load cpe/24.07 +module load PrgEnv-amd +module load craype-accel-amd-gfx942 + +set -x +cd "${SLURM_SUBMIT_DIR}" || exit + +mkdir -p slurm_out + +export OMP_NUM_THREADS=1 +export HSA_XNACK=1 +export CXX=hipcc + +./build-mi300/tests/euler_tests From 32edc47e76b101c6d61a6f3d900e3b7b26b641f1 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 29 Apr 2026 15:00:36 +0200 Subject: [PATCH 36/56] Optimized hllc and godunov implementations --- euler_operators/godunov.hpp | 95 +++++++++++++++++++++++++++++++++++++ euler_operators/hllc.hpp | 84 ++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 9033aac..5676f02 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -249,3 +249,98 @@ void godunov_vec( dt); } } + +template +void godunov_opti( + Kokkos::DefaultExecutionSpace const& exec_space, + EulerPrimArrays, + Kokkos::layout_left>> const& prim_arrays, + EulerConsArrays, + Kokkos::layout_left>> const& cons_arrays, + PerfectGas const& eos, + UniformMesh3d const& mesh, + hllc_fast const& riemann_solver, + T const dt) +{ + T const ds0 = mesh.ds0(); + T const ds1 = mesh.ds1(); + T const ds2 = mesh.ds2(); + + T const dtodv = dt / mesh.dv(); + + Kokkos::parallel_for( + "godunov", + Kokkos::MDRangePolicy< + Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>, + Kokkos::IndexType>( + exec_space, + {1, 1, 1}, + {prim_arrays.d.extent(0) - 1, + prim_arrays.d.extent(1) - 1, + prim_arrays.d.extent(2) - 1}), + KOKKOS_LAMBDA(IndexType const i, IndexType const j, IndexType const k) { + EulerPrim const c = load(prim_arrays, i, j, k); + + T fd = T(0); + T fe = T(0); + T fx = T(0); + T fy = T(0); + T fz = T(0); + + { + auto const L = load(prim_arrays, i - 1, j, k); + auto const R = load(prim_arrays, i + 1, j, k); + + auto const FL = riemann_solver(dir_t<0> {}, eos, L, c); + auto const FR = riemann_solver(dir_t<0> {}, eos, c, R); + + fd += ds0 * (FR.d - FL.d); + fe += ds0 * (FR.e - FL.e); + fx += ds0 * (FR.mx0 - FL.mx0); + fy += ds0 * (FR.mx1 - FL.mx1); + fz += ds0 * (FR.mx2 - FL.mx2); + } + + { + auto const L = load(prim_arrays, i, j - 1, k); + auto const R = load(prim_arrays, i, j + 1, k); + + auto const FL = riemann_solver(dir_t<1> {}, eos, L, c); + auto const FR = riemann_solver(dir_t<1> {}, eos, c, R); + + fd += ds1 * (FR.d - FL.d); + fe += ds1 * (FR.e - FL.e); + fx += ds1 * (FR.mx0 - FL.mx0); + fy += ds1 * (FR.mx1 - FL.mx1); + fz += ds1 * (FR.mx2 - FL.mx2); + } + + { + auto const L = load(prim_arrays, i, j, k - 1); + auto const R = load(prim_arrays, i, j, k + 1); + + auto const FL = riemann_solver(dir_t<2> {}, eos, L, c); + auto const FR = riemann_solver(dir_t<2> {}, eos, c, R); + + fd += ds2 * (FR.d - FL.d); + fe += ds2 * (FR.e - FL.e); + fx += ds2 * (FR.mx0 - FL.mx0); + fy += ds2 * (FR.mx1 - FL.mx1); + fz += ds2 * (FR.mx2 - FL.mx2); + } + + EulerCons u = load(cons_arrays, i, j, k); + + u.d -= dtodv * fd; + u.e -= dtodv * fe; + u.mx0 -= dtodv * fx; + u.mx1 -= dtodv * fy; + u.mx2 -= dtodv * fz; + + store(u, cons_arrays, i, j, k); + }); +} diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index dbfda3f..a99abcf 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -143,3 +143,87 @@ struct hllc return flux; } }; +struct hllc_opti +{ + template + KOKKOS_FORCEINLINE_FUNCTION EulerFlux operator()( + std::integral_constant dir, + PerfectGas const& eos, + EulerPrim const& q_L, + EulerPrim const& q_R) const noexcept + { + using detail::select; + + static_assert(Dir < 3); + + T const un_L = get(dir, q_L); + T const un_R = get(dir, q_R); + + + T const c_L = eos.speed_of_sound(q_L.d, q_L.p); + T const c_R = eos.speed_of_sound(q_R.d, q_R.p); + + T const cmax = Kokkos::max(c_L, c_R); + + T const S_L = Kokkos::min(un_L, un_R) - cmax; + T const S_R = Kokkos::max(un_L, un_R) + cmax; + + T const rcL = q_L.d * (S_L - un_L); + T const rcR = q_R.d * (S_R - un_R); + + T const inv_rc = T(1) / (rcL - rcR); + + T const ustar = (q_R.p - q_L.p + rcL * un_L - rcR * un_R) * inv_rc; + + T const pstar = T(0.5) * (q_L.p + q_R.p + rcL * (ustar - un_L) + rcR * (ustar - un_R)); + + auto const useL = ustar > T(0); + auto const same = (S_L * S_R) > T(0); + + T const S = select(useL, S_L, S_R); + + T const d = select(useL, q_L.d, q_R.d); + T const p = select(useL, q_L.p, q_R.p); + T const ux0 = select(useL, q_L.ux0, q_R.ux0); + T const ux1 = select(useL, q_L.ux1, q_R.ux1); + T const ux2 = select(useL, q_L.ux2, q_R.ux2); + + T const un = select(useL, un_L, un_R); + + T const eint = eos.internal_energy(d, p); + T const ke = T(0.5) * d * (ux0 * ux0 + ux1 * ux1 + ux2 * ux2); + T const etot = eint + ke; + + T const uno = select(same, un, ustar); + T const po = select(same, p, pstar); + + T const inv1 = T(1) / (S - uno); + T const fac = (S - un) * inv1; + + T const dout = fac * d; + + T const inv2 = T(1) / (S - ustar); + + T const eout = (fac * etot) + (((po * uno) - (p * un)) * inv2); + + T const mom = dout * uno; + + EulerFlux f {}; + + f.d = mom; + f.e = (eout + po) * uno; + f.mx0 = mom * ux0; + f.mx1 = mom * ux1; + f.mx2 = mom * ux2; + + if constexpr (Dir == 0) { + f.mx0 = (mom * uno) + po; + } else if constexpr (Dir == 1) { + f.mx1 = (mom * uno) + po; + } else { + f.mx2 = (mom * uno) + po; + } + + return f; + } +}; From 5237f24d1e475c613fefd2391e8118c39ca47e3e Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 29 Apr 2026 15:50:07 +0200 Subject: [PATCH 37/56] Updated Godunov tests for opti --- euler_operators/godunov.hpp | 2 +- tests/test_godunov.cpp | 96 ++++++++++++++++++++++++------------- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 5676f02..1ef18eb 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -263,7 +263,7 @@ void godunov_opti( Kokkos::layout_left>> const& cons_arrays, PerfectGas const& eos, UniformMesh3d const& mesh, - hllc_fast const& riemann_solver, + hllc_opti const& riemann_solver, T const dt) { T const ds0 = mesh.ds0(); diff --git a/tests/test_godunov.cpp b/tests/test_godunov.cpp index b77188c..272cb9b 100644 --- a/tests/test_godunov.cpp +++ b/tests/test_godunov.cpp @@ -33,7 +33,7 @@ constexpr EulerPrim shock_state {.d = 4.0, .p = 10.0, .ux0 = 1.5, .ux1 = constexpr double dt_default = 1e-9; constexpr double dt_small = 1e-10; -template +template auto run( Kokkos::DefaultExecutionSpace const& exec, int n, @@ -41,7 +41,8 @@ auto run( PerfectGas const& eos, UniformMesh3d const& mesh, double dt, - Kernel kernel) + Kernel kernel, + Solver solver) { auto const nn = static_cast(n); std::size_t const n3 = nn * nn * nn; @@ -63,10 +64,10 @@ auto run( init_from_state(exec, U, to_cons(prim, eos.internal_energy(prim.d, prim.p))); exec.fence(); - kernel(exec, as_const(P), U, eos, mesh, hllc {}, dt); + kernel(exec, as_const(P), U, eos, mesh, solver, dt); exec.fence(); - return std::pair {prims_alloc, cons_alloc}; + return cons_alloc; } void run_case(int n, double dt, EulerPrim const& prim) @@ -75,35 +76,53 @@ void run_case(int n, double dt, EulerPrim const& prim) PerfectGas const eos(1.4); UniformMesh3d const mesh(1., 1., 1.); - auto [prims_ref, cons_ref] - = run(exec, - n, - prim, - eos, - mesh, - dt, - [](auto const& exec, - auto const& P, - auto& U, - auto const& eos, - auto const& mesh, - auto solver, - double dt) { godunov(exec, P, U, eos, mesh, solver, dt); }); - - auto [prims_vec, cons_vec] - = run(exec, - n, - prim, - eos, - mesh, - dt, - [](auto const& exec, - auto const& P, - auto& U, - auto const& eos, - auto const& mesh, - auto solver, - double dt) { godunov_vec(exec, P, U, eos, mesh, solver, dt); }); + auto cons_ref = run( + exec, + n, + prim, + eos, + mesh, + dt, + [](auto const& exec, + auto const& P, + auto& U, + auto const& eos, + auto const& mesh, + auto solver, + double dt) { godunov(exec, P, U, eos, mesh, solver, dt); }, + hllc {}); + + auto cons_vec = run( + exec, + n, + prim, + eos, + mesh, + dt, + [](auto const& exec, + auto const& P, + auto& U, + auto const& eos, + auto const& mesh, + auto solver, + double dt) { godunov_vec(exec, P, U, eos, mesh, solver, dt); }, + hllc {}); + + auto cons_opti = run( + exec, + n, + prim, + eos, + mesh, + dt, + [](auto const& exec, + auto const& P, + auto& U, + auto const& eos, + auto const& mesh, + auto solver, + double dt) { godunov_opti(exec, P, U, eos, mesh, solver, dt); }, + hllc_opti {}); auto ref_h = EulerConsArrays { .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_ref.d), @@ -119,13 +138,22 @@ void run_case(int n, double dt, EulerPrim const& prim) .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_vec.mx1), .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_vec.mx2)}; + auto opti_h = EulerConsArrays { + .d = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_opti.d), + .e = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_opti.e), + .mx0 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_opti.mx0), + .mx1 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_opti.mx1), + .mx2 = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), cons_opti.mx2)}; + double const tol = 1e-12; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { int const idx = i + (n * (j + (n * k))); + compare(ref_h, vec_h, tol, idx); + compare(ref_h, opti_h, tol, idx); } } } @@ -133,7 +161,7 @@ void run_case(int n, double dt, EulerPrim const& prim) } // namespace -TEST_P(GodunovTest, ScalarVsVectorized) +TEST_P(GodunovTest, ScalarVectorizedOptimizedAgree) { auto const& c = GetParam(); run_case(c.n, c.dt, c.prim); From 63e0c8379074e0a7ae93e8731274c9d41e2ad574 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 30 Apr 2026 11:33:35 +0200 Subject: [PATCH 38/56] Formatting --- benchmarks/benchmark_euler_simulation.cpp | 5 +++-- benchmarks/benchmark_godunov.cpp | 5 +++-- euler_operators/godunov.hpp | 3 +-- euler_operators/hllc.hpp | 3 +-- euler_operators/perfect_gas.hpp | 1 - euler_operators/time_step.hpp | 4 ++++ setups/adastra/mi300a/run_bench.sh | 2 +- setups/prepare-laptop.sh | 2 +- tests/test_time_step.cpp | 1 - 9 files changed, 14 insertions(+), 12 deletions(-) diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index 30dc33f..45808bb 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -117,7 +117,8 @@ void EulerSimulationVectorized(benchmark::State& state) } set_constant_cells_processed(state, size(cons_arrays)); } + } // namespace -BENCHMARK(EulerSimulation)->UseRealTime()->UseRealTime()->DenseRange(16, 320, 32); -BENCHMARK(EulerSimulationVectorized)->UseRealTime()->DenseRange(16, 320, 32); +BENCHMARK(EulerSimulation)->UseRealTime()->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); +BENCHMARK(EulerSimulationVectorized)->UseRealTime()->DenseRange(8, 31, 8)->DenseRange(32, 320, 32); diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index 07e6976..05c1a2e 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -158,18 +158,19 @@ void GodunovVectorizedWorstRem(benchmark::State& state) set_constant_cells_processed(state, size(cons_arrays)); set_constant_bytes_processed(state, size_bytes(prim_arrays) + (2 * size_bytes(cons_arrays))); } + } // namespace BENCHMARK(Godunov) ->UseRealTime() ->DenseRange(8, 31, 8) - ->DenseRange(32, 127, 16) // MI300 specific + ->DenseRange(32, 127, 16) ->DenseRange(128, 320, 32); BENCHMARK(GodunovVectorized) ->UseRealTime() ->DenseRange(8, 31, 8) - ->DenseRange(32, 127, 16) // MI300 specific + ->DenseRange(32, 127, 16) ->DenseRange(128, 320, 32); BENCHMARK(GodunovWorstRem)->UseRealTime()->DenseRange(7, 31, 8)->DenseRange(31, 320, 32); diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 1ef18eb..66873db 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -35,7 +35,6 @@ void godunov( Kokkos::Array const ds = {mesh.ds0(), mesh.ds1(), mesh.ds2()}; T const dtodv = dt / mesh.dv(); - Kokkos::parallel_for( "godunov", Kokkos::MDRangePolicy< @@ -95,6 +94,7 @@ void godunov( store(cons, cons_arrays, i, j, k); }); } + template void godunov_kernel( Kokkos::DefaultExecutionSpace const& exec_space, @@ -197,7 +197,6 @@ void godunov_kernel( }); } - template void godunov_vec( Kokkos::DefaultExecutionSpace const& exec_space, diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 70a77d9..1ee86ac 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -66,7 +66,6 @@ KOKKOS_FUNCTION T select(Mask const& mask, T const& a, T const& b) } // namespace detail - struct hllc { template @@ -143,6 +142,7 @@ struct hllc return flux; } }; + struct hllc_opti { template @@ -159,7 +159,6 @@ struct hllc_opti T const un_L = get(dir, q_L); T const un_R = get(dir, q_R); - T const c_L = eos.speed_of_sound(q_L.d, q_L.p); T const c_R = eos.speed_of_sound(q_R.d, q_R.p); diff --git a/euler_operators/perfect_gas.hpp b/euler_operators/perfect_gas.hpp index 998a9ec..9fb7825 100644 --- a/euler_operators/perfect_gas.hpp +++ b/euler_operators/perfect_gas.hpp @@ -13,7 +13,6 @@ class PerfectGas public: explicit PerfectGas(T const gamma) : m_gamma(gamma), gamma_minus_one_inv(1 / (gamma - 1)) {} - template KOKKOS_FUNCTION S speed_of_sound(S const density, S const pressure) const noexcept { diff --git a/euler_operators/time_step.hpp b/euler_operators/time_step.hpp index 1f2907a..6df82c3 100644 --- a/euler_operators/time_step.hpp +++ b/euler_operators/time_step.hpp @@ -60,20 +60,24 @@ struct SimdMaxReducer { dst = Kokkos::max(dst, src); } + KOKKOS_INLINE_FUNCTION void init(value_type& val) const { using scalar_t = SimdType::value_type; val = value_type(Kokkos::reduction_identity::max()); // val = SimdType(std::numeric_limits::lowest()); } + KOKKOS_INLINE_FUNCTION value_type& reference() const { return *m_value.data(); } + KOKKOS_INLINE_FUNCTION result_view_type view() const { return m_value; } + KOKKOS_INLINE_FUNCTION bool references_scalar() const { return false; diff --git a/setups/adastra/mi300a/run_bench.sh b/setups/adastra/mi300a/run_bench.sh index a1cb25b..82a2e1c 100755 --- a/setups/adastra/mi300a/run_bench.sh +++ b/setups/adastra/mi300a/run_bench.sh @@ -26,7 +26,7 @@ export OMP_NUM_THREADS=1 export HSA_XNACK=1 export CXX=hipcc -SAFE_FILTER=$(echo "$BENCHMARK_FILTER" | sed 's/[()|^\/]/_/g') +SAVE_FILTER=$(echo "$BENCHMARK_FILTER" | sed 's/[()|^\/]/_/g') ./build-mi300/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ diff --git a/setups/prepare-laptop.sh b/setups/prepare-laptop.sh index 58c02f0..f351421 100755 --- a/setups/prepare-laptop.sh +++ b/setups/prepare-laptop.sh @@ -40,7 +40,7 @@ cmake \ -B build-gtest \ -S googletest cmake --build build-gtest -cmake --install build-gtest --prefix $gtest_ROOT +cmake --install build-gtest --prefix "$gtest_ROOT" rm -rf build-gtest googletest cmake -D CMAKE_BUILD_TYPE=Release -B build-skx diff --git a/tests/test_time_step.cpp b/tests/test_time_step.cpp index 3c3793c..bac9fa5 100644 --- a/tests/test_time_step.cpp +++ b/tests/test_time_step.cpp @@ -15,7 +15,6 @@ TEST(TimeStepRemainderWorstRem, ScalarVsVectorized) auto const nn = static_cast(n); std::size_t const n3 = nn * nn * nn; - Kokkos::DefaultExecutionSpace const exec_space; PerfectGas const eos(1.4); UniformMesh3d const mesh(1., 1., 1.); From ff5f5773fa2e19d20192b38fc0f2fdb0c6b672d5 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 30 Apr 2026 12:46:13 +0200 Subject: [PATCH 39/56] Remove casts in hllc --- euler_operators/hllc.hpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 1ee86ac..e0baa66 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -93,12 +93,11 @@ struct hllc // Compute acoustic star states T const ustar = (q_R.p - q_L.p + (rc_L * un_L) - (rc_R * un_R)) / (rc_L - rc_R); - T const pstar = static_cast(0.5) - * (q_L.p + q_R.p + (rc_L * (ustar - un_L)) + (rc_R * (ustar - un_R))); + T const pstar = 0.5 * (q_L.p + q_R.p + (rc_L * (ustar - un_L)) + (rc_R * (ustar - un_R))); // Conditions (scalar -> bool, SIMD -> mask) - auto const cond_ustar = ustar > T(0); - auto const cond_SR = S_L * S_R > T(0); + auto const cond_ustar = ustar > 0; + auto const cond_SR = S_L * S_R > 0; // Select wave speed and state T const S = select(cond_ustar, S_L, S_R); @@ -170,14 +169,14 @@ struct hllc_opti T const rcL = q_L.d * (S_L - un_L); T const rcR = q_R.d * (S_R - un_R); - T const inv_rc = T(1) / (rcL - rcR); + T const inv_rc = 1 / (rcL - rcR); T const ustar = (q_R.p - q_L.p + (rcL * un_L) - (rcR * un_R)) * inv_rc; - T const pstar = T(0.5) * (q_L.p + q_R.p + (rcL * (ustar - un_L)) + (rcR * (ustar - un_R))); + T const pstar = 0.5 * (q_L.p + q_R.p + (rcL * (ustar - un_L)) + (rcR * (ustar - un_R))); - auto const useL = ustar > T(0); - auto const same = (S_L * S_R) > T(0); + auto const useL = ustar > 0; + auto const same = (S_L * S_R) > 0; T const S = select(useL, S_L, S_R); @@ -190,18 +189,18 @@ struct hllc_opti T const un = select(useL, un_L, un_R); T const eint = eos.internal_energy(d, p); - T const ke = T(0.5) * d * ((ux0 * ux0) + (ux1 * ux1) + (ux2 * ux2)); + T const ke = 0.5 * d * ((ux0 * ux0) + (ux1 * ux1) + (ux2 * ux2)); T const etot = eint + ke; T const uno = select(same, un, ustar); T const po = select(same, p, pstar); - T const inv1 = T(1) / (S - uno); + T const inv1 = 1 / (S - uno); T const fac = (S - un) * inv1; T const dout = fac * d; - T const inv2 = T(1) / (S - ustar); + T const inv2 = 1 / (S - ustar); T const eout = (fac * etot) + (((po * uno) - (p * un)) * inv2); From 0a261cd050d51506dc59a5dbf9238bb3cf9346c4 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 30 Apr 2026 12:55:30 +0200 Subject: [PATCH 40/56] Update bm save paths, consistency adastra<->ruche --- setups/adastra/genoa/run_bench.sh | 4 ++-- setups/adastra/mi300a/run_bench.sh | 4 ++-- setups/ruche/a100/run_bench.sh | 4 ++-- setups/ruche/skx/run_bench.sh | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/setups/adastra/genoa/run_bench.sh b/setups/adastra/genoa/run_bench.sh index 2414505..a49d413 100644 --- a/setups/adastra/genoa/run_bench.sh +++ b/setups/adastra/genoa/run_bench.sh @@ -20,7 +20,7 @@ module load PrgEnv-cray set -x cd "${SLURM_SUBMIT_DIR}" || exit -mkdir -p slurm_out results/adastra/genoa/mt/ +mkdir -p slurm_out results/bm_json/adastra/ BENCHMARK_FILTER=${1:-""} export OMP_NUM_THREADS=1 @@ -36,5 +36,5 @@ export OMP_NUM_THREADS=1 ./build-genoa/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/adastra/genoa/bm_json/"[${SLURM_JOB_ID}]_BASE_${BENCHMARK_FILTER}.json" + --benchmark_out=./results/bm_json/adastra/"[${SLURM_JOB_ID}]_BASE_${BENCHMARK_FILTER}.json" # --benchmark_out=./results/adastra/genoa/bm_json/mt/"[${SLURM_JOB_ID}]_T${OMP_NUM_THREADS}-${OMP_PROC_BIND}-${OMP_PLACES}_${BENCHMARK_FILTER}.json" diff --git a/setups/adastra/mi300a/run_bench.sh b/setups/adastra/mi300a/run_bench.sh index 82a2e1c..8799254 100755 --- a/setups/adastra/mi300a/run_bench.sh +++ b/setups/adastra/mi300a/run_bench.sh @@ -19,7 +19,7 @@ module load craype-accel-amd-gfx942 set -x cd "${SLURM_SUBMIT_DIR}" || exit -mkdir -p slurm_out results/adastra/mi300/bm_json/ +mkdir -p slurm_out results/bm_json/adastra/ BENCHMARK_FILTER=${1:-""} export OMP_NUM_THREADS=1 @@ -30,6 +30,6 @@ SAVE_FILTER=$(echo "$BENCHMARK_FILTER" | sed 's/[()|^\/]/_/g') ./build-mi300/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/adastra/mi300/bm_json/"[${SLURM_JOB_ID}]_${SAVE_FILTER}.json" + --benchmark_out=./results/adastra/mi300/bm_json/"[${SLURM_JOB_ID}]_mi300_${SAVE_FILTER}.json" # --benchmark_dry_run \ diff --git a/setups/ruche/a100/run_bench.sh b/setups/ruche/a100/run_bench.sh index c514c96..14849e4 100644 --- a/setups/ruche/a100/run_bench.sh +++ b/setups/ruche/a100/run_bench.sh @@ -16,7 +16,7 @@ module load \ set -x cd "${SLURM_SUBMIT_DIR}" || exit -mkdir -p slurm_out results/ruche/a100 +mkdir -p slurm_out results/ruche/ export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK} export OMP_PROC_BIND=true @@ -26,7 +26,7 @@ BENCHMARK_FILTER=${1:-""} # include SLURM_JOB_ID in the JSON output filename ./build-a100/benchmarks/euler_benchmarks \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/a100/"[${SLURM_JOB_ID}]_a100-${BENCHMARK_FILTER}.json" # --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out=./results/ruche/"[${SLURM_JOB_ID}]_a100-${BENCHMARK_FILTER}.json" # --benchmark_filter="${BENCHMARK_FILTER}" \ ##SBATCH --exclusive ##SBATCH --hint=nomultithread diff --git a/setups/ruche/skx/run_bench.sh b/setups/ruche/skx/run_bench.sh index 1ffe8be..6054c34 100644 --- a/setups/ruche/skx/run_bench.sh +++ b/setups/ruche/skx/run_bench.sh @@ -17,7 +17,7 @@ module load \ set -x cd "${SLURM_SUBMIT_DIR}" || exit -mkdir -p slurm_out results/ruche/skx +mkdir -p slurm_out results/bm_json/ruche/ export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK} export OMP_PROC_BIND=close @@ -29,4 +29,4 @@ BENCHMARK_FILTER=${1:-""} ./build-skx/benchmarks/euler_benchmarks \ --benchmark_filter="${BENCHMARK_FILTER}" \ --benchmark_out_format=json \ - --benchmark_out=./results/ruche/skx/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}.json" + --benchmark_out=./results/bm_json/ruche/"[${SLURM_JOB_ID}]_skx-${BENCHMARK_FILTER}.json" From d3a9149d172c6baacd21c29c02761bbee0c4d4e5 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 13:25:28 +0200 Subject: [PATCH 41/56] Use common mapping + data_handle in godunov --- euler_operators/godunov.hpp | 45 ++++++++++++++----------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index a14ebd4..f15552b 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -125,13 +125,9 @@ void godunov_kernel( Kokkos::Array const ds = {mesh.ds0(), mesh.ds1(), mesh.ds2()}; T const dtodv = dt / mesh.dv(); - T* cd = cons_arrays.d.data_handle(); - T* ce = cons_arrays.e.data_handle(); - T* cm0 = cons_arrays.mx0.data_handle(); - T* cm1 = cons_arrays.mx1.data_handle(); - T* cm2 = cons_arrays.mx2.data_handle(); - - auto const cons_ptrs = EulerConsArrays {cd, ce, cm0, cm1, cm2}; + Kokkos::layout_left::mapping const common_mapping = prim_arrays.d.mapping(); + EulerPrimArrays const prim_ptrs = data_handle(prim_arrays); + EulerConsArrays const cons_ptrs = data_handle(cons_arrays); Kokkos::parallel_for( "godunov_kernel", @@ -142,18 +138,15 @@ void godunov_kernel( {0, 1, 1}, {nx_blocks, ny - 1, nz - 1}), // nx_begin already acouting for ghost cells KOKKOS_LAMBDA(IndexType const bi, IndexType const j, IndexType const k) { - IndexType const base = prim_arrays.d.mapping()(nx_begin + (bi * width), j, k); - + IndexType const base = common_mapping(nx_begin + (bi * width), j, k); EulerPrim const prim = load(prim_arrays, base); EulerFlux flux {}; { - EulerPrim const prim_L = load(prim_arrays, base - 1); - EulerPrim const prim_R = load(prim_arrays, base + 1); - EulerFlux const flux_L - = riemann_solver(dir_t<0>(), eos, prim_L, prim); - EulerFlux const flux_R - = riemann_solver(dir_t<0>(), eos, prim, prim_R); + EulerPrim const prim_L = load(prim_arrays, base - 1); + EulerPrim const prim_R = load(prim_arrays, base + 1); + EulerFlux const flux_L = riemann_solver(dir_t<0>(), eos, prim_L, prim); + EulerFlux const flux_R = riemann_solver(dir_t<0>(), eos, prim, prim_R); flux.d += ds[0] * (flux_R.d - flux_L.d); flux.e += ds[0] * (flux_R.e - flux_L.e); flux.mx0 += ds[0] * (flux_R.mx0 - flux_L.mx0); @@ -161,12 +154,10 @@ void godunov_kernel( flux.mx2 += ds[0] * (flux_R.mx2 - flux_L.mx2); } { - EulerPrim const prim_L = load(prim_arrays, base - stride_y); - EulerPrim const prim_R = load(prim_arrays, base + stride_y); - EulerFlux const flux_L - = riemann_solver(dir_t<1>(), eos, prim_L, prim); - EulerFlux const flux_R - = riemann_solver(dir_t<1>(), eos, prim, prim_R); + EulerPrim const prim_L = load(prim_arrays, base - stride_y); + EulerPrim const prim_R = load(prim_arrays, base + stride_y); + EulerFlux const flux_L = riemann_solver(dir_t<1>(), eos, prim_L, prim); + EulerFlux const flux_R = riemann_solver(dir_t<1>(), eos, prim, prim_R); flux.d += ds[1] * (flux_R.d - flux_L.d); flux.e += ds[1] * (flux_R.e - flux_L.e); flux.mx0 += ds[1] * (flux_R.mx0 - flux_L.mx0); @@ -174,12 +165,10 @@ void godunov_kernel( flux.mx2 += ds[1] * (flux_R.mx2 - flux_L.mx2); } { - EulerPrim const prim_L = load(prim_arrays, base - stride_z); - EulerPrim const prim_R = load(prim_arrays, base + stride_z); - EulerFlux const flux_L - = riemann_solver(dir_t<2>(), eos, prim_L, prim); - EulerFlux const flux_R - = riemann_solver(dir_t<2>(), eos, prim, prim_R); + EulerPrim const prim_L = load(prim_arrays, base - stride_z); + EulerPrim const prim_R = load(prim_arrays, base + stride_z); + EulerFlux const flux_L = riemann_solver(dir_t<2>(), eos, prim_L, prim); + EulerFlux const flux_R = riemann_solver(dir_t<2>(), eos, prim, prim_R); flux.d += ds[2] * (flux_R.d - flux_L.d); flux.e += ds[2] * (flux_R.e - flux_L.e); flux.mx0 += ds[2] * (flux_R.mx0 - flux_L.mx0); @@ -187,7 +176,7 @@ void godunov_kernel( flux.mx2 += ds[2] * (flux_R.mx2 - flux_L.mx2); } - EulerCons cons = load(cons_ptrs, base); + EulerCons cons = load(cons_ptrs, base); cons.d -= dtodv * flux.d; cons.e -= dtodv * flux.e; cons.mx0 -= dtodv * flux.mx0; From 7720339db9965083d75de001cf549534fe302919 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 13:27:19 +0200 Subject: [PATCH 42/56] Flatten loop for compare() in test_godunov --- tests/test_godunov.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/test_godunov.cpp b/tests/test_godunov.cpp index 9534727..780bcfd 100644 --- a/tests/test_godunov.cpp +++ b/tests/test_godunov.cpp @@ -124,14 +124,8 @@ void run_case(int n, double dt, EulerPrim const& prim) double const tol = 1e-12; - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - for (int k = 0; k < n; ++k) { - int const idx = i + (n * (j + (n * k))); - - compare(ref_h, vec_h, tol, idx); - } - } + for (int idx = 0; idx < n * n * n; ++idx) { + compare(ref_h, vec_h, tol, idx); } } From 0ecd9bcf83b530e7621c8591e23a71b9cba02927 Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 14:50:49 +0200 Subject: [PATCH 43/56] Uncomment prepare scripts sections, format hllc headers --- euler_operators/hllc.hpp | 5 ++--- setups/adastra/genoa/prepare.sh | 38 ++++++++++++++++---------------- setups/adastra/mi300a/prepare.sh | 38 ++++++++++++++++---------------- setups/prepare-laptop.sh | 5 ++++- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 3bbaac5..228a013 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -5,9 +5,8 @@ #include #include - -#include "euler_arrays.hpp" -#include "perfect_gas.hpp" +#include +#include template KOKKOS_FUNCTION T get(std::integral_constant /*unused*/, EulerPrim const& prim) diff --git a/setups/adastra/genoa/prepare.sh b/setups/adastra/genoa/prepare.sh index ecc2da1..b56f17e 100755 --- a/setups/adastra/genoa/prepare.sh +++ b/setups/adastra/genoa/prepare.sh @@ -11,16 +11,16 @@ export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark export gtest_ROOT=$install_dir/gtest -# git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git -# cmake \ -# -D BENCHMARK_ENABLE_TESTING=OFF \ -# -D CMAKE_BUILD_TYPE=Release \ -# -D CMAKE_CXX_STANDARD=20 \ -# -B build-benchmark \ -# -S benchmark -# cmake --build build-benchmark --parallel 8 -# cmake --install build-benchmark --prefix "$benchmark_ROOT" -# rm -rf build-benchmark benchmark +git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git +cmake \ + -D BENCHMARK_ENABLE_TESTING=OFF \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-benchmark \ + -S benchmark +cmake --build build-benchmark --parallel 8 +cmake --install build-benchmark --prefix "$benchmark_ROOT" +rm -rf build-benchmark benchmark git clone https://github.com/kokkos/kokkos.git cd kokkos || exit @@ -39,15 +39,15 @@ cmake --build build-kokkos --parallel cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos -# git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git -# cmake \ -# -D CMAKE_BUILD_TYPE=Release \ -# -D CMAKE_CXX_STANDARD=20 \ -# -B build-gtest \ -# -S googletest -# cmake --build build-gtest -# cmake --install build-gtest --prefix "$gtest_ROOT" -# rm -rf build-gtest googletest +git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest +cmake --install build-gtest --prefix "$gtest_ROOT" +rm -rf build-gtest googletest cmake -DGTest_ROOT="$gtest_ROOT" -D CMAKE_BUILD_TYPE=Release -B build-genoa cmake --build build-genoa --parallel 8 diff --git a/setups/adastra/mi300a/prepare.sh b/setups/adastra/mi300a/prepare.sh index 1a54df5..dde83df 100755 --- a/setups/adastra/mi300a/prepare.sh +++ b/setups/adastra/mi300a/prepare.sh @@ -15,16 +15,16 @@ export Kokkos_ROOT=$install_dir/kokkos export benchmark_ROOT=$install_dir/benchmark export gtest_ROOT=$install_dir/gtest -# git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git -# cmake \ -# -D BENCHMARK_ENABLE_TESTING=OFF \ -# -D CMAKE_BUILD_TYPE=Release \ -# -D CMAKE_CXX_STANDARD=20 \ -# -B build-benchmark \ -# -S benchmark -# cmake --build build-benchmark --parallel 8 -# cmake --install build-benchmark --prefix "$benchmark_ROOT" -# rm -rf build-benchmark benchmark +git clone --branch v1.9.4 --depth 1 https://github.com/google/benchmark.git +cmake \ + -D BENCHMARK_ENABLE_TESTING=OFF \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-benchmark \ + -S benchmark +cmake --build build-benchmark --parallel 8 +cmake --install build-benchmark --prefix "$benchmark_ROOT" +rm -rf build-benchmark benchmark git clone https://github.com/kokkos/kokkos.git cd kokkos || exit @@ -46,15 +46,15 @@ cmake --build build-kokkos --parallel 8 cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos -# git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git -# cmake \ -# -D CMAKE_BUILD_TYPE=Release \ -# -D CMAKE_CXX_STANDARD=20 \ -# -B build-gtest \ -# -S googletest -# cmake --build build-gtest -# cmake --install build-gtest --prefix "$gtest_ROOT" -# rm -rf build-gtest googletest +git clone --branch v1.17.0 --depth 1 https://github.com/google/googletest.git +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest +cmake --install build-gtest --prefix "$gtest_ROOT" +rm -rf build-gtest googletest cmake -DGTest_ROOT="$gtest_ROOT" -D CMAKE_BUILD_TYPE=Release -B build-mi300 cmake --build build-mi300 --parallel 8 diff --git a/setups/prepare-laptop.sh b/setups/prepare-laptop.sh index f351421..1401c04 100755 --- a/setups/prepare-laptop.sh +++ b/setups/prepare-laptop.sh @@ -19,7 +19,10 @@ cmake --build build-benchmark --parallel 4 cmake --install build-benchmark --prefix "$benchmark_ROOT" rm -rf build-benchmark benchmark -git clone --branch 5.0.0 --depth 1 https://github.com/kokkos/kokkos.git +git clone https://github.com/kokkos/kokkos.git +cd kokkos || exit +git checkout 7f8988b4d +cd .. || exit cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ From 1bfd88233131ab206637f392848c041fdf29979a Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 15:05:17 +0200 Subject: [PATCH 44/56] Use APU Kokkos Arch flag for mi300a --- setups/adastra/mi300a/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setups/adastra/mi300a/prepare.sh b/setups/adastra/mi300a/prepare.sh index dde83df..73f2e08 100755 --- a/setups/adastra/mi300a/prepare.sh +++ b/setups/adastra/mi300a/prepare.sh @@ -35,7 +35,7 @@ cmake \ -D CMAKE_BUILD_TYPE=Release \ -D CMAKE_CXX_STANDARD=20 \ -D Kokkos_ARCH_ZEN4=ON \ - -D Kokkos_ARCH_AMD_GFX942=ON \ + -D Kokkos_ARCH_AMD_GFX942_APU=ON \ -D Kokkos_ENABLE_DEPRECATED_CODE_4=OFF \ -D Kokkos_ENABLE_DEPRECATION_WARNINGS=OFF \ -D Kokkos_ENABLE_HIP=ON \ From f0b1992aec1ca9866bce8a4c940fc903fcb3b31b Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 15:13:02 +0200 Subject: [PATCH 45/56] linting and iwyu --- euler_operators/godunov.hpp | 1 - tests/test_godunov.cpp | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index f15552b..14c5cff 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/tests/test_godunov.cpp b/tests/test_godunov.cpp index 780bcfd..1b168ec 100644 --- a/tests/test_godunov.cpp +++ b/tests/test_godunov.cpp @@ -1,6 +1,11 @@ +#include +#include + #include #include +#include +#include #include #include #include From 3fd27af58d75c211cf317f766c8c38a20dcc9a6e Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 15:18:35 +0200 Subject: [PATCH 46/56] kokkos simd include bm euler_simulation fro iwyu --- benchmarks/benchmark_euler_simulation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index 45808bb..7a6bd12 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include From 33077da490a1c22de8a7c738b61e4a5535c57d7b Mon Sep 17 00:00:00 2001 From: Timothy Date: Tue, 5 May 2026 15:43:18 +0200 Subject: [PATCH 47/56] Try Kokkos_SIMD.hpp for iwyu --- benchmarks/benchmark_euler_simulation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index 7a6bd12..8967728 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include From 9f49653c0c7b79737b50ffa7a38bab1dbd332aca Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:42:27 +0200 Subject: [PATCH 48/56] test_godunov.cpp update inlcude for iwyu --- tests/test_godunov.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_godunov.cpp b/tests/test_godunov.cpp index 1b168ec..d0c9632 100644 --- a/tests/test_godunov.cpp +++ b/tests/test_godunov.cpp @@ -4,8 +4,7 @@ #include #include -#include -#include +#include #include #include #include From dd144685431f4b70bbc88d80096defde1fe72fde Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:45:18 +0200 Subject: [PATCH 49/56] benchmark_godunov.cpp update inlcude for iwyu --- benchmarks/benchmark_godunov.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index 05c1a2e..c7a15c3 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include From 13017f3af881150094d4a304412f6f6e3d55b9e7 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:48:23 +0200 Subject: [PATCH 50/56] godunov.hpp update inlcude for iwyu --- euler_operators/godunov.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/euler_operators/godunov.hpp b/euler_operators/godunov.hpp index 14c5cff..d9de850 100644 --- a/euler_operators/godunov.hpp +++ b/euler_operators/godunov.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include From 0eb64fe50b2840308764a7a431331adcbcb5a964 Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:50:39 +0200 Subject: [PATCH 51/56] benchmark_euler_simulation.cpp update include for iwyu --- benchmarks/benchmark_euler_simulation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/benchmarks/benchmark_euler_simulation.cpp b/benchmarks/benchmark_euler_simulation.cpp index 8967728..45808bb 100644 --- a/benchmarks/benchmark_euler_simulation.cpp +++ b/benchmarks/benchmark_euler_simulation.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include From 6ca6d0472d5fc60ee0183d09dd682ea471d3ff0f Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:52:25 +0200 Subject: [PATCH 52/56] benchmark_godunov.cpp update include for iwyu --- benchmarks/benchmark_godunov.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/benchmarks/benchmark_godunov.cpp b/benchmarks/benchmark_godunov.cpp index c7a15c3..05c1a2e 100644 --- a/benchmarks/benchmark_godunov.cpp +++ b/benchmarks/benchmark_godunov.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include From e267113105bf728cefc9d7e2eaa32beaf102509b Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:55:19 +0200 Subject: [PATCH 53/56] test_godunov.cpp update include for iwyu --- tests/test_godunov.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_godunov.cpp b/tests/test_godunov.cpp index d0c9632..0fd788c 100644 --- a/tests/test_godunov.cpp +++ b/tests/test_godunov.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include From c05168d6099616a765b72a0cbb0cc19e42adb56e Mon Sep 17 00:00:00 2001 From: Timothy Date: Wed, 6 May 2026 13:59:31 +0200 Subject: [PATCH 54/56] hllc.hpp update include for iwyu --- euler_operators/hllc.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 228a013..4d23621 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -3,8 +3,8 @@ #include #include -#include -#include +#include +#include #include #include From db6c671e98f34cdef9ce7d84a8d39c36ad09b08a Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 7 May 2026 09:56:29 +0200 Subject: [PATCH 55/56] Specify 8 to --parallel flag, remove old modules, fix inlcudes in hllc --- euler_operators/hllc.hpp | 3 +- setups/adastra/genoa/prepare.sh | 2 +- setups/inti/grace/job.sh | 26 ++++++++++++++ setups/inti/grace/prepare.sh | 63 +++++++++++++++++++++++++++++++++ setups/inti/grace/run_bench.sh | 30 ++++++++++++++++ setups/ruche/skx/prepare.sh | 4 +-- setups/ruche/v100/prepare.sh | 5 --- 7 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 setups/inti/grace/job.sh create mode 100755 setups/inti/grace/prepare.sh create mode 100644 setups/inti/grace/run_bench.sh diff --git a/euler_operators/hllc.hpp b/euler_operators/hllc.hpp index 4d23621..9b4cf55 100644 --- a/euler_operators/hllc.hpp +++ b/euler_operators/hllc.hpp @@ -3,7 +3,8 @@ #include #include -#include +#include +#include #include #include #include diff --git a/setups/adastra/genoa/prepare.sh b/setups/adastra/genoa/prepare.sh index b56f17e..8525366 100755 --- a/setups/adastra/genoa/prepare.sh +++ b/setups/adastra/genoa/prepare.sh @@ -35,7 +35,7 @@ cmake \ -D Kokkos_ENABLE_OPENMP=ON \ -B build-kokkos \ -S kokkos -cmake --build build-kokkos --parallel +cmake --build build-kokkos --parallel 8 cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos diff --git a/setups/inti/grace/job.sh b/setups/inti/grace/job.sh new file mode 100644 index 0000000..9303c88 --- /dev/null +++ b/setups/inti/grace/job.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +#SBATCH --account=gen2224 +#SBATCH --job-name=benchmarks-euler +#SBATCH --nodes=1 +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=192 +#SBATCH --constraint=GENOA +#SBATCH --time=05:59:00 +#SBATCH --exclusive + +set -ex + +cd "${SLURM_SUBMIT_DIR}" + +module purge + +module load \ + gcc-native/13.2 + +export OMP_PLACES=cores +export OMP_PROC_BIND=close +export OMP_DISPLAY_AFFINITY=true + +export OMP_NUM_THREADS=8 +srun ./build-genoa/euler_benchmarks --kokkos-print-configuration diff --git a/setups/inti/grace/prepare.sh b/setups/inti/grace/prepare.sh new file mode 100755 index 0000000..dbcb031 --- /dev/null +++ b/setups/inti/grace/prepare.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +module load nvhpc/26.3 \ + mpi/openmpi/5 \ + flavor/hdf5/parallel \ + cmake/3.31.4 +# /ccc/products/openmpi-5.0.8/nvidia--26.3__cuda--13.0/default/ + +export install_dir=$PWD/opt/gh200 +export Kokkos_ROOT=$install_dir/kokkos +export benchmark_ROOT=$install_dir/benchmark +export gtest_ROOT=$install_dir/gtest + +# ======================== +# benchmark +# ======================== +cmake \ + -D BENCHMARK_ENABLE_TESTING=OFF \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-benchmark \ + -S benchmark +cmake --build build-benchmark --parallel 8 +cmake --install build-benchmark --prefix "$benchmark_ROOT" +rm -rf build-benchmark + +# ======================== +# kokkos +# ======================== +cmake \ + -D CMAKE_C_COMPILER=$(which gcc) \ + -D CMAKE_CXX_COMPILER=$(which c++) \ + -D CMAKE_BUILD_TYPE=Release \ + -D Kokkos_ARCH_ARMV9_GRACE=ON \ + -D Kokkos_ENABLE_OPENMP=ON \ + -D CMAKE_CXX_FLAGS="-mcpu=neoverse-v2+crypto+sve2-aes+sve2-sha3+sve2-sm4+norng" \ + -B build-kokkos \ + -S . +cmake --build build-kokkos --parallel 8 +cmake --install build-kokkos --prefix "$Kokkos_ROOT" +rm -rf build-kokkos + +# ======================== +# gtest +# ======================== +cmake \ + -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_CXX_STANDARD=20 \ + -B build-gtest \ + -S googletest +cmake --build build-gtest --parallel 8 +cmake --install build-gtest --prefix "$gtest_ROOT" +rm -rf build-gtest + +# ======================== +# your project +# ======================== +cmake \ + -DGTest_ROOT="$gtest_ROOT" \ + -DCMAKE_BUILD_TYPE=Release \ + -B build-gh200 + +cmake --build build-gh200 --parallel 8 diff --git a/setups/inti/grace/run_bench.sh b/setups/inti/grace/run_bench.sh new file mode 100644 index 0000000..3336b82 --- /dev/null +++ b/setups/inti/grace/run_bench.sh @@ -0,0 +1,30 @@ +#!/bin/bash +#MSUB + +module purge + +module load \ + gcc-13.2.0 \ + cmake/3.29.6 + +set -x +cd "${SLURM_SUBMIT_DIR}" || exit + +mkdir -p slurm_out results/inti/grace/ +BENCHMARK_FILTER=${1:-""} + +export OMP_NUM_THREADS=1 +# export OMP_PROC_BIND=CLOSE +# export OMP_PLACES=THREADS + +# export OMP_DISPLAY_AFFINITY=TRUE +# export OMP_AFFINITY_FORMAT="thread %0.3n -> cpu %A" +# numactl -H + +# srun bash -c 'echo $SLURM_CPUS_PER_TASK; grep Cpus_allowed_list /proc/self/status' + +./build-genoa/benchmarks/euler_benchmarks \ + --benchmark_filter="${BENCHMARK_FILTER}" \ + --benchmark_out_format=json \ + --benchmark_out=./results/bm_json/adastra/"[${SLURM_JOB_ID}]_BASE_${BENCHMARK_FILTER}.json" +# --benchmark_out=./results/adastra/genoa/bm_json/mt/"[${SLURM_JOB_ID}]_T${OMP_NUM_THREADS}-${OMP_PROC_BIND}-${OMP_PLACES}_${BENCHMARK_FILTER}.json" diff --git a/setups/ruche/skx/prepare.sh b/setups/ruche/skx/prepare.sh index 7aee118..2e08785 100755 --- a/setups/ruche/skx/prepare.sh +++ b/setups/ruche/skx/prepare.sh @@ -37,7 +37,7 @@ cmake \ -D Kokkos_ENABLE_OPENMP=ON \ -B build-kokkos \ -S kokkos -cmake --build build-kokkos --parallel +cmake --build build-kokkos --parallel 8 cmake --install build-kokkos --prefix "$Kokkos_ROOT" rm -rf build-kokkos kokkos @@ -52,4 +52,4 @@ cmake --install build-gtest --prefix "$gtest_ROOT" rm -rf build-gtest googletest cmake -D CMAKE_BUILD_TYPE=Release -B build-skx -cmake --build build-skx --parallel +cmake --build build-skx --parallel 8 diff --git a/setups/ruche/v100/prepare.sh b/setups/ruche/v100/prepare.sh index 7d29e6b..64e412a 100755 --- a/setups/ruche/v100/prepare.sh +++ b/setups/ruche/v100/prepare.sh @@ -2,11 +2,6 @@ module purge -module load \ - gcc/11.2.0/gcc-4.8.5 \ - cmake/3.28.3/gcc-11.2.0 \ - cuda/12.2.1/gcc-11.2.0 - module load \ gcc/13.4.0/gcc-15.1.0 \ cmake/3.31.9/gcc-15.1.0 \ From a95a503ae0f4578e8f45b5077c2c32b14a787a26 Mon Sep 17 00:00:00 2001 From: Timothy Date: Thu, 7 May 2026 10:22:05 +0200 Subject: [PATCH 56/56] Remove inti setup scripts --- setups/inti/grace/job.sh | 26 -------------- setups/inti/grace/prepare.sh | 63 ---------------------------------- setups/inti/grace/run_bench.sh | 30 ---------------- 3 files changed, 119 deletions(-) delete mode 100644 setups/inti/grace/job.sh delete mode 100755 setups/inti/grace/prepare.sh delete mode 100644 setups/inti/grace/run_bench.sh diff --git a/setups/inti/grace/job.sh b/setups/inti/grace/job.sh deleted file mode 100644 index 9303c88..0000000 --- a/setups/inti/grace/job.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -#SBATCH --account=gen2224 -#SBATCH --job-name=benchmarks-euler -#SBATCH --nodes=1 -#SBATCH --ntasks=1 -#SBATCH --cpus-per-task=192 -#SBATCH --constraint=GENOA -#SBATCH --time=05:59:00 -#SBATCH --exclusive - -set -ex - -cd "${SLURM_SUBMIT_DIR}" - -module purge - -module load \ - gcc-native/13.2 - -export OMP_PLACES=cores -export OMP_PROC_BIND=close -export OMP_DISPLAY_AFFINITY=true - -export OMP_NUM_THREADS=8 -srun ./build-genoa/euler_benchmarks --kokkos-print-configuration diff --git a/setups/inti/grace/prepare.sh b/setups/inti/grace/prepare.sh deleted file mode 100755 index dbcb031..0000000 --- a/setups/inti/grace/prepare.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -module load nvhpc/26.3 \ - mpi/openmpi/5 \ - flavor/hdf5/parallel \ - cmake/3.31.4 -# /ccc/products/openmpi-5.0.8/nvidia--26.3__cuda--13.0/default/ - -export install_dir=$PWD/opt/gh200 -export Kokkos_ROOT=$install_dir/kokkos -export benchmark_ROOT=$install_dir/benchmark -export gtest_ROOT=$install_dir/gtest - -# ======================== -# benchmark -# ======================== -cmake \ - -D BENCHMARK_ENABLE_TESTING=OFF \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-benchmark \ - -S benchmark -cmake --build build-benchmark --parallel 8 -cmake --install build-benchmark --prefix "$benchmark_ROOT" -rm -rf build-benchmark - -# ======================== -# kokkos -# ======================== -cmake \ - -D CMAKE_C_COMPILER=$(which gcc) \ - -D CMAKE_CXX_COMPILER=$(which c++) \ - -D CMAKE_BUILD_TYPE=Release \ - -D Kokkos_ARCH_ARMV9_GRACE=ON \ - -D Kokkos_ENABLE_OPENMP=ON \ - -D CMAKE_CXX_FLAGS="-mcpu=neoverse-v2+crypto+sve2-aes+sve2-sha3+sve2-sm4+norng" \ - -B build-kokkos \ - -S . -cmake --build build-kokkos --parallel 8 -cmake --install build-kokkos --prefix "$Kokkos_ROOT" -rm -rf build-kokkos - -# ======================== -# gtest -# ======================== -cmake \ - -D CMAKE_BUILD_TYPE=Release \ - -D CMAKE_CXX_STANDARD=20 \ - -B build-gtest \ - -S googletest -cmake --build build-gtest --parallel 8 -cmake --install build-gtest --prefix "$gtest_ROOT" -rm -rf build-gtest - -# ======================== -# your project -# ======================== -cmake \ - -DGTest_ROOT="$gtest_ROOT" \ - -DCMAKE_BUILD_TYPE=Release \ - -B build-gh200 - -cmake --build build-gh200 --parallel 8 diff --git a/setups/inti/grace/run_bench.sh b/setups/inti/grace/run_bench.sh deleted file mode 100644 index 3336b82..0000000 --- a/setups/inti/grace/run_bench.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -#MSUB - -module purge - -module load \ - gcc-13.2.0 \ - cmake/3.29.6 - -set -x -cd "${SLURM_SUBMIT_DIR}" || exit - -mkdir -p slurm_out results/inti/grace/ -BENCHMARK_FILTER=${1:-""} - -export OMP_NUM_THREADS=1 -# export OMP_PROC_BIND=CLOSE -# export OMP_PLACES=THREADS - -# export OMP_DISPLAY_AFFINITY=TRUE -# export OMP_AFFINITY_FORMAT="thread %0.3n -> cpu %A" -# numactl -H - -# srun bash -c 'echo $SLURM_CPUS_PER_TASK; grep Cpus_allowed_list /proc/self/status' - -./build-genoa/benchmarks/euler_benchmarks \ - --benchmark_filter="${BENCHMARK_FILTER}" \ - --benchmark_out_format=json \ - --benchmark_out=./results/bm_json/adastra/"[${SLURM_JOB_ID}]_BASE_${BENCHMARK_FILTER}.json" -# --benchmark_out=./results/adastra/genoa/bm_json/mt/"[${SLURM_JOB_ID}]_T${OMP_NUM_THREADS}-${OMP_PROC_BIND}-${OMP_PLACES}_${BENCHMARK_FILTER}.json"