// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved. // SPDX-License-Identifier: BSD-3 // Ensure printing of CUDA runtime errors to console #include "cub/util_type.cuh" #define CUB_STDERR #include #include #include #include #include #include #include #include #include #include #include "test_util.h" bool g_verbose = false; cub::CachingDeviceAllocator g_allocator(true); template __launch_bounds__(ThreadsPerBlock, 1) __global__ void kernel(Key* d_keys, int* d_ranks) { using block_radix_rank = cub::detail::block_radix_rank_t; using storage_t = typename block_radix_rank::TempStorage; // Allocate temp storage in shared memory __shared__ storage_t temp_storage; // Items per thread Key keys[ItemsPerThread]; int ranks[ItemsPerThread]; constexpr bool uses_warp_striped_arrangement = RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH || RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ANY || RankAlgorithm == cub::RadixRankAlgorithm::RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR; if (uses_warp_striped_arrangement) { cub::LoadDirectWarpStriped(threadIdx.x, d_keys, keys); } else { cub::LoadDirectBlocked(threadIdx.x, d_keys, keys); } cub::BFEDigitExtractor extractor(0, RadixBits); block_radix_rank(temp_storage).RankKeys(keys, ranks, extractor); if (uses_warp_striped_arrangement) { cub::StoreDirectWarpStriped(threadIdx.x, d_ranks, ranks); } else { cub::StoreDirectBlocked(threadIdx.x, d_ranks, ranks); } } //--------------------------------------------------------------------- // Host testing subroutines //--------------------------------------------------------------------- /** * Simple key-value pairing */ template struct pair_t { Key key; int value; bool operator<(const pair_t& b) const { return (key < b.key); } }; template void Initialize(GenMode gen_mode, Key* h_keys, int* h_reference_ranks, int num_items, int num_bits) { std::unique_ptr[]> h_pairs_storage(new pair_t[num_items]); pair_t* h_pairs = h_pairs_storage.get(); for (int i = 0; i < num_items; ++i) { InitValue(gen_mode, h_keys[i], i); // Mask off unwanted portions std::uint64_t base = 0; memcpy(&base, &h_keys[i], sizeof(Key)); base &= (1ull << num_bits) - 1; memcpy(&h_keys[i], &base, sizeof(Key)); h_pairs[i].key = h_keys[i]; h_pairs[i].value = i; } if (DESCENDING) { std::reverse(h_pairs, h_pairs + num_items); } std::stable_sort(h_pairs, h_pairs + num_items); if (DESCENDING) { std::reverse(h_pairs, h_pairs + num_items); } for (int i = 0; i < num_items; ++i) { h_reference_ranks[h_pairs[i].value] = i; } } template void TestDriver(GenMode gen_mode) { constexpr int tile_size = ThreadsPerBlock * ItemsPerThread; // Allocate host arrays std::unique_ptr h_keys(new Key[tile_size]); std::unique_ptr h_ranks(new int[tile_size]); std::unique_ptr h_reference_ranks(new int[tile_size]); // Allocate device arrays Key* d_keys = nullptr; int* d_ranks = nullptr; CubDebugExit(g_allocator.DeviceAllocate((void**) &d_keys, sizeof(Key) * tile_size)); CubDebugExit(g_allocator.DeviceAllocate((void**) &d_ranks, sizeof(int) * tile_size)); // Initialize problem and solution on host Initialize(gen_mode, h_keys.get(), h_reference_ranks.get(), tile_size, RadixBits); // Copy problem to device CubDebugExit(cudaMemcpy(d_keys, h_keys.get(), sizeof(Key) * tile_size, cudaMemcpyHostToDevice)); // Run kernel kernel <<<1, ThreadsPerBlock>>>(d_keys, d_ranks); // Flush kernel output / errors CubDebugExit(cudaPeekAtLastError()); CubDebugExit(cudaDeviceSynchronize()); // Check keys results const bool compare = CompareDeviceResults(h_reference_ranks.get(), d_ranks, tile_size, g_verbose, g_verbose); AssertEquals(0, compare); if (d_keys) { CubDebugExit(g_allocator.DeviceFree(d_keys)); } if (d_ranks) { CubDebugExit(g_allocator.DeviceFree(d_ranks)); } } template void TestValid(cuda::std::true_type /*fits_smem_capacity*/) { TestDriver(UNIFORM); TestDriver(INTEGER_SEED); } template void TestValid(cuda::std::false_type fits_smem_capacity) {} template void Test() { // Check size of smem storage for the target arch to make sure it will fit using block_radix_rank = cub::detail::block_radix_rank_t; using storage_t = typename block_radix_rank::TempStorage; cuda::std::bool_constant<(sizeof(storage_t) <= cub::detail::max_smem_per_block)> fits_smem_capacity; TestValid( fits_smem_capacity); } template void Test() { Test(); Test(); } template void Test() { Test(); Test(); } template void Test() { Test(); Test(); } template void Test() { Test(); Test(); } template void Test() { Test(); Test(); } template void Test(cuda::std::true_type /* multiple of hw warp */) { Test(); // TODO(senior-zero): // - RADIX_RANK_MATCH_EARLY_COUNTS_ANY // - RADIX_RANK_MATCH_EARLY_COUNTS_ATOMIC_OR } template void Test(cuda::std::false_type /* multiple of hw warp */) {} template void Test() { Test(); Test(); Test(cuda::std::bool_constant < (ThreadsPerBlock % 32) == 0 > {}); } int main(int argc, char** argv) { // Initialize command line CommandLineArgs args(argc, argv); g_verbose = args.CheckCmdLineFlag("v"); // Print usage if (args.CheckCmdLineFlag("help")) { printf("%s " "[--device=] " "[--v] " "\n", argv[0]); exit(0); } // Initialize device CubDebugExit(args.DeviceInit()); Test<16>(); Test<32>(); Test<128>(); Test<130>(); g_allocator.FreeAllCached(); return 0; }