// Copyright (c) 2023-2024 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "rng/common.hpp" #include "rng/config_types.hpp" #include "test_common.hpp" #include __global__ void write_target_arch([[maybe_unused]] rocrand_impl::host::target_arch host_arch, int* __restrict__ result) { #if !defined(__SPIRV__) constexpr auto arch = rocrand_impl::host::get_device_arch(); *result = arch == host_arch; #else *result = -1; #endif } static constexpr rocrand_rng_type dummy_rng_type = rocrand_rng_type(0); namespace rocrand_impl::host { template struct generator_config_defaults { static constexpr inline unsigned int threads = 256; static constexpr inline unsigned int blocks = 1; }; template<> struct generator_config_defaults { static constexpr inline unsigned int threads = 512; static constexpr inline unsigned int blocks = 2; }; template<> struct generator_config_defaults { static constexpr inline unsigned int threads = 512; static constexpr inline unsigned int blocks = 7; }; } // namespace rocrand_impl::host template::template device_config(true) .threads> __global__ __launch_bounds__(BlockSize) void write_config(unsigned int* block_size, unsigned int* grid_size) { if(blockIdx.x == 0 && threadIdx.x == 0 && BlockSize == blockDim.x) { *block_size = blockDim.x; *grid_size = gridDim.x; } } TEST(rocrand_config_dispatch_tests, host_matches_device) { const hipStream_t stream = 0; rocrand_impl::host::target_arch host_arch; HIP_CHECK(rocrand_impl::host::get_device_arch(stream, host_arch)); int* result_ptr = nullptr; HIP_CHECK(hipMallocHelper(&result_ptr, sizeof(result_ptr))); hipLaunchKernelGGL(write_target_arch, dim3(1), dim3(1), 0, stream, host_arch, result_ptr); HIP_CHECK(hipGetLastError()); int result = -1; HIP_CHECK(hipMemcpy(&result, result_ptr, sizeof(result), hipMemcpyDeviceToHost)); if(result != -1) { ASSERT_NE(host_arch, rocrand_impl::host::target_arch::invalid); ASSERT_EQ(result, 1); } else { GTEST_SKIP() << "SPIR-V build: result is null; skipping arch match assertion."; } HIP_CHECK(hipFree(result_ptr)); } TEST(rocrand_config_dispatch_tests, parse_common_architectures) { using rocrand_impl::host::parse_gcn_arch; using rocrand_impl::host::target_arch; ASSERT_EQ(parse_gcn_arch(""), target_arch::unknown); ASSERT_EQ(parse_gcn_arch("not a gfx arch"), target_arch::unknown); ASSERT_EQ(parse_gcn_arch(":"), target_arch::unknown); ASSERT_EQ(parse_gcn_arch("g:"), target_arch::unknown); ASSERT_EQ(parse_gcn_arch("gfx900:sramecc+"), target_arch::gfx900); ASSERT_EQ(parse_gcn_arch("gfx906:::"), target_arch::gfx906); ASSERT_EQ(parse_gcn_arch("gfx908:"), target_arch::gfx908); ASSERT_EQ(parse_gcn_arch("gfx90a:sramecc+:xnack-"), target_arch::gfx90a); } TEST(rocrand_config_dispatch_tests, get_config_on_host_and_device) { using T = unsigned int; const hipStream_t stream = 0; unsigned int *d_block_size{}, *d_grid_size{}; HIP_CHECK(hipMallocHelper(&d_block_size, sizeof(*d_block_size))); HIP_CHECK(hipMallocHelper(&d_grid_size, sizeof(*d_grid_size))); rocrand_impl::host::generator_config config{}; const hipError_t err = rocrand_impl::host::get_generator_config( stream, ROCRAND_ORDERING_PSEUDO_DEFAULT, config); HIP_CHECK(err); hipLaunchKernelGGL(write_config, dim3(config.blocks), dim3(config.threads), 0, stream, d_block_size, d_grid_size); HIP_CHECK(hipGetLastError()); unsigned int block_size{}, grid_size{}; HIP_CHECK(hipMemcpy(&block_size, d_block_size, sizeof(block_size), hipMemcpyDeviceToHost)); HIP_CHECK(hipMemcpy(&grid_size, d_grid_size, sizeof(grid_size), hipMemcpyDeviceToHost)); HIP_CHECK(hipFree(d_block_size)); HIP_CHECK(hipFree(d_grid_size)); ASSERT_EQ(block_size, config.threads); ASSERT_EQ(grid_size, config.blocks); } #if USE_DEVICE_DISPATCH TEST(rocrand_config_dispatch_tests, device_id_from_stream) { using rocrand_impl::host::get_device_from_stream; hipDevice_t device_id; HIP_CHECK(hipGetDevice(&device_id)); int result; static constexpr hipStream_t default_stream = 0; HIP_CHECK(get_device_from_stream(default_stream, result)); ASSERT_EQ(result, device_id); HIP_CHECK(get_device_from_stream(hipStreamPerThread, result)); ASSERT_EQ(result, device_id); hipStream_t stream; HIP_CHECK(hipStreamCreate(&stream)); HIP_CHECK(get_device_from_stream(stream, result)); HIP_CHECK(hipStreamDestroy(stream)); ASSERT_EQ(result, device_id); HIP_CHECK(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking)); HIP_CHECK(get_device_from_stream(stream, result)); HIP_CHECK(hipStreamDestroy(stream)); ASSERT_EQ(result, device_id); } template __global__ void least_common_grid_size_kernel(unsigned int* least_common_grid_size, rocrand_ordering order) { *least_common_grid_size = rocrand_impl::host::get_least_common_grid_size( rocrand_impl::host::is_ordering_dynamic(order)); } TEST(rocrand_config_dispatch_tests, default_config_provider) { using config_provider = rocrand_impl::host::default_config_provider; static constexpr hipStream_t default_stream = 0; static constexpr rocrand_ordering ordering = ROCRAND_ORDERING_PSEUDO_DEFAULT; rocrand_impl::host::generator_config config{}; ASSERT_EQ(config_provider::host_config(default_stream, ordering, config), hipSuccess); ASSERT_EQ(config.blocks, 1); ASSERT_EQ(config.threads, 256); config = {}; ASSERT_EQ(config_provider::host_config(default_stream, ordering, config), hipSuccess); ASSERT_EQ(config.blocks, 2); ASSERT_EQ(config.threads, 512); unsigned int least_common_grid_size{}; ASSERT_EQ( rocrand_impl::host::get_least_common_grid_size(default_stream, ordering, least_common_grid_size), hipSuccess); ASSERT_EQ(least_common_grid_size, 512 * 2 * 7); unsigned int* d_least_common_grid_size{}; HIP_CHECK(hipMalloc(&d_least_common_grid_size, sizeof(*d_least_common_grid_size))); hipLaunchKernelGGL(HIP_KERNEL_NAME(least_common_grid_size_kernel), 1, 1, 0, default_stream, d_least_common_grid_size, ordering); unsigned int h_least_common_grid_size{}; HIP_CHECK(hipMemcpy(&h_least_common_grid_size, d_least_common_grid_size, sizeof(h_least_common_grid_size), hipMemcpyDeviceToHost)); HIP_CHECK(hipFree(d_least_common_grid_size)); ASSERT_EQ(least_common_grid_size, h_least_common_grid_size); } template __global__ void config_selector_kernel(unsigned int* output) { if(threadIdx.x == 0 && blockIdx.x == 0) { output[0] = ConfigProvider::template device_config(true).blocks; output[1] = ConfigProvider::template device_config(true).threads; } } namespace rocrand_impl::host { template<> struct generator_config_selector { __host__ __device__ static constexpr unsigned int get_threads(const target_arch arch) { if(arch == target_arch::gfx906) return 64; return generator_config_defaults::threads; } __host__ __device__ static constexpr unsigned int get_blocks(const target_arch /*arch*/) { return generator_config_defaults::blocks; } }; } // namespace rocrand_impl::host TEST(rocrand_config_dispatch_tests, config_selection) { unsigned int* d_output{}; constexpr std::size_t size = 2; HIP_CHECK(hipMallocHelper(&d_output, size * sizeof(*d_output))); using config_provider_t = rocrand_impl::host::default_config_provider; config_provider_t config_provider{}; rocrand_impl::host::generator_config config{}; static constexpr hipStream_t default_stream = 0; static constexpr rocrand_ordering ordering = ROCRAND_ORDERING_PSEUDO_DYNAMIC; HIP_CHECK(config_provider.host_config(default_stream, ordering, config)); hipLaunchKernelGGL(HIP_KERNEL_NAME(config_selector_kernel), config.blocks, config.threads, 0, default_stream, d_output); HIP_CHECK(hipGetLastError()); std::array h_output{}; HIP_CHECK( hipMemcpy(h_output.data(), d_output, size * sizeof(*d_output), hipMemcpyDeviceToHost)); HIP_CHECK(hipFree(d_output)); ASSERT_EQ(config.blocks, h_output[0]); ASSERT_EQ(config.threads, h_output[1]); } #endif // USE_DEVICE_DISPATCH