Files
project_6/cccl_upstream/thrust/testing/unittest/testframework.h
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

643 lines
23 KiB
C++

#pragma once
#include <thrust/detail/config.h>
#include <thrust/mr/allocator.h>
#include <thrust/mr/device_memory_resource.h>
#include <thrust/mr/host_memory_resource.h>
#include <thrust/mr/universal_memory_resource.h>
#include <cuda/std/limits>
#include <cstdio>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <string>
#include <type_traits>
#include <vector>
#include "meta.h"
#include "util.h"
// define some common lists of types
using ThirtyTwoBitTypes = unittest::type_list<int, unsigned int, float>;
using SixtyFourBitTypes = unittest::type_list<long long, unsigned long long, double>;
using IntegralTypes = unittest::type_list<
char,
signed char,
unsigned char,
short,
unsigned short,
int,
unsigned int,
long,
unsigned long,
long long,
unsigned long long>;
using SignedIntegralTypes = unittest::type_list<signed char, short, int, long, long long>;
using UnsignedIntegralTypes =
unittest::type_list<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long>;
using ByteTypes = unittest::type_list<char, signed char, unsigned char>;
using SmallIntegralTypes = unittest::type_list<char, signed char, unsigned char, short, unsigned short>;
using LargeIntegralTypes = unittest::type_list<long long, unsigned long long>;
using FloatingPointTypes = unittest::type_list<float, double>;
// A type that behaves as if it was a normal numeric type,
// so it can be used in the same tests as "normal" numeric types.
// NOTE: This is explicitly NOT proclaimed trivially reloctable.
class custom_numeric
{
public:
_CCCL_HOST_DEVICE constexpr custom_numeric()
{
fill(0);
}
// Allow construction from any integral numeric.
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
_CCCL_HOST_DEVICE constexpr custom_numeric(const T& i)
{
fill(static_cast<int>(i));
}
_CCCL_HOST_DEVICE constexpr custom_numeric(const custom_numeric& other)
{
fill(other.value[0]);
}
_CCCL_HOST_DEVICE constexpr custom_numeric& operator=(int val)
{
fill(val);
return *this;
}
_CCCL_HOST_DEVICE constexpr custom_numeric& operator=(const custom_numeric& other)
{
if (this != &other)
{
fill(other.value[0]);
}
return *this;
}
// cast to void * instead of bool to fool overload resolution
// WTB C++11 explicit conversion operators
_CCCL_HOST_DEVICE operator void*() const
{
// static cast first to avoid MSVC warning C4312
return reinterpret_cast<void*>(static_cast<std::size_t>(value[0])); // NOLINT(performance-no-int-to-ptr)
}
#define DEFINE_OPERATOR(op) \
_CCCL_HOST_DEVICE constexpr custom_numeric& operator op() \
{ \
fill(op value[0]); \
return *this; \
} \
_CCCL_HOST_DEVICE constexpr custom_numeric operator op(int) const \
{ \
custom_numeric ret(*this); \
op ret; \
return ret; \
}
DEFINE_OPERATOR(++)
DEFINE_OPERATOR(--)
#undef DEFINE_OPERATOR
#define DEFINE_OPERATOR(op) \
_CCCL_HOST_DEVICE constexpr custom_numeric operator op() const \
{ \
return custom_numeric(op value[0]); \
}
DEFINE_OPERATOR(+)
DEFINE_OPERATOR(-)
DEFINE_OPERATOR(~)
#undef DEFINE_OPERATOR
#define DEFINE_OPERATOR(op) \
_CCCL_HOST_DEVICE constexpr custom_numeric operator op(const custom_numeric& other) const \
{ \
return custom_numeric(value[0] op other.value[0]); \
}
DEFINE_OPERATOR(+)
DEFINE_OPERATOR(-)
DEFINE_OPERATOR(*)
DEFINE_OPERATOR(/)
DEFINE_OPERATOR(%)
DEFINE_OPERATOR(<<)
DEFINE_OPERATOR(>>)
DEFINE_OPERATOR(&)
DEFINE_OPERATOR(|)
DEFINE_OPERATOR(^)
#undef DEFINE_OPERATOR
#define CONCAT(X, Y) X##Y
#define DEFINE_OPERATOR(op) \
_CCCL_HOST_DEVICE constexpr custom_numeric& operator CONCAT(op, =)(const custom_numeric & other) \
{ \
fill(value[0] op other.value[0]); \
return *this; \
}
DEFINE_OPERATOR(+)
DEFINE_OPERATOR(-)
DEFINE_OPERATOR(*)
DEFINE_OPERATOR(/)
DEFINE_OPERATOR(%)
DEFINE_OPERATOR(<<)
DEFINE_OPERATOR(>>)
DEFINE_OPERATOR(&)
DEFINE_OPERATOR(|)
DEFINE_OPERATOR(^)
#undef DEFINE_OPERATOR
#define DEFINE_OPERATOR(op) \
_CCCL_HOST_DEVICE friend constexpr bool operator op(const custom_numeric& lhs, const custom_numeric& rhs) \
{ \
return lhs.value[0] op rhs.value[0]; \
}
DEFINE_OPERATOR(==)
DEFINE_OPERATOR(!=)
DEFINE_OPERATOR(<)
DEFINE_OPERATOR(<=)
DEFINE_OPERATOR(>)
DEFINE_OPERATOR(>=)
DEFINE_OPERATOR(&&)
DEFINE_OPERATOR(||)
#undef DEFINE_OPERATOR
friend std::ostream& operator<<(std::ostream& os, const custom_numeric& val)
{
return os << "custom_numeric{" << val.value[0] << "}";
}
private:
int value[5] = {0};
_CCCL_HOST_DEVICE constexpr void fill(int val)
{
for (auto& v : value)
{
v = val;
}
}
};
namespace std
{
template <>
struct numeric_limits<custom_numeric> : numeric_limits<int>
{};
} // namespace std
_CCCL_BEGIN_NAMESPACE_CUDA_STD
template <>
struct numeric_limits<custom_numeric> : numeric_limits<int>
{};
_CCCL_END_NAMESPACE_CUDA_STD
using NumericTypes = unittest::type_list<
char,
signed char,
unsigned char,
short,
unsigned short,
int,
unsigned int,
long,
unsigned long,
long long,
unsigned long long,
float,
double,
custom_numeric>;
using BuiltinNumericTypes = unittest::type_list<
char,
signed char,
unsigned char,
short,
unsigned short,
int,
unsigned int,
long,
unsigned long,
long long,
unsigned long long,
float,
double>;
inline void chop_prefix(std::string& str, const std::string& prefix)
{
str.replace(str.find(prefix) == 0 ? 0 : str.size(), prefix.size(), "");
}
inline std::string base_class_name(const std::string& name)
{
std::string result = name;
// if the name begins with "struct ", chop it off
chop_prefix(result, "struct ");
// if the name begins with "class ", chop it off
chop_prefix(result, "class ");
const std::size_t first_lt = result.find_first_of('<');
if (first_lt < result.size())
{
// chop everything including and after first "<"
return result.replace(first_lt, result.size(), "");
}
else
{
return result;
}
}
enum TestStatus
{
Pass = 0,
Failure = 1,
KnownFailure = 2,
Error = 3,
UnknownException = 4
};
using ArgumentSet = std::set<std::string>;
using ArgumentMap = std::map<std::string, std::string>;
// clang-format off
inline constexpr size_t standard_test_sizes[] =
{
0, 1, 2, 3, 4, 5, 8, 10, 13, 16, 17, 19, 27, 30, 31, 32,
33, 35, 42, 53, 58, 63, 64, 65, 72, 97, 100, 127, 128, 129, 142, 183, 192, 201, 240, 255, 256,
257, 302, 511, 512, 513, 687, 900, 1023, 1024, 1025, 1565, 1786, 1973, 2047, 2048, 2049, 3050, 4095, 4096,
4097, 5030, 7791, 10000, 10027, 12345, 16384, 17354, 26255, 32768, 43718, 65533, 65536,
65539, 123456, 131072, 731588, 1048575, 1048576,
3398570, 9760840, (1 << 24) - 1, (1 << 24),
(1 << 24) + 1, (1 << 25) - 1, (1 << 25), (1 << 25) + 1, (1 << 26) - 1, 1 << 26,
(1 << 26) + 1, (1 << 27) - 1, (1 << 27)
};
// clang-format on
inline constexpr size_t tiny_threshold = 1 << 5; // 32
inline constexpr size_t small_threshold = 1 << 8; // 256
inline constexpr size_t medium_threshold = 1 << 12; // 4K
inline constexpr size_t default_threshold = 1 << 16; // 64K
inline constexpr size_t large_threshold = 1 << 20; // 1M
inline constexpr size_t huge_threshold = 1 << 24; // 16M
inline constexpr size_t epic_threshold = 1 << 26; // 64M
inline constexpr size_t max_threshold = (std::numeric_limits<size_t>::max)();
inline std::vector<size_t> test_sizes = [] {
std::vector<size_t> v;
for (size_t s : standard_test_sizes)
{
if (s <= default_threshold)
{
v.push_back(s);
}
}
return v;
}();
inline const std::vector<size_t>& get_test_sizes()
{
return test_sizes;
}
void set_test_sizes(const std::string&);
class UnitTest
{
public:
std::string name;
UnitTest() = default;
UnitTest(const char* name);
virtual ~UnitTest() = default;
virtual void run() {}
bool operator<(const UnitTest& u) const
{
return name < u.name;
}
};
class UnitTestDriver;
class UnitTestDriver
{
using TestMap = std::map<std::string, UnitTest*>;
TestMap test_map;
bool run_tests(std::vector<UnitTest*>& tests_to_run, const ArgumentMap& kwargs);
protected:
// executed immediately after each test
// \param test The UnitTest of interest
// \param concise Whether or not to suppress output
// \return true if all is well; false if the tests must be immediately aborted
virtual bool post_test_smoke_check(const UnitTest& test, bool concise);
public:
inline virtual ~UnitTestDriver() = default;
void register_test(UnitTest* test);
virtual bool run_tests(const ArgumentSet& args, const ArgumentMap& kwargs);
void list_tests();
static UnitTestDriver& s_driver();
};
// Macro to create a single unittest
#define DECLARE_UNITTEST(TEST) \
class TEST##UnitTest : public UnitTest \
{ \
public: \
TEST##UnitTest() \
: UnitTest(#TEST) \
{} \
void run() \
{ \
TEST(); \
} \
}; \
TEST##UnitTest TEST##Instance
#define DECLARE_UNITTEST_WITH_NAME(TEST, NAME) \
class NAME##UnitTest : public UnitTest \
{ \
public: \
NAME##UnitTest() \
: UnitTest(#NAME) \
{} \
void run() \
{ \
TEST(); \
} \
}; \
NAME##UnitTest NAME##Instance
// Macro to create host and device versions of a
// unit test for a bunch of data types
#define DECLARE_VECTOR_UNITTEST(VTEST) \
void VTEST##Host() \
{ \
VTEST<thrust::host_vector<signed char>>(); \
VTEST<thrust::host_vector<short>>(); \
VTEST<thrust::host_vector<int>>(); \
VTEST<thrust::host_vector<float>>(); \
VTEST<thrust::host_vector<custom_numeric>>(); \
/* MR vectors */ \
VTEST<thrust::host_vector<int, thrust::mr::stateless_resource_allocator<int, thrust::host_memory_resource>>>(); \
} \
void VTEST##Device() \
{ \
VTEST<thrust::device_vector<signed char>>(); \
VTEST<thrust::device_vector<short>>(); \
VTEST<thrust::device_vector<int>>(); \
VTEST<thrust::device_vector<float>>(); \
VTEST<thrust::device_vector<custom_numeric>>(); \
/* MR vectors */ \
VTEST<thrust::device_vector<int, thrust::mr::stateless_resource_allocator<int, thrust::device_memory_resource>>>(); \
} \
void VTEST##Universal() \
{ \
VTEST<thrust::universal_vector<int>>(); \
VTEST<thrust::universal_host_pinned_vector<int>>(); \
} \
DECLARE_UNITTEST(VTEST##Host); \
DECLARE_UNITTEST(VTEST##Device); \
DECLARE_UNITTEST(VTEST##Universal);
// Same as above, but only for integral types
#define DECLARE_INTEGRAL_VECTOR_UNITTEST(VTEST) \
void VTEST##Host() \
{ \
VTEST<thrust::host_vector<signed char>>(); \
VTEST<thrust::host_vector<short>>(); \
VTEST<thrust::host_vector<int>>(); \
} \
void VTEST##Device() \
{ \
VTEST<thrust::device_vector<signed char>>(); \
VTEST<thrust::device_vector<short>>(); \
VTEST<thrust::device_vector<int>>(); \
} \
void VTEST##Universal() \
{ \
VTEST<thrust::universal_vector<int>>(); \
VTEST<thrust::universal_host_pinned_vector<int>>(); \
} \
DECLARE_UNITTEST(VTEST##Host); \
DECLARE_UNITTEST(VTEST##Device); \
DECLARE_UNITTEST(VTEST##Universal);
// Macro to create instances of a test for several data types.
#define DECLARE_GENERIC_UNITTEST(TEST) \
class TEST##UnitTest : public UnitTest \
{ \
public: \
TEST##UnitTest() \
: UnitTest(#TEST) \
{} \
void run() \
{ \
TEST<signed char>(); \
TEST<unsigned char>(); \
TEST<short>(); \
TEST<unsigned short>(); \
TEST<int>(); \
TEST<unsigned int>(); \
TEST<float>(); \
} \
}; \
TEST##UnitTest TEST##Instance
// Macro to create instances of a test for several array sizes.
#define DECLARE_SIZED_UNITTEST(TEST) \
class TEST##UnitTest : public UnitTest \
{ \
public: \
TEST##UnitTest() \
: UnitTest(#TEST) \
{} \
void run() \
{ \
const std::vector<size_t>& sizes = get_test_sizes(); \
for (size_t i = 0; i != sizes.size(); ++i) \
{ \
TEST(sizes[i]); \
} \
} \
}; \
TEST##UnitTest TEST##Instance
// Macro to create instances of a test for several data types and array sizes
#define DECLARE_VARIABLE_UNITTEST(TEST) \
class TEST##UnitTest : public UnitTest \
{ \
public: \
TEST##UnitTest() \
: UnitTest(#TEST) \
{} \
void run() \
{ \
const std::vector<size_t>& sizes = get_test_sizes(); \
for (size_t i = 0; i != sizes.size(); ++i) \
{ \
TEST<signed char>(sizes[i]); \
TEST<unsigned char>(sizes[i]); \
TEST<short>(sizes[i]); \
TEST<unsigned short>(sizes[i]); \
TEST<int>(sizes[i]); \
TEST<unsigned int>(sizes[i]); \
TEST<float>(sizes[i]); \
TEST<double>(sizes[i]); \
} \
} \
}; \
TEST##UnitTest TEST##Instance
#define DECLARE_INTEGRAL_VARIABLE_UNITTEST(TEST) \
class TEST##UnitTest : public UnitTest \
{ \
public: \
TEST##UnitTest() \
: UnitTest(#TEST) \
{} \
void run() \
{ \
const std::vector<size_t>& sizes = get_test_sizes(); \
for (size_t i = 0; i != sizes.size(); ++i) \
{ \
TEST<signed char>(sizes[i]); \
TEST<unsigned char>(sizes[i]); \
TEST<short>(sizes[i]); \
TEST<unsigned short>(sizes[i]); \
TEST<int>(sizes[i]); \
TEST<unsigned int>(sizes[i]); \
} \
} \
}; \
TEST##UnitTest TEST##Instance
#define DECLARE_GENERIC_UNITTEST_WITH_TYPES_AND_NAME(TEST, TYPES, NAME) \
::SimpleUnitTest<TEST, TYPES> NAME##_instance(#NAME) /**/
#define DECLARE_GENERIC_SIZED_UNITTEST_WITH_TYPES_AND_NAME(TEST, TYPES, NAME) \
::VariableUnitTest<TEST, TYPES> NAME##_instance(#NAME) /**/
#define DECLARE_GENERIC_UNITTEST_WITH_TYPES(TEST, TYPES) ::SimpleUnitTest<TEST, TYPES> TEST##_instance(#TEST) /**/
#define DECLARE_GENERIC_SIZED_UNITTEST_WITH_TYPES(TEST, TYPES) \
::VariableUnitTest<TEST, TYPES> TEST##_instance(#TEST) /**/
template <template <typename> class TestName, typename TypeList>
class SimpleUnitTest : public UnitTest
{
public:
SimpleUnitTest()
: UnitTest(base_class_name(unittest::type_name<TestName<int>>()).c_str())
{}
SimpleUnitTest(const char* name)
: UnitTest(name)
{}
void run() override
{
// get the first type in the list
using first_type = typename unittest::get_type<TypeList, 0>::type;
unittest::for_each_type<TypeList, TestName, first_type, 0> for_each;
// loop over the types
for_each();
}
}; // end SimpleUnitTest
template <template <typename> class TestName, typename TypeList>
class VariableUnitTest : public UnitTest
{
public:
VariableUnitTest()
: UnitTest(base_class_name(unittest::type_name<TestName<int>>()).c_str())
{}
VariableUnitTest(const char* name)
: UnitTest(name)
{}
void run() override
{
const std::vector<size_t>& sizes = get_test_sizes();
for (const auto size : sizes)
{
// get the first type in the list
using first_type = typename unittest::get_type<TypeList, 0>::type;
unittest::for_each_type<TypeList, TestName, first_type, 0> loop;
// loop over the types
loop(size);
}
}
}; // end VariableUnitTest
template <template <typename> class TestName,
typename TypeList,
template <typename, typename> class Vector,
template <typename> class Alloc>
struct VectorUnitTest : public UnitTest
{
VectorUnitTest()
: UnitTest((base_class_name(unittest::type_name<TestName<Vector<int, Alloc<int>>>>()) + "<"
+ base_class_name(unittest::type_name<Vector<int, Alloc<int>>>()) + ">")
.c_str())
{}
VectorUnitTest(const char* name)
: UnitTest(name)
{}
void run() override
{
// zip up the type list with Alloc
using AllocList = typename unittest::transform1<TypeList, Alloc>::type;
// zip up the type list & alloc list with Vector
using VectorList = typename unittest::transform2<TypeList, AllocList, Vector>::type;
// get the first type in the list
using first_type = typename unittest::get_type<VectorList, 0>::type;
unittest::for_each_type<VectorList, TestName, first_type, 0> loop;
// loop over the types
loop(0);
}
}; // end VectorUnitTest