diff --git a/fft/src/CMakeLists.txt b/fft/src/CMakeLists.txt index 1dc94d02..3a7161ad 100644 --- a/fft/src/CMakeLists.txt +++ b/fft/src/CMakeLists.txt @@ -2,15 +2,15 @@ # # SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception -add_library(fft STATIC KokkosFFT_Core.cpp) +add_library(fft INTERFACE) target_link_libraries(fft - PUBLIC +INTERFACE common Kokkos::kokkos ) -target_include_directories(fft PUBLIC +target_include_directories(fft INTERFACE $ $ ) diff --git a/fft/src/KokkosFFT.hpp b/fft/src/KokkosFFT.hpp index 6b035b56..2df13dd8 100644 --- a/fft/src/KokkosFFT.hpp +++ b/fft/src/KokkosFFT.hpp @@ -10,6 +10,5 @@ #include "KokkosFFT_Helpers.hpp" #include "KokkosFFT_Plans.hpp" #include "KokkosFFT_Transform.hpp" -#include "KokkosFFT_Core.hpp" #endif diff --git a/fft/src/KokkosFFT_Cuda_plans.hpp b/fft/src/KokkosFFT_Cuda_plans.hpp index dc8423c8..9e50071c 100644 --- a/fft/src/KokkosFFT_Cuda_plans.hpp +++ b/fft/src/KokkosFFT_Cuda_plans.hpp @@ -13,6 +13,26 @@ namespace KokkosFFT { namespace Impl { + +template , + std::nullptr_t> = nullptr> +void setup() { + static bool once = [] { + if (!(Kokkos::is_initialized() || Kokkos::is_finalized())) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called before initializing " + "Kokkos.\n"); + } + if (Kokkos::is_finalized()) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called after finalizing " + "Kokkos.\n"); + } + return true; + }(); +} + // 1D transform template , + std::nullptr_t> = nullptr> +void setup() { + static bool once = [] { + if (!(Kokkos::is_initialized() || Kokkos::is_finalized())) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called before initializing " + "Kokkos.\n"); + } + if (Kokkos::is_finalized()) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called after finalizing " + "Kokkos.\n"); + } + return true; + }(); +} + // 1D transform template , std::nullptr_t> = + nullptr> +void setup() { + static bool once = [] { + if (!(Kokkos::is_initialized() || Kokkos::is_finalized())) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called before initializing " + "Kokkos.\n"); + } + if (Kokkos::is_finalized()) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called after finalizing " + "Kokkos.\n"); + } +#if defined(KOKKOS_ENABLE_OPENMP) || defined(KOKKOS_ENABLE_THREADS) + if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { + fftwf_init_threads(); + } else { + fftw_init_threads(); + } + + // Register cleanup function as a hook in Kokkos::finalize + Kokkos::push_finalize_hook([]() { + if constexpr (std::is_same_v) { + fftwf_cleanup_threads(); + } else { + fftw_cleanup_threads(); + } + }); + } +#endif + return true; + }(); +} + // batched transform, over ND Views template (); m_fft_size = KokkosFFT::Impl::create_plan( exec_space, m_plan, in, out, direction, m_axes, s, m_is_inplace); } @@ -260,6 +261,7 @@ class Plan { "In-place transform is not supported with reshape. " "Please use out-of-place transform."); + KokkosFFT::Impl::setup(); m_fft_size = KokkosFFT::Impl::create_plan(exec_space, m_plan, in, out, direction, axes, s, m_is_inplace); } diff --git a/fft/src/KokkosFFT_ROCM_plans.hpp b/fft/src/KokkosFFT_ROCM_plans.hpp index 56c1d6c3..68b45dc8 100644 --- a/fft/src/KokkosFFT_ROCM_plans.hpp +++ b/fft/src/KokkosFFT_ROCM_plans.hpp @@ -5,6 +5,7 @@ #ifndef KOKKOSFFT_ROCM_PLANS_HPP #define KOKKOSFFT_ROCM_PLANS_HPP +#include #include "KokkosFFT_ROCM_types.hpp" #include "KokkosFFT_Extents.hpp" #include "KokkosFFT_traits.hpp" @@ -14,6 +15,35 @@ namespace KokkosFFT { namespace Impl { +template , + std::nullptr_t> = nullptr> +void setup() { + static bool once = [] { + if (!(Kokkos::is_initialized() || Kokkos::is_finalized())) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called before initializing " + "Kokkos.\n"); + } + if (Kokkos::is_finalized()) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called after finalizing " + "Kokkos.\n"); + } + + rocfft_status status = rocfft_setup(); + if (status != rocfft_status_success) Kokkos::abort("rocfft_setup failed"); + + // Register cleanup function as a hook in Kokkos::finalize + Kokkos::push_finalize_hook([]() { + rocfft_status status = rocfft_cleanup(); + if (status != rocfft_status_success) + Kokkos::abort("rocfft_cleanup failed"); + }); + return true; + }(); +} + // batched transform, over ND Views template , + std::nullptr_t> = nullptr> +void setup() { + static bool once = [] { + if (!(Kokkos::is_initialized() || Kokkos::is_finalized())) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called before initializing " + "Kokkos.\n"); + } + if (Kokkos::is_finalized()) { + Kokkos::abort( + "Error: KokkosFFT APIs must not be called after finalizing " + "Kokkos.\n"); + } + return true; + }(); +} + // Helper to convert the integer type of vectors template auto convert_int_type(std::vector& in) -> std::vector { diff --git a/fft/src/KokkosFFT_SYCL_types.hpp b/fft/src/KokkosFFT_SYCL_types.hpp index 285574c8..05dfe6ec 100644 --- a/fft/src/KokkosFFT_SYCL_types.hpp +++ b/fft/src/KokkosFFT_SYCL_types.hpp @@ -226,12 +226,8 @@ auto direction_type(Direction direction) { return direction == Direction::forward ? MKL_FFT_FORWARD : MKL_FFT_BACKWARD; } -inline void initialize_host() {} -inline void finalize_host() {} #endif -inline void initialize_device() {} -inline void finalize_device() {} } // namespace Impl } // namespace KokkosFFT diff --git a/fft/unit_test/CMakeLists.txt b/fft/unit_test/CMakeLists.txt index 5ce15ad8..e173111a 100644 --- a/fft/unit_test/CMakeLists.txt +++ b/fft/unit_test/CMakeLists.txt @@ -8,18 +8,9 @@ add_executable(unit-tests-kokkos-fft-core Test_Transform.cpp ) -add_executable(unit-tests-kokkos-fft-core-no-init - Test_MainNoInit.cpp - Test_InitializeFinalize.cpp -) - target_compile_features(unit-tests-kokkos-fft-core PUBLIC cxx_std_17) target_link_libraries(unit-tests-kokkos-fft-core PUBLIC KokkosFFT::fft GTest::gtest) -target_compile_features(unit-tests-kokkos-fft-core-no-init PUBLIC cxx_std_17) -target_link_libraries(unit-tests-kokkos-fft-core-no-init PUBLIC KokkosFFT::fft GTest::gtest) - # Enable GoogleTest include(GoogleTest) gtest_discover_tests(unit-tests-kokkos-fft-core PROPERTIES DISCOVERY_TIMEOUT 600 DISCOVERY_MODE PRE_TEST) -gtest_discover_tests(unit-tests-kokkos-fft-core-no-init PROPERTIES DISCOVERY_TIMEOUT 600 DISCOVERY_MODE PRE_TEST) diff --git a/fft/unit_test/Test_InitializeFinalize.cpp b/fft/unit_test/Test_InitializeFinalize.cpp deleted file mode 100644 index 8ffbdaa7..00000000 --- a/fft/unit_test/Test_InitializeFinalize.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-FileCopyrightText: (C) The kokkos-fft development team, see COPYRIGHT.md file -// -// SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception - -#include -#include -#include "KokkosFFT_Core.hpp" - -/** - * Fixture that checks Kokkos is neither initialized nor finalized before and - * after each test. - */ -class KokkosFFTExecutionEnvironmentNeverInitialized : public ::testing::Test { - static void checkNeverInitialized() { - ASSERT_FALSE(Kokkos::is_initialized()); - ASSERT_FALSE(Kokkos::is_finalized()); - ASSERT_FALSE(KokkosFFT::is_initialized()); - ASSERT_FALSE(KokkosFFT::is_finalized()); - } - - protected: - void SetUp() override { checkNeverInitialized(); } - void TearDown() override { checkNeverInitialized(); } -}; - -namespace { -using InitializeFinalize_DeathTest = - KokkosFFTExecutionEnvironmentNeverInitialized; -using InitializeFinalize_Test = KokkosFFTExecutionEnvironmentNeverInitialized; - -TEST_F(InitializeFinalize_DeathTest, initialize) { - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // Valid usage - EXPECT_EXIT( - { - Kokkos::initialize(); - KokkosFFT::initialize(); - KokkosFFT::finalize(); - Kokkos::finalize(); - std::exit(EXIT_SUCCESS); - }, - ::testing::ExitedWithCode(EXIT_SUCCESS), ""); - - // KokkosFFT is initialized twice - EXPECT_DEATH( - { - Kokkos::initialize(); - KokkosFFT::initialize(); - KokkosFFT::initialize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::initialize\\(\\) has already been called. KokkosFFT " - "can be " - "initialized at most once\\."); - - // KokkosFFT is initialized twice after finalize - EXPECT_DEATH( - { - Kokkos::initialize(); - KokkosFFT::initialize(); - KokkosFFT::finalize(); - KokkosFFT::initialize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::initialize\\(\\) has already been called. KokkosFFT " - "can be " - "initialized at most once\\."); - - // KokkosFFT is initialized before Kokkos initialization - EXPECT_DEATH( - { - KokkosFFT::initialize(); - Kokkos::initialize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::initialize\\(\\) must not be called before " - "initializing Kokkos\\."); - - // KokkosFFT is initialized after Kokkos finalization - EXPECT_DEATH( - { - Kokkos::initialize(); - Kokkos::finalize(); - KokkosFFT::initialize(); - }, - "Error: KokkosFFT::initialize\\(\\) must not be called after finalizing " - "Kokkos\\."); -} - -TEST_F(InitializeFinalize_DeathTest, finalize) { - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // KokkosFFT is not initialized - EXPECT_DEATH( - { - Kokkos::initialize(); - KokkosFFT::finalize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::finalize\\(\\) may only be called after KokkosFFT has " - "been initialized\\."); - - // KokkosFFT is finalized twice - EXPECT_DEATH( - { - Kokkos::initialize(); - KokkosFFT::initialize(); - KokkosFFT::finalize(); - KokkosFFT::finalize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::finalize\\(\\) has already been called\\."); - - // KokkosFFT is finalized before Kokkos initialization - EXPECT_DEATH( - { - KokkosFFT::finalize(); - Kokkos::initialize(); - Kokkos::finalize(); - }, - "Error: KokkosFFT::finalize\\(\\) may only be called after Kokkos has " - "been initialized\\."); - - // KokkosFFT is finalized after Kokkos finalization - EXPECT_DEATH( - { - Kokkos::initialize(); - KokkosFFT::initialize(); - Kokkos::finalize(); - KokkosFFT::finalize(); - }, - "Error: KokkosFFT::finalize\\(\\) must be called before finalizing " - "Kokkos\\."); -} - -TEST_F(InitializeFinalize_Test, is_initialized) { - EXPECT_EXIT( - { - Kokkos::initialize(); - bool success = true; - success &= !KokkosFFT::is_initialized(); - std::cout << "success" << success << std::endl; - KokkosFFT::initialize(); - success &= KokkosFFT::is_initialized(); - std::cout << "success" << success << std::endl; - KokkosFFT::finalize(); - success &= !KokkosFFT::is_initialized(); - std::cout << "success" << success << std::endl; - Kokkos::finalize(); - std::exit(success ? EXIT_SUCCESS : EXIT_FAILURE); - }, - ::testing::ExitedWithCode(EXIT_SUCCESS), ""); -} - -TEST_F(InitializeFinalize_Test, is_finalized) { - EXPECT_EXIT( - { - Kokkos::initialize(); - bool success = true; - success &= !KokkosFFT::is_finalized(); - KokkosFFT::initialize(); - success &= !KokkosFFT::is_finalized(); - KokkosFFT::finalize(); - success &= KokkosFFT::is_finalized(); - Kokkos::finalize(); - std::exit(success ? EXIT_SUCCESS : EXIT_FAILURE); - }, - ::testing::ExitedWithCode(EXIT_SUCCESS), ""); -} - -} // namespace diff --git a/fft/unit_test/Test_Main.cpp b/fft/unit_test/Test_Main.cpp index b26aba88..6b6fda88 100644 --- a/fft/unit_test/Test_Main.cpp +++ b/fft/unit_test/Test_Main.cpp @@ -5,16 +5,13 @@ #include #include -#include int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); int result = 0; Kokkos::initialize(argc, argv); - KokkosFFT::initialize(); result = RUN_ALL_TESTS(); - KokkosFFT::finalize(); Kokkos::finalize(); return result; diff --git a/fft/unit_test/Test_MainNoInit.cpp b/fft/unit_test/Test_MainNoInit.cpp deleted file mode 100644 index 8e9bb64c..00000000 --- a/fft/unit_test/Test_MainNoInit.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-FileCopyrightText: (C) 2022 National Technology & Engineering Solutions of Sandia, LLC (NTESS). -// SPDX-FileCopyrightText: Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights in this software. -// -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -int main(int argc, char* argv[]) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}