// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. // SPDX-License-Identifier: BSD-3 #pragma once #include #include #include #include #include #include #include #include #include template struct exchange_data_t; template struct exchange_data_t>> { InputT input[ItemsPerThread]; OutputT (&output)[ItemsPerThread] = input; template inline __device__ void scatter(cub::WarpExchange& exchange, int (&ranks)[ItemsPerThread]) { exchange.ScatterToStriped(input, ranks); } }; template struct exchange_data_t>> { InputT input[ItemsPerThread]; OutputT output[ItemsPerThread]; template inline __device__ void scatter(cub::WarpExchange& exchange, int (&ranks)[ItemsPerThread]) { exchange.ScatterToStriped(input, output, ranks); } }; template __global__ void scatter_kernel(const InputT* input_data, OutputT* output_data) { using warp_exchange_t = cub::WarpExchange; using storage_t = typename warp_exchange_t::TempStorage; constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS; __shared__ storage_t temp_storage[TOTAL_WARPS]; const int tid = cub::RowMajorTid(static_cast(blockDim.x), static_cast(blockDim.y), static_cast(blockDim.z)); // Get warp index const int warp_id = tid / LOGICAL_WARP_THREADS; const int lane_id = tid % LOGICAL_WARP_THREADS; warp_exchange_t exchange(temp_storage[warp_id]); exchange_data_t exchange_data; // Reverse data int ranks[ITEMS_PER_THREAD]; input_data += warp_id * tile_size; output_data += warp_id * tile_size; for (int item = 0; item < ITEMS_PER_THREAD; item++) { const auto item_idx = lane_id * ITEMS_PER_THREAD + item; exchange_data.input[item] = input_data[item_idx]; ranks[item] = tile_size - 1 - item_idx; } exchange_data.scatter(exchange, ranks); // Striped to blocked for (int item = 0; item < ITEMS_PER_THREAD; item++) { output_data[item * LOGICAL_WARP_THREADS + lane_id] = exchange_data.output[item]; } } template void warp_scatter_strided(c2h::device_vector& in, c2h::device_vector& out) { scatter_kernel <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>( thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data())); REQUIRE(cudaSuccess == cudaPeekAtLastError()); REQUIRE(cudaSuccess == cudaDeviceSynchronize()); } template __global__ void kernel(const InputT* input_data, OutputT* output_data, ActionT action) { using warp_exchange_t = cub::WarpExchange; using storage_t = typename warp_exchange_t::TempStorage; constexpr int tile_size = ITEMS_PER_THREAD * LOGICAL_WARP_THREADS; __shared__ storage_t temp_storage[TOTAL_WARPS]; const int tid = cub::RowMajorTid(static_cast(blockDim.x), static_cast(blockDim.y), static_cast(blockDim.z)); // Get warp index const int warp_id = tid / LOGICAL_WARP_THREADS; const int lane_id = tid % LOGICAL_WARP_THREADS; warp_exchange_t exchange(temp_storage[warp_id]); exchange_data_t exchange_data; input_data += warp_id * tile_size; output_data += warp_id * tile_size; for (int item = 0; item < ITEMS_PER_THREAD; item++) { exchange_data.input[item] = input_data[lane_id * ITEMS_PER_THREAD + item]; } action(exchange_data.input, exchange_data.output, exchange); for (int item = 0; item < ITEMS_PER_THREAD; item++) { output_data[lane_id * ITEMS_PER_THREAD + item] = exchange_data.output[item]; } } template void warp_exchange(c2h::device_vector& in, c2h::device_vector& out, ActionT action) { kernel <<<1, LOGICAL_WARP_THREADS * TOTAL_WARPS>>>( thrust::raw_pointer_cast(in.data()), thrust::raw_pointer_cast(out.data()), action); REQUIRE(cudaSuccess == cudaPeekAtLastError()); REQUIRE(cudaSuccess == cudaDeviceSynchronize()); } struct blocked_to_striped { template __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD], OutputT (&output)[ITEMS_PER_THREAD], cub::WarpExchange& exchange) { exchange.BlockedToStriped(input, output); } }; struct striped_to_blocked { template __device__ void operator()(InputT (&input)[ITEMS_PER_THREAD], OutputT (&output)[ITEMS_PER_THREAD], cub::WarpExchange& exchange) { exchange.StripedToBlocked(input, output); } }; template c2h::host_vector compute_host_reference(const c2h::device_vector& d_input, int tile_size) { c2h::host_vector input = d_input; int num_warps = cuda::ceil_div(static_cast(d_input.size()), tile_size); for (int warp_id = 0; warp_id < num_warps; warp_id++) { const int warp_data_begin = tile_size * warp_id; const int warp_data_end = warp_data_begin + tile_size; thrust::reverse(input.begin() + warp_data_begin, input.begin() + warp_data_end); } return input; } template struct total_warps_t { private: static constexpr int max_warps = 2; static constexpr bool is_arch_warp = (logical_warp_threads == cub::detail::warp_threads); static constexpr bool is_pow_of_two = ((logical_warp_threads & (logical_warp_threads - 1)) == 0); static constexpr int total_warps = (is_arch_warp || is_pow_of_two) ? max_warps : 1; public: static constexpr int value() { return total_warps; } };