Files
project_6/cccl_upstream/cub/test/catch2_test_util_device.cu
EngineX CI 56fd68e7dd [INFRA] Import NVIDIA/CCCL upstream as optimization reference library
CCCL (CUDA C++ Core Libraries) provides:
- CUB: device/block/warp-level GPU primitives (reduce, scan, sort, topk)
- Thrust: high-level parallel algorithms (transform_reduce, sort, scan)
- libcudacxx: CUDA C++ standard library (atomics, barriers, memory)
- cudax: experimental features (memory resources, allocators)
- Tuning policies: per-SM hardware-specific algorithm parameters

Competition optimization vectors mapped to CCCL:
- Output TPS (83% weight): warp_reduce, block_reduce, device_topk
- Input TPS (14% weight): device_scan, block_load, prefetch
- Cache TPS (3% weight): prefix caching strategy patterns
- Memory (0.9 util): pooled/cached/buddy allocators

Source: https://github.com/NVIDIA/cccl (shallow clone, HEAD only)
License: Apache-2.0
2026-07-30 09:35:51 +00:00

318 lines
9.5 KiB
Plaintext

// SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
// SPDX-License-Identifier: BSD-3
#include <cub/util_arch.cuh>
#include <cub/util_device.cuh>
#include <cub/util_macro.cuh>
#include <thrust/detail/raw_pointer_cast.h>
#include <thrust/device_vector.h>
#include <cuda/devices>
#include <cuda/std/__algorithm/find_if.h>
#include <cuda/std/array>
#include "catch2_test_launch_helper.h"
#include <c2h/catch2_test_helper.h>
CUB_NAMESPACE_BEGIN
_CCCL_KERNEL_ATTRIBUTES void write_ptx_version_kernel(int* d_kernel_cuda_cc)
{
*d_kernel_cuda_cc = CUB_PTX_ARCH;
}
CUB_RUNTIME_FUNCTION static cudaError_t get_cuda_cc_from_kernel(
void* d_temp_storage,
size_t& temp_storage_bytes,
int* d_kernel_cuda_cc,
int* ptx_version,
cudaStream_t stream = nullptr)
{
if (d_temp_storage == nullptr)
{
temp_storage_bytes = 1;
return cudaSuccess;
}
write_ptx_version_kernel<<<1, 1, 0, stream>>>(d_kernel_cuda_cc);
return cub::PtxVersion(*ptx_version);
}
CUB_NAMESPACE_END
// %PARAM% TEST_LAUNCH lid 0:1:2
DECLARE_LAUNCH_WRAPPER(cub::get_cuda_cc_from_kernel, get_cuda_cc_from_kernel);
C2H_TEST("CUB correctly identifies the ptx version the kernel was compiled for", "[util][dispatch]")
{
constexpr std::size_t single_item = 1;
c2h::device_vector<int> cuda_cc(single_item);
// Query the arch the kernel was actually compiled for
int ptx_version = [&]() -> int {
int* buffer{};
cudaMallocHost(&buffer, sizeof(*buffer));
get_cuda_cc_from_kernel(thrust::raw_pointer_cast(cuda_cc.data()), buffer);
int result = *buffer;
cudaFreeHost(buffer);
return result;
}();
int kernel_cuda_cc = cuda_cc[0];
// Host cub::PtxVersion
int host_ptx_version{};
cub::PtxVersion(host_ptx_version);
// Ensure variable was properly populated
REQUIRE(0 != kernel_cuda_cc);
// Ensure that the ptx version corresponds to the cc the kernel was compiled for
REQUIRE(ptx_version == kernel_cuda_cc);
REQUIRE(host_ptx_version == kernel_cuda_cc);
}
#ifdef __CUDA_ARCH_LIST__
# define CUDA_SM_LIST __CUDA_ARCH_LIST__
# define CUDA_SM_LIST_SCALE 1
#elif defined(NV_TARGET_SM_INTEGER_LIST)
# define CUDA_SM_LIST NV_TARGET_SM_INTEGER_LIST
# define CUDA_SM_LIST_SCALE 10
#endif
#ifdef CUDA_SM_LIST
C2H_TEST("PtxVersion returns a value from __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST", "[util][dispatch]")
{
int ptx_version = 0;
REQUIRE(cub::PtxVersion(ptx_version) == cudaSuccess);
auto cc_list = std::vector<int>{CUDA_SM_LIST};
for (auto& cc : cc_list)
{
cc *= CUDA_SM_LIST_SCALE;
}
REQUIRE(std::find(cc_list.begin(), cc_list.end(), ptx_version) != cc_list.end());
}
#endif
#define GEN_POLICY(cur, prev) \
struct policy##cur : cub::detail::chained_policy<cur, policy##cur, policy##prev> \
{ \
static constexpr int value = cur; \
}
#ifdef CUDA_SM_LIST
// We list policies for all virtual architectures that __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST can contain, so the
// actual architectures the tests are compiled for should match to one of those
struct policy_hub_all
{
// for the list of supported architectures,
// see libcudacxx/include/nv/target or libcudacxx/include/cuda/__device/arch_id.h
GEN_POLICY(500, 500);
GEN_POLICY(520, 500);
GEN_POLICY(530, 520);
GEN_POLICY(600, 530);
GEN_POLICY(610, 600);
GEN_POLICY(620, 610);
GEN_POLICY(700, 620);
GEN_POLICY(720, 700);
GEN_POLICY(750, 720);
GEN_POLICY(800, 750);
GEN_POLICY(860, 800);
GEN_POLICY(870, 860);
GEN_POLICY(880, 870);
GEN_POLICY(890, 880);
GEN_POLICY(900, 890);
GEN_POLICY(1000, 900);
GEN_POLICY(1010, 1000);
GEN_POLICY(1030, 1010);
GEN_POLICY(1070, 1030);
GEN_POLICY(1100, 1070);
GEN_POLICY(1200, 1100);
GEN_POLICY(1210, 1200);
// add more policies here when new architectures emerge
GEN_POLICY(2000, 1210); // non-existing architecture, just to test pruning
using max_policy = policy2000;
};
// check that the selected policy exactly matches one of (scaled) arches we compile for
template <int SelectedPolicyCc, int... CcList>
struct check
{
static_assert(((SelectedPolicyCc == CcList * CUDA_SM_LIST_SCALE) || ...));
using type = cudaError_t;
};
struct closure_all
{
int ptx_version;
template <typename ActivePolicy>
CUB_RUNTIME_FUNCTION auto Invoke() const -> typename check<ActivePolicy::value, CUDA_SM_LIST>::type
{
// since policy_hub_all lists all PTX virtual architectures, we can do an exact comparison here
# if TEST_LAUNCH == 0
REQUIRE(+ActivePolicy::value == ptx_version);
# endif // TEST_LAUNCH == 0
// the returned error code will be checked by the launch helper
return +ActivePolicy::value == ptx_version ? cudaSuccess : cudaErrorInvalidValue;
}
};
CUB_RUNTIME_FUNCTION cudaError_t
check_chained_policy_prunes_to_cc_list(void* d_temp_storage, size_t& temp_storage_bytes, cudaStream_t = 0)
{
if (d_temp_storage == nullptr)
{
temp_storage_bytes = 1;
return cudaSuccess;
}
int ptx_version = 0;
cub::PtxVersion(ptx_version);
closure_all c{ptx_version};
return policy_hub_all::max_policy::Invoke(ptx_version, c);
}
DECLARE_LAUNCH_WRAPPER(check_chained_policy_prunes_to_cc_list, check_wrapper_all);
// TODO(bgruber): drop in CCCL 4.0
C2H_TEST("ChainedPolicy prunes based on __CUDA_ARCH_LIST__/NV_TARGET_SM_INTEGER_LIST", "[util][dispatch]")
{
check_wrapper_all();
}
#endif
template <int NumPolicies>
struct check_policy_closure
{
int ptx_version;
cuda::std::array<int, NumPolicies> policies;
// quick way to get a comparator for find_if below
__host__ __device__ bool operator()(int policy_ver) const
{
return policy_ver <= ptx_version;
}
template <typename ActivePolicy>
CUB_RUNTIME_FUNCTION cudaError_t Invoke() const
{
#define CHECK_EXPR +ActivePolicy::value == *cuda::std::find_if(policies.rbegin(), policies.rend(), *this)
#if TEST_LAUNCH == 0
CAPTURE(ptx_version, policies);
REQUIRE(CHECK_EXPR);
#else // TEST_LAUNCH == 0
if (!(CHECK_EXPR))
{
printf("Check `%s` failed!\n ptx_version=%d\n ActivePolicy::value=%d\n policies=",
THRUST_PP_STRINGIZE(CHECK_EXPR),
ptx_version,
ActivePolicy::value);
for (int i = 0; i < NumPolicies; i++)
{
printf("%d,", policies[i]);
}
printf("\n");
}
#endif // TEST_LAUNCH == 0
// the returned error code will be checked by the launch helper
return (CHECK_EXPR) ? cudaSuccess : cudaErrorInvalidValue;
#undef CHECK_EXPR
}
};
template <typename PolicyHub, int NumPolicies>
CUB_RUNTIME_FUNCTION cudaError_t check_chained_policy_selects_correct_policy(
void* d_temp_storage, size_t& temp_storage_bytes, cuda::std::array<int, NumPolicies> policies, cudaStream_t = nullptr)
{
if (d_temp_storage == nullptr)
{
temp_storage_bytes = 1;
return cudaSuccess;
}
int ptx_version = 0;
cub::PtxVersion(ptx_version);
check_policy_closure<NumPolicies> c{ptx_version, std::move(policies)};
return PolicyHub::max_policy::Invoke(ptx_version, c);
}
DECLARE_TMPL_LAUNCH_WRAPPER(check_chained_policy_selects_correct_policy,
check_wrapper_some,
ESCAPE_LIST(typename PolicyHub, int NumPolicies),
ESCAPE_LIST(PolicyHub, NumPolicies));
struct policy_hub_some
{
GEN_POLICY(500, 500);
GEN_POLICY(700, 500);
GEN_POLICY(900, 700);
GEN_POLICY(2000, 900); // non-existing architecture, just to test
using max_policy = policy2000;
};
struct policy_hub_few
{
GEN_POLICY(500, 500);
GEN_POLICY(860, 500);
GEN_POLICY(2000, 860); // non-existing architecture, just to test
using max_policy = policy2000;
};
struct policy_hub_minimal
{
GEN_POLICY(500, 500);
using max_policy = policy500;
};
// TODO(bgruber): drop in CCCL 4.0
C2H_TEST("ChainedPolicy invokes correct policy", "[util][dispatch]")
{
SECTION("policy_hub_some")
{
check_wrapper_some<policy_hub_some, 4>(cuda::std::array<int, 4>{500, 700, 900, 2000});
}
SECTION("policy_hub_few")
{
check_wrapper_some<policy_hub_few, 3>(cuda::std::array<int, 3>{500, 860, 2000});
}
SECTION("policy_hub_minimal")
{
check_wrapper_some<policy_hub_minimal, 1>(cuda::std::array<int, 1>{500});
}
}
__global__ void test_max_potential_dynamic_smem_bytes_kernel()
{
// use inline PTX so the variable doesn't get optimized out
asm volatile(".shared .align 1 .b8 static_smem[4096];");
}
#if defined(CUB_RDC_ENABLED)
__global__ void test_max_potential_dynamic_smem_bytes_device(int* result)
{
// Just compile on device.
cub::MaxPotentialDynamicSmemBytes(*result, test_max_potential_dynamic_smem_bytes_kernel);
}
#endif // CUB_RDC_ENABLED
C2H_TEST("MaxPotentialDynamicSmemBytes", "[util][launch]")
{
cuda::device_ref device{0};
// Calculate the expected max potential dynamic shared memory size.
const auto max_smem_per_block_optin = device.attribute(cuda::device_attributes::max_shared_memory_per_block_optin);
const auto reserved_smem_per_block = device.attribute(cuda::device_attributes::reserved_shared_memory_per_block);
const auto expected = static_cast<int>(max_smem_per_block_optin - reserved_smem_per_block - 4096);
// 1. Test positive case.
int dyn_smem_size{};
REQUIRE(
cub::MaxPotentialDynamicSmemBytes(dyn_smem_size, test_max_potential_dynamic_smem_bytes_kernel) == cudaSuccess);
REQUIRE(dyn_smem_size == expected);
// 2. Test that we return -1 if an error occurs.
REQUIRE(cub::MaxPotentialDynamicSmemBytes(dyn_smem_size, nullptr) != cudaSuccess);
REQUIRE(dyn_smem_size == -1);
}