[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
This commit is contained in:
29
cccl_upstream/thrust/testing/unittest/CMakeLists.txt
Normal file
29
cccl_upstream/thrust/testing/unittest/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
foreach (thrust_target IN LISTS THRUST_TARGETS)
|
||||
thrust_get_target_property(config_device ${thrust_target} DEVICE)
|
||||
thrust_get_target_property(config_prefix ${thrust_target} PREFIX)
|
||||
|
||||
set(framework_target ${config_prefix}.test.framework)
|
||||
|
||||
if ("CUDA" STREQUAL "${config_device}")
|
||||
set(
|
||||
framework_srcs #
|
||||
testframework.cu
|
||||
cuda/testframework.cu
|
||||
)
|
||||
else()
|
||||
# Wrap the cu file inside a .cpp file for non-CUDA builds
|
||||
thrust_wrap_cu_in_cpp(framework_srcs testframework.cu ${thrust_target})
|
||||
endif()
|
||||
|
||||
add_library(${framework_target} STATIC ${framework_srcs})
|
||||
cccl_configure_target(${framework_target})
|
||||
cccl_ensure_metatargets(${framework_target})
|
||||
target_link_libraries(${framework_target} PUBLIC ${thrust_target})
|
||||
target_include_directories(
|
||||
${framework_target}
|
||||
PRIVATE "${Thrust_SOURCE_DIR}/testing"
|
||||
)
|
||||
if ("CUDA" STREQUAL "${config_device}")
|
||||
thrust_configure_cuda_target(${framework_target} RDC ${THRUST_FORCE_RDC})
|
||||
endif()
|
||||
endforeach()
|
||||
767
cccl_upstream/thrust/testing/unittest/assertions.h
Normal file
767
cccl_upstream/thrust/testing/unittest/assertions.h
Normal file
@@ -0,0 +1,767 @@
|
||||
#pragma once
|
||||
|
||||
#include <thrust/complex.h>
|
||||
#include <thrust/detail/type_traits.h>
|
||||
#include <thrust/device_vector.h>
|
||||
#include <thrust/host_vector.h>
|
||||
#include <thrust/iterator/iterator_traits.h>
|
||||
#include <thrust/universal_vector.h>
|
||||
|
||||
#include <cuda/std/utility>
|
||||
|
||||
#include <unittest/exceptions.h>
|
||||
#include <unittest/util.h>
|
||||
|
||||
#define ASSERT_EQUAL_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_equal((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_EQUAL_QUIET_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_equal_quiet((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_NOT_EQUAL_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_not_equal((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_NOT_EQUAL_QUIET_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) \
|
||||
unittest::assert_not_equal_quiet((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_LEQUAL_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_lequal((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_GEQUAL_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_gequal((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_LESS_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_less((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_GREATER_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_greater((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_ALMOST_EQUAL_WITH_FILE_AND_LINE(X, Y, FILE_, LINE_) unittest::assert_almost_equal((X), (Y), FILE_, LINE_)
|
||||
#define ASSERT_EQUAL_RANGES_WITH_FILE_AND_LINE(X, Y, Z, FILE_, LINE_) \
|
||||
unittest::assert_equal((X), (Y), (Z), FILE_, LINE_)
|
||||
|
||||
#define ASSERT_THROWS_WITH_FILE_AND_LINE(EXPR, EXCEPTION_TYPE, FILE_, LINE_) \
|
||||
{ \
|
||||
unittest::threw_status THRUST_PP_CAT2(__s, LINE_) = unittest::did_not_throw; \
|
||||
try \
|
||||
{ \
|
||||
EXPR; \
|
||||
} \
|
||||
catch (EXCEPTION_TYPE const&) \
|
||||
{ \
|
||||
THRUST_PP_CAT2(__s, LINE_) = unittest::threw_right_type; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
THRUST_PP_CAT2(__s, LINE_) = unittest::threw_wrong_type; \
|
||||
} \
|
||||
unittest::check_assert_throws(THRUST_PP_CAT2(__s, LINE_), THRUST_PP_STRINGIZE(EXCEPTION_TYPE), FILE_, LINE_); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
#define ASSERT_THROWS_EQUAL_WITH_FILE_AND_LINE(EXPR, EXCEPTION_TYPE, VALUE, FILE_, LINE_) \
|
||||
{ \
|
||||
unittest::threw_status THRUST_PP_CAT2(__s, LINE_) = unittest::did_not_throw; \
|
||||
try \
|
||||
{ \
|
||||
EXPR; \
|
||||
} \
|
||||
catch (EXCEPTION_TYPE const& THRUST_PP_CAT2(__e, LINE_)) \
|
||||
{ \
|
||||
if (VALUE == THRUST_PP_CAT2(__e, LINE_)) \
|
||||
THRUST_PP_CAT2(__s, LINE_) = unittest::threw_right_type; \
|
||||
else \
|
||||
THRUST_PP_CAT2(__s, LINE_) = unittest::threw_right_type_but_wrong_value; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
THRUST_PP_CAT2(__s, LINE_) = unittest::threw_wrong_type; \
|
||||
} \
|
||||
unittest::check_assert_throws(THRUST_PP_CAT2(__s, LINE_), THRUST_PP_STRINGIZE(EXCEPTION_TYPE), FILE_, LINE_); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
#define KNOWN_FAILURE_WITH_FILE_AND_LINE(FILE_, LINE_) \
|
||||
{ \
|
||||
unittest::UnitTestKnownFailure f; \
|
||||
f << "[" << FILE_ ":" << LINE_ << "]"; \
|
||||
throw f; \
|
||||
} \
|
||||
/**/
|
||||
|
||||
#define ASSERT_EQUAL(X, Y) ASSERT_EQUAL_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_EQUAL_QUIET(X, Y) ASSERT_EQUAL_QUIET_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_NOT_EQUAL(X, Y) ASSERT_NOT_EQUAL_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_NOT_EQUAL_QUIET(X, Y) ASSERT_NOT_EQUAL_QUIET_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_LEQUAL(X, Y) ASSERT_LEQUAL_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_GEQUAL(X, Y) ASSERT_GEQUAL_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_LESS(X, Y) ASSERT_LESS_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_GREATER(X, Y) ASSERT_GREATER_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_ALMOST_EQUAL(X, Y) ASSERT_ALMOST_EQUAL_WITH_FILE_AND_LINE((X), (Y), __FILE__, __LINE__)
|
||||
#define ASSERT_EQUAL_RANGES(X, Y, Z) ASSERT_EQUAL_WITH_FILE_AND_LINE((X), (Y), (Z), __FILE__, __LINE__)
|
||||
|
||||
#define ASSERT_THROWS(EXPR, EXCEPTION_TYPE) \
|
||||
ASSERT_THROWS_WITH_FILE_AND_LINE(EXPR, EXCEPTION_TYPE, __FILE__, __LINE__) \
|
||||
/**/
|
||||
|
||||
#define ASSERT_THROWS_EQUAL(EXPR, EXCEPTION_TYPE, VALUE) \
|
||||
ASSERT_THROWS_EQUAL_WITH_FILE_AND_LINE(EXPR, EXCEPTION_TYPE, VALUE, __FILE__, __LINE__) \
|
||||
/**/
|
||||
|
||||
#define KNOWN_FAILURE KNOWN_FAILURE_WITH_FILE_AND_LINE(__FILE__, __LINE__)
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
size_t const MAX_OUTPUT_LINES = 10;
|
||||
|
||||
double const DEFAULT_RELATIVE_TOL = 1e-4;
|
||||
double const DEFAULT_ABSOLUTE_TOL = 1e-4;
|
||||
|
||||
template <typename T>
|
||||
struct value_type
|
||||
{
|
||||
using type = ::cuda::std::remove_const_t<::cuda::std::remove_reference_t<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct value_type<THRUST_NS_QUALIFIER::device_reference<T>>
|
||||
{
|
||||
using type = typename value_type<T>::type;
|
||||
};
|
||||
|
||||
////
|
||||
// check scalar values
|
||||
template <typename T1, typename T2>
|
||||
void assert_equal(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a == b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are not equal: " << a << " " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_equal(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a == b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are not equal: " << int(a) << " " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes its not possible to << a type
|
||||
template <typename T1, typename T2>
|
||||
void assert_equal_quiet(const T1& a, const T2& b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a == b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are not equal";
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
// check scalar values
|
||||
template <typename T1, typename T2>
|
||||
void assert_not_equal(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (a == b)
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are equal: " << a << " " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_not_equal(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (a == b)
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are equal: " << int(a) << " " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
// sometimes its not possible to << a type
|
||||
template <typename T1, typename T2>
|
||||
void assert_not_equal_quiet(const T1& a, const T2& b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (a == b)
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are equal";
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void assert_less(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a < b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << a << " is greater or equal to " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_less(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a < b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << int(a) << " is greater than or equal to " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void assert_greater(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a > b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << a << " is less than or equal to " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_greater(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a > b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << int(a) << " is less than or equal to " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void assert_lequal(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a <= b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << a << " is greater than " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_lequal(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a <= b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << int(a) << " is greater than " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void assert_gequal(T1 a, T2 b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a >= b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << a << " is less than " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_gequal(char a, char b, const std::string& filename = "unknown", int lineno = -1)
|
||||
{
|
||||
if (!(a >= b))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << int(a) << " is less than " << int(b);
|
||||
f << " [type='" << type_name<char>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
// will catch everything implicitly convertible to a double
|
||||
bool almost_equal(double a, double b, double a_tol, double r_tol)
|
||||
{
|
||||
if (std::abs(a - b) > r_tol * (std::abs(a) + std::abs(b)) + a_tol)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{ // anonymous namespace
|
||||
template <typename>
|
||||
struct is_complex : public THRUST_NS_QUALIFIER::false_type
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct is_complex<THRUST_NS_QUALIFIER::complex<T>> : public THRUST_NS_QUALIFIER::true_type
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct is_complex<std::complex<T>> : public THRUST_NS_QUALIFIER::true_type
|
||||
{};
|
||||
} // namespace
|
||||
|
||||
template <typename T1, typename T2>
|
||||
inline ::cuda::std::enable_if_t<is_complex<T1>::value && is_complex<T2>::value, bool>
|
||||
almost_equal(const T1& a, const T2& b, double a_tol, double r_tol)
|
||||
{
|
||||
return almost_equal(a.real(), b.real(), a_tol, r_tol) && almost_equal(a.imag(), b.imag(), a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void assert_almost_equal(
|
||||
T1 a,
|
||||
T2 b,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
|
||||
{
|
||||
if (!almost_equal(a, b, a_tol, r_tol))
|
||||
{
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
f << "values are not approximately equal: " << a << " " << b;
|
||||
f << " [type='" << type_name<T1>() << "']";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class almost_equal_to
|
||||
{
|
||||
public:
|
||||
double a_tol, r_tol;
|
||||
almost_equal_to(double _a_tol = DEFAULT_ABSOLUTE_TOL, double _r_tol = DEFAULT_RELATIVE_TOL)
|
||||
: a_tol(_a_tol)
|
||||
, r_tol(_r_tol)
|
||||
{}
|
||||
bool operator()(const T& a, const T& b) const
|
||||
{
|
||||
return almost_equal((double) a, (double) b, a_tol, r_tol);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class almost_equal_to<THRUST_NS_QUALIFIER::complex<T>>
|
||||
{
|
||||
public:
|
||||
double a_tol, r_tol;
|
||||
almost_equal_to(double _a_tol = DEFAULT_ABSOLUTE_TOL, double _r_tol = DEFAULT_RELATIVE_TOL)
|
||||
: a_tol(_a_tol)
|
||||
, r_tol(_r_tol)
|
||||
{}
|
||||
bool operator()(const THRUST_NS_QUALIFIER::complex<T>& a, const THRUST_NS_QUALIFIER::complex<T>& b) const
|
||||
{
|
||||
return almost_equal((double) a.real(), (double) b.real(), a_tol, r_tol)
|
||||
&& almost_equal((double) a.imag(), (double) b.imag(), a_tol, r_tol);
|
||||
}
|
||||
};
|
||||
|
||||
////
|
||||
// check sequences
|
||||
|
||||
inline int promote_char(char c)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T&& promote_char(T&& t)
|
||||
{
|
||||
return ::cuda::std::forward<T>(t);
|
||||
}
|
||||
|
||||
template <typename ForwardIterator1, typename ForwardIterator2, typename BinaryPredicate>
|
||||
void assert_equal(
|
||||
ForwardIterator1 first1,
|
||||
ForwardIterator1 last1,
|
||||
ForwardIterator2 first2,
|
||||
ForwardIterator2 last2,
|
||||
BinaryPredicate op,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
using difference_type = THRUST_NS_QUALIFIER::detail::it_difference_t<ForwardIterator1>;
|
||||
using InputType = THRUST_NS_QUALIFIER::detail::it_value_t<ForwardIterator1>;
|
||||
|
||||
bool failure = false;
|
||||
|
||||
difference_type length1 = ::cuda::std::distance(first1, last1);
|
||||
difference_type length2 = ::cuda::std::distance(first2, last2);
|
||||
|
||||
difference_type min_length = ::cuda::std::min(length1, length2);
|
||||
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << filename << ":" << lineno << "] ";
|
||||
|
||||
// check lengths
|
||||
if (length1 != length2)
|
||||
{
|
||||
failure = true;
|
||||
f << "Sequences have different sizes (" << length1 << " != " << length2 << ")\n";
|
||||
}
|
||||
|
||||
// check values
|
||||
|
||||
size_t mismatches = 0;
|
||||
|
||||
for (difference_type i = 0; i < min_length; i++)
|
||||
{
|
||||
if (!op(*first1, *first2))
|
||||
{
|
||||
if (mismatches == 0)
|
||||
{
|
||||
failure = true;
|
||||
f << "Sequences are not equal [type='" << type_name<InputType>() << "']\n";
|
||||
f << "--------------------------------\n";
|
||||
}
|
||||
|
||||
mismatches++;
|
||||
|
||||
if (mismatches <= MAX_OUTPUT_LINES)
|
||||
{
|
||||
f << " [" << i << "] " << promote_char(*first1) << " " << promote_char(*first2) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
first1++;
|
||||
first2++;
|
||||
}
|
||||
|
||||
if (mismatches > 0)
|
||||
{
|
||||
if (mismatches > MAX_OUTPUT_LINES)
|
||||
{
|
||||
f << " (output limit reached)\n";
|
||||
}
|
||||
f << "--------------------------------\n";
|
||||
f << "Sequences differ at " << mismatches << " of " << min_length << " positions"
|
||||
<< "\n";
|
||||
}
|
||||
else if (length1 != length2)
|
||||
{
|
||||
f << "Sequences agree through " << min_length << " positions [type='" << type_name<InputType>() << "']\n";
|
||||
}
|
||||
|
||||
if (failure)
|
||||
{
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ForwardIterator1, typename ForwardIterator2>
|
||||
void assert_equal(
|
||||
ForwardIterator1 first1,
|
||||
ForwardIterator1 last1,
|
||||
ForwardIterator2 first2,
|
||||
ForwardIterator2 last2,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
using InputType = typename ::cuda::std::iterator_traits<ForwardIterator1>::value_type;
|
||||
assert_equal(first1, last1, first2, last2, cuda::std::equal_to<InputType>(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename ForwardIterator1, typename ForwardIterator2>
|
||||
void assert_almost_equal(
|
||||
ForwardIterator1 first1,
|
||||
ForwardIterator1 last1,
|
||||
ForwardIterator2 first2,
|
||||
ForwardIterator2 last2,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
using InputType = typename ::cuda::std::iterator_traits<ForwardIterator1>::value_type;
|
||||
assert_equal(first1, last1, first2, last2, almost_equal_to<InputType>(a_tol, r_tol), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
assert_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> B_host = B;
|
||||
assert_equal(A, B_host, filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc2> A_host = A;
|
||||
assert_equal(A_host, B, filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T> A_host = A;
|
||||
THRUST_NS_QUALIFIER::host_vector<T> B_host = B;
|
||||
assert_equal(A_host, B_host, filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
assert_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
assert_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
assert_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> A_host = A;
|
||||
assert_equal(A_host, B, filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> B_host = B;
|
||||
assert_equal(A, B_host, filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_equal(const std::vector<T, Alloc1>& A,
|
||||
const std::vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1)
|
||||
{
|
||||
assert_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
assert_almost_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> B_host = B;
|
||||
assert_almost_equal(A, B_host, filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc2> A_host = A;
|
||||
assert_almost_equal(A_host, B, filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T> A_host = A;
|
||||
THRUST_NS_QUALIFIER::host_vector<T> B_host = B;
|
||||
assert_almost_equal(A_host, B_host, filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
assert_almost_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
assert_almost_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::host_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
assert_almost_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> A_host = A;
|
||||
assert_almost_equal(A_host, B, filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const THRUST_NS_QUALIFIER::universal_vector<T, Alloc1>& A,
|
||||
const THRUST_NS_QUALIFIER::device_vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T, Alloc1> B_host = B;
|
||||
assert_almost_equal(A, B_host, filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc1, typename Alloc2>
|
||||
void assert_almost_equal(
|
||||
const std::vector<T, Alloc1>& A,
|
||||
const std::vector<T, Alloc2>& B,
|
||||
const std::string& filename = "unknown",
|
||||
int lineno = -1,
|
||||
const double a_tol = DEFAULT_ABSOLUTE_TOL,
|
||||
const double r_tol = DEFAULT_RELATIVE_TOL)
|
||||
{
|
||||
assert_almost_equal(A.begin(), A.end(), B.begin(), B.end(), filename, lineno, a_tol, r_tol);
|
||||
}
|
||||
|
||||
enum threw_status
|
||||
{
|
||||
did_not_throw,
|
||||
threw_wrong_type,
|
||||
threw_right_type_but_wrong_value,
|
||||
threw_right_type
|
||||
};
|
||||
|
||||
void check_assert_throws(
|
||||
threw_status s, std::string const& exception_name, std::string const& file_name = "unknown", int line_number = -1)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case did_not_throw: {
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << file_name << ":" << line_number << "] did not throw anything";
|
||||
throw f;
|
||||
}
|
||||
case threw_wrong_type: {
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << file_name << ":" << line_number << "] did not throw an "
|
||||
<< "object of type " << exception_name;
|
||||
throw f;
|
||||
}
|
||||
case threw_right_type_but_wrong_value: {
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << file_name << ":" << line_number << "] threw an object of the "
|
||||
<< "correct type (" << exception_name << ") but wrong value";
|
||||
throw f;
|
||||
}
|
||||
case threw_right_type:
|
||||
break;
|
||||
default: {
|
||||
unittest::UnitTestFailure f;
|
||||
f << "[" << file_name << ":" << line_number << "] encountered an "
|
||||
<< "unknown error";
|
||||
throw f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // end namespace unittest
|
||||
231
cccl_upstream/thrust/testing/unittest/cuda/testframework.cu
Normal file
231
cccl_upstream/thrust/testing/unittest/cuda/testframework.cu
Normal file
@@ -0,0 +1,231 @@
|
||||
#include <thrust/system/cuda/memory.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <unittest/cuda/testframework.h>
|
||||
#include <unittest/testframework.h>
|
||||
|
||||
__global__ void dummy_kernel() {}
|
||||
|
||||
bool binary_exists_for_current_device()
|
||||
{
|
||||
// check against the dummy_kernel
|
||||
// if we're unable to get the attributes, then
|
||||
// we didn't compile a binary compatible with the current device
|
||||
cudaFuncAttributes attr;
|
||||
cudaError_t error = cudaFuncGetAttributes(&attr, dummy_kernel);
|
||||
|
||||
// clear the CUDA global error state if we just set it, so that
|
||||
// check_cuda_error doesn't complain
|
||||
if (cudaSuccess != error)
|
||||
{
|
||||
(void) cudaGetLastError();
|
||||
}
|
||||
|
||||
return cudaSuccess == error;
|
||||
}
|
||||
|
||||
void list_devices()
|
||||
{
|
||||
int deviceCount;
|
||||
cudaGetDeviceCount(&deviceCount);
|
||||
if (deviceCount == 0)
|
||||
{
|
||||
std::cout << "There is no device supporting CUDA" << '\n';
|
||||
}
|
||||
|
||||
int selected_device;
|
||||
cudaGetDevice(&selected_device);
|
||||
|
||||
for (int dev = 0; dev < deviceCount; ++dev)
|
||||
{
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, dev);
|
||||
|
||||
if (dev == 0)
|
||||
{
|
||||
if (deviceProp.major == 9999 && deviceProp.minor == 9999)
|
||||
{
|
||||
std::cout << "There is no device supporting CUDA." << '\n';
|
||||
}
|
||||
else if (deviceCount == 1)
|
||||
{
|
||||
std::cout << "There is 1 device supporting CUDA" << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "There are " << deviceCount << " devices supporting CUDA" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nDevice " << dev << ": \"" << deviceProp.name << "\"";
|
||||
if (dev == selected_device)
|
||||
{
|
||||
std::cout << " [SELECTED]";
|
||||
}
|
||||
std::cout << '\n';
|
||||
|
||||
std::cout << " Major revision number: " << deviceProp.major << '\n';
|
||||
std::cout << " Minor revision number: " << deviceProp.minor << '\n';
|
||||
std::cout << " Total amount of global memory: " << deviceProp.totalGlobalMem << " bytes" << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
// provide next, which c++03 doesn't have
|
||||
template <typename Iterator>
|
||||
Iterator my_next(Iterator iter)
|
||||
{
|
||||
return ++iter;
|
||||
}
|
||||
|
||||
std::vector<int> CUDATestDriver::target_devices(const ArgumentMap& kwargs)
|
||||
{
|
||||
std::vector<int> result;
|
||||
|
||||
// by default, test all devices in the system (device id -1)
|
||||
int device_id = kwargs.count("device") ? atoi(kwargs.find("device")->second.c_str()) : -1;
|
||||
|
||||
if (device_id < 0)
|
||||
{
|
||||
// target all devices in the system
|
||||
int count = 0;
|
||||
cudaGetDeviceCount(&count);
|
||||
|
||||
result.resize(count);
|
||||
std::iota(result.begin(), result.end(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// target the specified device
|
||||
result = std::vector<int>(1, device_id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CUDATestDriver::check_cuda_error(bool concise)
|
||||
{
|
||||
cudaError_t const error = cudaGetLastError();
|
||||
if (cudaSuccess != error)
|
||||
{
|
||||
if (!concise)
|
||||
{
|
||||
std::cout << "[ERROR] CUDA error detected before running tests: [" << std::string(cudaGetErrorName(error)) << ": "
|
||||
<< std::string(cudaGetErrorString(error)) << "]" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return cudaSuccess != error;
|
||||
}
|
||||
|
||||
bool CUDATestDriver::post_test_smoke_check(const UnitTest& test, bool concise)
|
||||
{
|
||||
cudaError_t const error = cudaDeviceSynchronize();
|
||||
if (cudaSuccess != error)
|
||||
{
|
||||
if (!concise)
|
||||
{
|
||||
std::cout
|
||||
<< "\t[ERROR] CUDA error detected after running " << test.name << ": [" << std::string(cudaGetErrorName(error))
|
||||
<< ": " << std::string(cudaGetErrorString(error)) << "]" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return cudaSuccess == error;
|
||||
}
|
||||
|
||||
bool CUDATestDriver::run_tests(const ArgumentSet& args, const ArgumentMap& kwargs)
|
||||
{
|
||||
bool verbose = kwargs.count("verbose");
|
||||
bool concise = kwargs.count("concise");
|
||||
|
||||
if (verbose && concise)
|
||||
{
|
||||
std::cout << "--verbose and --concise cannot be used together" << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// check error status before doing anything
|
||||
if (check_cuda_error(concise))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
if (kwargs.count("verbose"))
|
||||
{
|
||||
list_devices();
|
||||
}
|
||||
|
||||
// figure out which devices to target
|
||||
std::vector<int> devices = target_devices(kwargs);
|
||||
|
||||
// target each device
|
||||
for (std::vector<int>::iterator device = devices.begin(); device != devices.end(); ++device)
|
||||
{
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
// set the device
|
||||
cudaSetDevice(*device);
|
||||
|
||||
// check if a binary exists for this device
|
||||
// if none exists, skip the device silently unless this is the only one we're targeting
|
||||
if (devices.size() > 1 && !binary_exists_for_current_device())
|
||||
{
|
||||
// note which device we're skipping
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, *device);
|
||||
|
||||
std::cout << "Skipping Device " << *device << ": \"" << deviceProp.name << "\"" << '\n';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!concise)
|
||||
{
|
||||
// note which device we're testing
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, *device);
|
||||
|
||||
std::cout << "Testing Device " << *device << ": \"" << deviceProp.name << "\"" << '\n';
|
||||
}
|
||||
|
||||
// check error status before running any tests
|
||||
if (check_cuda_error(concise))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// run tests
|
||||
result &= UnitTestDriver::run_tests(args, kwargs);
|
||||
|
||||
if (!concise && my_next(device) != devices.end())
|
||||
{
|
||||
// provide some separation between the output of separate tests
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int CUDATestDriver::current_device_architecture() const
|
||||
{
|
||||
int current = -1;
|
||||
cudaGetDevice(¤t);
|
||||
cudaDeviceProp deviceProp;
|
||||
cudaGetDeviceProperties(&deviceProp, current);
|
||||
|
||||
return 100 * deviceProp.major + 10 * deviceProp.minor;
|
||||
}
|
||||
|
||||
UnitTestDriver& driver_instance(thrust::system::cuda::tag)
|
||||
{
|
||||
static CUDATestDriver s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
25
cccl_upstream/thrust/testing/unittest/cuda/testframework.h
Normal file
25
cccl_upstream/thrust/testing/unittest/cuda/testframework.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <thrust/system/cuda/memory.h>
|
||||
#include <thrust/system_error.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <unittest/testframework.h>
|
||||
|
||||
class CUDATestDriver : public UnitTestDriver
|
||||
{
|
||||
public:
|
||||
int current_device_architecture() const;
|
||||
|
||||
private:
|
||||
std::vector<int> target_devices(const ArgumentMap& kwargs);
|
||||
|
||||
bool check_cuda_error(bool concise);
|
||||
|
||||
bool post_test_smoke_check(const UnitTest& test, bool concise) override;
|
||||
|
||||
bool run_tests(const ArgumentSet& args, const ArgumentMap& kwargs) override;
|
||||
};
|
||||
|
||||
UnitTestDriver& driver_instance(thrust::system::cuda::tag);
|
||||
61
cccl_upstream/thrust/testing/unittest/exceptions.h
Normal file
61
cccl_upstream/thrust/testing/unittest/exceptions.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
class UnitTestException
|
||||
{
|
||||
public:
|
||||
std::string message;
|
||||
|
||||
UnitTestException() = default;
|
||||
UnitTestException(std::string msg)
|
||||
: message(std::move(msg))
|
||||
{}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const UnitTestException& e)
|
||||
{
|
||||
return os << e.message;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
UnitTestException& operator<<(const T& t)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << t;
|
||||
message += oss.str();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class UnitTestError : public UnitTestException
|
||||
{
|
||||
public:
|
||||
UnitTestError() = default;
|
||||
UnitTestError(const std::string& msg)
|
||||
: UnitTestException(msg)
|
||||
{}
|
||||
};
|
||||
|
||||
class UnitTestFailure : public UnitTestException
|
||||
{
|
||||
public:
|
||||
UnitTestFailure() = default;
|
||||
UnitTestFailure(const std::string& msg)
|
||||
: UnitTestException(msg)
|
||||
{}
|
||||
};
|
||||
|
||||
class UnitTestKnownFailure : public UnitTestException
|
||||
{
|
||||
public:
|
||||
UnitTestKnownFailure() = default;
|
||||
UnitTestKnownFailure(const std::string& msg)
|
||||
: UnitTestException(msg)
|
||||
{}
|
||||
};
|
||||
} // end namespace unittest
|
||||
173
cccl_upstream/thrust/testing/unittest/meta.h
Normal file
173
cccl_upstream/thrust/testing/unittest/meta.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*! \file meta.h
|
||||
* \brief Defines template classes
|
||||
* for metaprogramming in the
|
||||
* unit tests.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
// mark the absence of a type
|
||||
struct null_type
|
||||
{};
|
||||
|
||||
// this type encapsulates a list of
|
||||
// types
|
||||
template <typename... Ts>
|
||||
struct type_list
|
||||
{};
|
||||
|
||||
// this type provides a way of indexing
|
||||
// into a type_list
|
||||
template <typename List, unsigned int i>
|
||||
struct get_type
|
||||
{
|
||||
using type = null_type;
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct get_type<type_list<T, Ts...>, 0>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T, typename... Ts, unsigned int i>
|
||||
struct get_type<type_list<T, Ts...>, i>
|
||||
{
|
||||
using type = typename get_type<type_list<Ts...>, i - 1>::type;
|
||||
};
|
||||
|
||||
template <typename T, unsigned int i>
|
||||
using get_type_t = typename get_type<T, i>::type;
|
||||
|
||||
// this type and its specialization provides a way to
|
||||
// iterate over a type_list, and
|
||||
// applying a unary function to each type
|
||||
template <typename TypeList, template <typename> class Function, typename T, unsigned int i = 0>
|
||||
struct for_each_type
|
||||
{
|
||||
template <typename U>
|
||||
void operator()(U n)
|
||||
{
|
||||
// run the function on type T
|
||||
Function<T> f;
|
||||
f(n);
|
||||
|
||||
// get the next type
|
||||
using next_type = typename get_type<TypeList, i + 1>::type;
|
||||
|
||||
// recurse to i + 1
|
||||
for_each_type<TypeList, Function, next_type, i + 1> loop;
|
||||
loop(n);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
// run the function on type T
|
||||
Function<T> f;
|
||||
f();
|
||||
|
||||
// get the next type
|
||||
using next_type = typename get_type<TypeList, i + 1>::type;
|
||||
|
||||
// recurse to i + 1
|
||||
for_each_type<TypeList, Function, next_type, i + 1> loop;
|
||||
loop();
|
||||
}
|
||||
};
|
||||
|
||||
// terminal case: do nothing when encountering null_type
|
||||
template <typename TypeList, template <typename> class Function, unsigned int i>
|
||||
struct for_each_type<TypeList, Function, null_type, i>
|
||||
{
|
||||
template <typename U>
|
||||
void operator()(U)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
// this type and its specialization instantiates
|
||||
// a template by applying T to Template.
|
||||
// if T == null_type, then its result is also null_type
|
||||
template <template <typename> class Template, typename T>
|
||||
struct ApplyTemplate1
|
||||
{
|
||||
using type = Template<T>;
|
||||
};
|
||||
|
||||
template <template <typename> class Template>
|
||||
struct ApplyTemplate1<Template, null_type>
|
||||
{
|
||||
using type = null_type;
|
||||
};
|
||||
|
||||
// this type and its specializations instantiates
|
||||
// a template by applying T1 & T2 to Template.
|
||||
// if either T1 or T2 == null_type, then its result
|
||||
// is also null_type
|
||||
template <template <typename, typename> class Template, typename T1, typename T2>
|
||||
struct ApplyTemplate2
|
||||
{
|
||||
using type = Template<T1, T2>;
|
||||
};
|
||||
|
||||
template <template <typename, typename> class Template, typename T>
|
||||
struct ApplyTemplate2<Template, T, null_type>
|
||||
{
|
||||
using type = null_type;
|
||||
};
|
||||
|
||||
template <template <typename, typename> class Template, typename T>
|
||||
struct ApplyTemplate2<Template, null_type, T>
|
||||
{
|
||||
using type = null_type;
|
||||
};
|
||||
|
||||
template <template <typename, typename> class Template>
|
||||
struct ApplyTemplate2<Template, null_type, null_type>
|
||||
{
|
||||
using type = null_type;
|
||||
};
|
||||
|
||||
// this type creates a new type_list by applying a Template to each of
|
||||
// the Type_list's types
|
||||
template <typename TypeList, template <typename> class Template>
|
||||
struct transform1;
|
||||
|
||||
template <typename... Ts, template <typename> class Template>
|
||||
struct transform1<type_list<Ts...>, Template>
|
||||
{
|
||||
using type = type_list<typename ApplyTemplate1<Template, Ts>::type...>;
|
||||
};
|
||||
|
||||
template <typename TypeList1, typename TypeList2, template <typename, typename> class Template>
|
||||
struct transform2;
|
||||
|
||||
template <typename... T1s, typename... T2s, template <typename, typename> class Template>
|
||||
struct transform2<type_list<T1s...>, type_list<T2s...>, Template>
|
||||
{
|
||||
using type = type_list<typename ApplyTemplate2<Template, T1s, T2s>::type...>;
|
||||
};
|
||||
|
||||
template <typename... Ls>
|
||||
struct concat;
|
||||
|
||||
template <typename L>
|
||||
struct concat<L>
|
||||
{
|
||||
using type = L;
|
||||
};
|
||||
|
||||
template <template <typename...> class L, typename... T1s, typename... T2s, typename... Ls>
|
||||
struct concat<L<T1s...>, L<T2s...>, Ls...>
|
||||
{
|
||||
using type = concat<L<T1s..., T2s...>, Ls...>;
|
||||
};
|
||||
} // namespace unittest
|
||||
97
cccl_upstream/thrust/testing/unittest/random.h
Normal file
97
cccl_upstream/thrust/testing/unittest/random.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/type_traits.h>
|
||||
#include <thrust/host_vector.h>
|
||||
#include <thrust/random.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
inline unsigned int hash(unsigned int a)
|
||||
{
|
||||
a = (a + 0x7ed55d16) + (a << 12);
|
||||
a = (a ^ 0xc761c23c) ^ (a >> 19);
|
||||
a = (a + 0x165667b1) + (a << 5);
|
||||
a = (a + 0xd3a2646c) ^ (a << 9);
|
||||
a = (a + 0xfd7046c5) + (a << 3);
|
||||
a = (a ^ 0xb55a4f09) ^ (a >> 16);
|
||||
return a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct generate_random_integer
|
||||
{
|
||||
T operator()(unsigned int i) const
|
||||
{
|
||||
THRUST_NS_QUALIFIER::default_random_engine rng(hash(i));
|
||||
if constexpr (::cuda::std::is_same_v<T, bool>)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::uniform_int_distribution<unsigned int> dist(0, 1);
|
||||
return dist(rng) == 1;
|
||||
}
|
||||
else if constexpr (::cuda::std::is_integral_v<T>)
|
||||
{
|
||||
T const min = ::cuda::std::numeric_limits<T>::min();
|
||||
T const max = ::cuda::std::numeric_limits<T>::max();
|
||||
THRUST_NS_QUALIFIER::uniform_int_distribution<T> dist(min, max);
|
||||
return static_cast<T>(dist(rng));
|
||||
}
|
||||
else if constexpr (::cuda::std::is_floating_point_v<T>)
|
||||
{
|
||||
T const min = ::cuda::std::numeric_limits<T>::lowest();
|
||||
T const max = ::cuda::std::numeric_limits<T>::max();
|
||||
THRUST_NS_QUALIFIER::uniform_real_distribution<T> dist(min, max);
|
||||
return static_cast<T>(dist(rng));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<T>(rng());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct generate_random_sample
|
||||
{
|
||||
T operator()(unsigned int i) const
|
||||
{
|
||||
THRUST_NS_QUALIFIER::default_random_engine rng(hash(i));
|
||||
THRUST_NS_QUALIFIER::uniform_int_distribution<unsigned int> dist(0, 20);
|
||||
|
||||
return static_cast<T>(dist(rng));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
THRUST_NS_QUALIFIER::host_vector<T> random_integers(const size_t N)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T> vec(N);
|
||||
THRUST_NS_QUALIFIER::transform(
|
||||
THRUST_NS_QUALIFIER::counting_iterator{0u},
|
||||
THRUST_NS_QUALIFIER::counting_iterator{static_cast<unsigned int>(N)},
|
||||
vec.begin(),
|
||||
generate_random_integer<T>());
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T random_integer()
|
||||
{
|
||||
return generate_random_integer<T>()(0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
THRUST_NS_QUALIFIER::host_vector<T> random_samples(const size_t N)
|
||||
{
|
||||
THRUST_NS_QUALIFIER::host_vector<T> vec(N);
|
||||
THRUST_NS_QUALIFIER::transform(
|
||||
THRUST_NS_QUALIFIER::counting_iterator{0u},
|
||||
THRUST_NS_QUALIFIER::counting_iterator{static_cast<unsigned int>(N)},
|
||||
vec.begin(),
|
||||
generate_random_sample<T>());
|
||||
|
||||
return vec;
|
||||
}
|
||||
}; // end namespace unittest
|
||||
183
cccl_upstream/thrust/testing/unittest/special_types.h
Normal file
183
cccl_upstream/thrust/testing/unittest/special_types.h
Normal file
@@ -0,0 +1,183 @@
|
||||
#pragma once
|
||||
|
||||
#include <thrust/execution_policy.h>
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
template <typename T, unsigned int N>
|
||||
struct FixedVector
|
||||
{
|
||||
T data[N];
|
||||
|
||||
_CCCL_HOST_DEVICE FixedVector()
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
data[i] = T();
|
||||
}
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE explicit FixedVector(T init)
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
data[i] = init;
|
||||
}
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE
|
||||
#if _CCCL_COMPILER(NVHPC)
|
||||
__attribute__((noinline))
|
||||
#endif
|
||||
FixedVector
|
||||
operator+(const FixedVector& bs) const
|
||||
{
|
||||
FixedVector output;
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
output.data[i] = data[i] + bs.data[i];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator<(const FixedVector& bs) const
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
if (data[i] < bs.data[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (bs.data[i] < data[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator==(const FixedVector& bs) const
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
if (!(data[i] == bs.data[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
struct key_value
|
||||
{
|
||||
using key_type = Key;
|
||||
using value_type = Value;
|
||||
|
||||
_CCCL_HOST_DEVICE key_value()
|
||||
: key()
|
||||
, value()
|
||||
{}
|
||||
|
||||
_CCCL_HOST_DEVICE key_value(key_type k, value_type v)
|
||||
: key(k)
|
||||
, value(v)
|
||||
{}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator<(const key_value& rhs) const
|
||||
{
|
||||
return key < rhs.key;
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator>(const key_value& rhs) const
|
||||
{
|
||||
return key > rhs.key;
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator==(const key_value& rhs) const
|
||||
{
|
||||
return key == rhs.key && value == rhs.value;
|
||||
}
|
||||
|
||||
_CCCL_HOST_DEVICE bool operator!=(const key_value& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const key_value& kv)
|
||||
{
|
||||
return os << "(" << kv.key << ", " << kv.value << ")";
|
||||
}
|
||||
|
||||
key_type key;
|
||||
value_type value;
|
||||
};
|
||||
|
||||
struct user_swappable
|
||||
{
|
||||
_CCCL_HOST_DEVICE user_swappable(bool swapped = false)
|
||||
: was_swapped(swapped)
|
||||
{}
|
||||
|
||||
bool was_swapped;
|
||||
|
||||
friend _CCCL_HOST_DEVICE bool operator==(const user_swappable& x, const user_swappable& y)
|
||||
{
|
||||
return x.was_swapped == y.was_swapped;
|
||||
}
|
||||
|
||||
friend _CCCL_HOST_DEVICE void swap(user_swappable& x, user_swappable& y) noexcept
|
||||
{
|
||||
x.was_swapped = true;
|
||||
y.was_swapped = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Inheriting from classes in anonymous namespaces is not allowed.
|
||||
// The anonymous namespace tests don't use these, so just disable them:
|
||||
#ifndef THRUST_USE_ANON_NAMESPACE
|
||||
|
||||
struct my_system : THRUST_NS_QUALIFIER::device_execution_policy<my_system>
|
||||
{
|
||||
my_system(int) {}
|
||||
|
||||
my_system(const my_system& other)
|
||||
: num_copies(other.num_copies + 1)
|
||||
{}
|
||||
|
||||
void validate_dispatch()
|
||||
{
|
||||
correctly_dispatched = (num_copies == 0);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return correctly_dispatched;
|
||||
}
|
||||
|
||||
private:
|
||||
bool correctly_dispatched = false;
|
||||
|
||||
// count the number of copies so that we can validate
|
||||
// that dispatch does not introduce any
|
||||
unsigned int num_copies = 0;
|
||||
};
|
||||
|
||||
struct my_tag : THRUST_NS_QUALIFIER::device_execution_policy<my_tag>
|
||||
{};
|
||||
|
||||
#endif // THRUST_USE_ANON_NAMESPACE
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
using std::int16_t;
|
||||
using std::int32_t;
|
||||
using std::int64_t;
|
||||
using std::int8_t;
|
||||
|
||||
using std::uint16_t;
|
||||
using std::uint32_t;
|
||||
using std::uint64_t;
|
||||
using std::uint8_t;
|
||||
} // namespace unittest
|
||||
26
cccl_upstream/thrust/testing/unittest/system.h
Normal file
26
cccl_upstream/thrust/testing/unittest/system.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
// for demangling the result of type_info.name()
|
||||
// with msvc, type_info.name() is already demangled
|
||||
#ifdef __GNUC__
|
||||
# include <cxxabi.h>
|
||||
#endif // __GNUC__
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
inline std::string demangle(const char* name)
|
||||
{
|
||||
#if __GNUC__ && !_NVHPC_CUDA
|
||||
int status = 0;
|
||||
char* realname = abi::__cxa_demangle(name, nullptr, nullptr, &status);
|
||||
std::string result(realname);
|
||||
std::free(realname);
|
||||
return result;
|
||||
#else
|
||||
return name;
|
||||
#endif
|
||||
}
|
||||
} // namespace unittest
|
||||
505
cccl_upstream/thrust/testing/unittest/testframework.cu
Normal file
505
cccl_upstream/thrust/testing/unittest/testframework.cu
Normal file
@@ -0,0 +1,505 @@
|
||||
#include <thrust/memory.h>
|
||||
|
||||
#include "unittest/exceptions.h"
|
||||
#include "unittest/testframework.h"
|
||||
|
||||
// #include backends' testframework.h, if they exist and are required for the build
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
# include <unittest/cuda/testframework.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
|
||||
void set_test_sizes(const std::string& val)
|
||||
{
|
||||
size_t threshold = 0;
|
||||
|
||||
if (val == "tiny")
|
||||
{
|
||||
threshold = tiny_threshold;
|
||||
}
|
||||
else if (val == "small")
|
||||
{
|
||||
threshold = small_threshold;
|
||||
}
|
||||
else if (val == "medium")
|
||||
{
|
||||
threshold = medium_threshold;
|
||||
}
|
||||
else if (val == "default")
|
||||
{
|
||||
threshold = default_threshold;
|
||||
}
|
||||
else if (val == "large")
|
||||
{
|
||||
threshold = large_threshold;
|
||||
}
|
||||
else if (val == "huge")
|
||||
{
|
||||
threshold = huge_threshold;
|
||||
}
|
||||
else if (val == "epic")
|
||||
{
|
||||
threshold = epic_threshold;
|
||||
}
|
||||
else if (val == "max")
|
||||
{
|
||||
threshold = max_threshold;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "invalid test size \"" << val << "\"" << '\n';
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t s : standard_test_sizes)
|
||||
{
|
||||
if (s <= threshold)
|
||||
{
|
||||
test_sizes.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestDriver::register_test(UnitTest* test)
|
||||
{
|
||||
if (UnitTestDriver::s_driver().test_map.count(test->name))
|
||||
{
|
||||
std::cout << "[WARNING] Test name \"" << test->name << " already encountered " << '\n';
|
||||
}
|
||||
|
||||
UnitTestDriver::s_driver().test_map[test->name] = test;
|
||||
}
|
||||
|
||||
UnitTest::UnitTest(const char* _name)
|
||||
: name(_name)
|
||||
{
|
||||
UnitTestDriver::s_driver().register_test(this);
|
||||
}
|
||||
|
||||
void process_args(int argc, char** argv, ArgumentSet& args, ArgumentMap& kwargs)
|
||||
|
||||
{
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
std::string arg(argv[i]);
|
||||
|
||||
// look for --key or --key=value arguments
|
||||
if (arg.substr(0, 2) == "--")
|
||||
{
|
||||
std::string::size_type n = arg.find('=', 2);
|
||||
|
||||
if (n == std::string::npos)
|
||||
{
|
||||
kwargs[arg.substr(2)] = std::string(); // (key,"")
|
||||
}
|
||||
else
|
||||
{
|
||||
kwargs[arg.substr(2, n - 2)] = arg.substr(n + 1); // (key,value)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.insert(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usage(int /*argc*/, char** argv)
|
||||
{
|
||||
std::string indent = " ";
|
||||
|
||||
std::cout << "Example Usage:\n";
|
||||
std::cout << indent << argv[0] << "\n";
|
||||
std::cout << indent << argv[0] << " TestName1 [TestName2 ...] \n";
|
||||
std::cout << indent << argv[0] << " PartialTestName1* [PartialTestName2* ...] \n";
|
||||
std::cout << indent << argv[0] << " --device=1\n";
|
||||
std::cout << indent << argv[0] << " --sizes={tiny,small,medium,default,large,huge,epic,max}\n";
|
||||
std::cout << indent << argv[0] << " --verbose or --concise\n";
|
||||
std::cout << indent << argv[0] << " --list\n";
|
||||
std::cout << indent << argv[0] << " --help\n";
|
||||
std::cout << "\n";
|
||||
std::cout << "Options:\n";
|
||||
std::cout << indent << "The sizes option determines which input sizes are tested.\n";
|
||||
std::cout << indent << indent << "--sizes=tiny tests sizes up to " << tiny_threshold << "\n";
|
||||
std::cout << indent << indent << "--sizes=small tests sizes up to " << small_threshold << "\n";
|
||||
std::cout << indent << indent << "--sizes=medium tests sizes up to " << medium_threshold << "\n";
|
||||
std::cout << indent << indent << "--sizes=default tests sizes up to " << default_threshold << "\n";
|
||||
std::cout << indent << indent << "--sizes=large tests sizes up to " << large_threshold << " (0.25 GB memory)\n";
|
||||
std::cout << indent << indent << "--sizes=huge tests sizes up to " << huge_threshold << " (1.50 GB memory)\n";
|
||||
std::cout << indent << indent << "--sizes=epic tests sizes up to " << epic_threshold << " (3.00 GB memory)\n";
|
||||
std::cout << indent << indent << "--sizes=max tests all available sizes\n";
|
||||
}
|
||||
|
||||
struct TestResult
|
||||
{
|
||||
TestStatus status;
|
||||
std::string name;
|
||||
std::string message;
|
||||
|
||||
// XXX use a c++11 timer result when available
|
||||
std::clock_t elapsed;
|
||||
|
||||
TestResult(const TestStatus status, std::clock_t elapsed, const UnitTest& u, const std::string& message = "")
|
||||
: status(status)
|
||||
, name(u.name)
|
||||
, message(message)
|
||||
, elapsed(elapsed)
|
||||
{}
|
||||
|
||||
bool operator<(const TestResult& tr) const
|
||||
{
|
||||
if (status < tr.status)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (tr.status < status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return name < tr.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void record_result(const TestResult& test_result, std::vector<TestResult>& test_results)
|
||||
{
|
||||
test_results.push_back(test_result);
|
||||
}
|
||||
|
||||
void report_results(std::vector<TestResult>& test_results, double elapsed_minutes)
|
||||
{
|
||||
std::cout << '\n';
|
||||
|
||||
std::string hline = "================================================================";
|
||||
|
||||
std::sort(test_results.begin(), test_results.end());
|
||||
|
||||
size_t num_passes = 0;
|
||||
size_t num_failures = 0;
|
||||
size_t num_known_failures = 0;
|
||||
size_t num_errors = 0;
|
||||
|
||||
for (size_t i = 0; i < test_results.size(); i++)
|
||||
{
|
||||
const TestResult& tr = test_results[i];
|
||||
|
||||
if (tr.status == Pass)
|
||||
{
|
||||
num_passes++;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << hline << '\n';
|
||||
|
||||
switch (tr.status)
|
||||
{
|
||||
case Failure:
|
||||
std::cout << "FAILURE";
|
||||
num_failures++;
|
||||
break;
|
||||
case KnownFailure:
|
||||
std::cout << "KNOWN FAILURE";
|
||||
num_known_failures++;
|
||||
break;
|
||||
case Error:
|
||||
std::cout << "ERROR";
|
||||
num_errors++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << ": " << tr.name << '\n' << tr.message << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << hline << '\n';
|
||||
|
||||
std::cout << "Totals: ";
|
||||
std::cout << num_failures << " failures, ";
|
||||
std::cout << num_known_failures << " known failures, ";
|
||||
std::cout << num_errors << " errors, and ";
|
||||
std::cout << num_passes << " passes." << '\n';
|
||||
std::cout << "Time: " << elapsed_minutes << " minutes" << '\n';
|
||||
}
|
||||
|
||||
void UnitTestDriver::list_tests()
|
||||
{
|
||||
for (TestMap::iterator iter = test_map.begin(); iter != test_map.end(); iter++)
|
||||
{
|
||||
std::cout << iter->second->name << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
bool UnitTestDriver::post_test_smoke_check(const UnitTest& /*test*/, bool /*concise*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnitTestDriver::run_tests(std::vector<UnitTest*>& tests_to_run, const ArgumentMap& kwargs)
|
||||
{
|
||||
std::time_t start_time = std::time(0);
|
||||
|
||||
_CCCL_DIAG_PUSH
|
||||
_CCCL_DIAG_SUPPRESS_MSVC(4800) // Forcing value to bool
|
||||
bool verbose = kwargs.count("verbose");
|
||||
bool concise = kwargs.count("concise");
|
||||
_CCCL_DIAG_POP
|
||||
|
||||
std::vector<TestResult> test_results;
|
||||
|
||||
if (verbose && concise)
|
||||
{
|
||||
std::cout << "--verbose and --concise cannot be used together" << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!concise)
|
||||
{
|
||||
std::cout << "Running " << tests_to_run.size() << " unit tests." << '\n';
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tests_to_run.size(); i++)
|
||||
{
|
||||
UnitTest& test = *tests_to_run[i];
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cout << "Running " << test.name << "..." << std::flush;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// time the test
|
||||
std::clock_t start = std::clock();
|
||||
|
||||
// run the test
|
||||
test.run();
|
||||
|
||||
// test passed
|
||||
record_result(TestResult(Pass, std::clock() - start, test), test_results);
|
||||
}
|
||||
catch (unittest::UnitTestFailure& f)
|
||||
{
|
||||
record_result(TestResult(Failure, (std::numeric_limits<std::clock_t>::max)(), test, f.message), test_results);
|
||||
}
|
||||
catch (unittest::UnitTestKnownFailure& f)
|
||||
{
|
||||
record_result(TestResult(KnownFailure, (std::numeric_limits<std::clock_t>::max)(), test, f.message),
|
||||
test_results);
|
||||
}
|
||||
catch (std::bad_alloc& e)
|
||||
{
|
||||
record_result(TestResult(Error, (std::numeric_limits<std::clock_t>::max)(), test, e.what()), test_results);
|
||||
}
|
||||
catch (unittest::UnitTestError& e)
|
||||
{
|
||||
record_result(TestResult(Error, (std::numeric_limits<std::clock_t>::max)(), test, e.message), test_results);
|
||||
}
|
||||
|
||||
// immediate report
|
||||
if (!concise)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
switch (test_results.back().status)
|
||||
{
|
||||
case Pass:
|
||||
std::cout << "\r[PASS] ";
|
||||
std::cout << std::setw(10) << 1000.f * float(test_results.back().elapsed) / CLOCKS_PER_SEC << " ms";
|
||||
break;
|
||||
case Failure:
|
||||
std::cout << "\r[FAILURE] ";
|
||||
break;
|
||||
case KnownFailure:
|
||||
std::cout << "\r[KNOWN FAILURE] ";
|
||||
break;
|
||||
case Error:
|
||||
std::cout << "\r[ERROR] ";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::cout << " " << test.name << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (test_results.back().status)
|
||||
{
|
||||
case Pass:
|
||||
std::cout << ".";
|
||||
break;
|
||||
case Failure:
|
||||
std::cout << "F";
|
||||
break;
|
||||
case KnownFailure:
|
||||
std::cout << "K";
|
||||
break;
|
||||
case Error:
|
||||
std::cout << "E";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!post_test_smoke_check(test, concise))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
double elapsed_minutes = double(std::time(0) - start_time) / 60;
|
||||
|
||||
// summary report
|
||||
if (!concise)
|
||||
{
|
||||
report_results(test_results, elapsed_minutes);
|
||||
}
|
||||
|
||||
// if any failures or errors return false
|
||||
for (size_t i = 0; i < test_results.size(); i++)
|
||||
{
|
||||
if (test_results[i].status != Pass && test_results[i].status != KnownFailure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// all tests pass or are known failures
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnitTestDriver::run_tests(const ArgumentSet& args, const ArgumentMap& kwargs)
|
||||
{
|
||||
if (args.empty())
|
||||
{
|
||||
// run all tests
|
||||
std::vector<UnitTest*> tests_to_run;
|
||||
|
||||
for (TestMap::iterator iter = test_map.begin(); iter != test_map.end(); iter++)
|
||||
{
|
||||
tests_to_run.push_back(iter->second);
|
||||
}
|
||||
|
||||
return run_tests(tests_to_run, kwargs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// all non-keyword arguments are assumed to be test names or partial test names
|
||||
|
||||
using TestMapIterator = TestMap::iterator;
|
||||
|
||||
// vector to accumulate tests
|
||||
std::vector<UnitTest*> tests_to_run;
|
||||
|
||||
for (ArgumentSet::const_iterator iter = args.begin(); iter != args.end(); iter++)
|
||||
{
|
||||
const std::string& arg = *iter;
|
||||
|
||||
size_t len = arg.size();
|
||||
size_t matches = 0;
|
||||
|
||||
if (arg[len - 1] == '*')
|
||||
{
|
||||
// wildcard search
|
||||
std::string search = arg.substr(0, len - 1);
|
||||
|
||||
TestMapIterator lb = test_map.lower_bound(search);
|
||||
while (lb != test_map.end())
|
||||
{
|
||||
if (search != lb->first.substr(0, len - 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
tests_to_run.push_back(lb->second);
|
||||
lb++;
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-wildcard search
|
||||
TestMapIterator lb = test_map.find(arg);
|
||||
|
||||
if (lb != test_map.end())
|
||||
{
|
||||
tests_to_run.push_back(lb->second);
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (matches == 0)
|
||||
{
|
||||
std::cout << "[ERROR] found no test names matching the pattern: " << arg << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return run_tests(tests_to_run, kwargs);
|
||||
}
|
||||
}
|
||||
|
||||
// driver_instance maps a DeviceSystem to a singleton UnitTestDriver
|
||||
template <typename DeviceSystem>
|
||||
UnitTestDriver& driver_instance(DeviceSystem)
|
||||
{
|
||||
static UnitTestDriver s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
// if we need a special kind of UnitTestDriver, overload
|
||||
// driver_instance in that function
|
||||
UnitTestDriver& UnitTestDriver::s_driver()
|
||||
{
|
||||
return driver_instance(thrust::device_system_tag());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ArgumentSet args;
|
||||
ArgumentMap kwargs;
|
||||
|
||||
process_args(argc, argv, args, kwargs);
|
||||
|
||||
if (kwargs.count("help"))
|
||||
{
|
||||
usage(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kwargs.count("list"))
|
||||
{
|
||||
UnitTestDriver::s_driver().list_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kwargs.count("sizes"))
|
||||
{
|
||||
set_test_sizes(kwargs["sizes"]);
|
||||
}
|
||||
|
||||
bool passed = UnitTestDriver::s_driver().run_tests(args, kwargs);
|
||||
|
||||
if (kwargs.count("concise"))
|
||||
{
|
||||
std::cout << ((passed) ? "PASSED" : "FAILED") << '\n';
|
||||
}
|
||||
|
||||
return (passed) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
642
cccl_upstream/thrust/testing/unittest/testframework.h
Normal file
642
cccl_upstream/thrust/testing/unittest/testframework.h
Normal file
@@ -0,0 +1,642 @@
|
||||
#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
|
||||
10
cccl_upstream/thrust/testing/unittest/unittest.h
Normal file
10
cccl_upstream/thrust/testing/unittest/unittest.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// this is the only header included by unittests
|
||||
// it pulls in all the others used for unittesting
|
||||
|
||||
#include <unittest/assertions.h>
|
||||
#include <unittest/meta.h>
|
||||
#include <unittest/random.h>
|
||||
#include <unittest/special_types.h>
|
||||
#include <unittest/testframework.h>
|
||||
59
cccl_upstream/thrust/testing/unittest/util.h
Normal file
59
cccl_upstream/thrust/testing/unittest/util.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/type_traits.h>
|
||||
#include <thrust/extrema.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <unittest/system.h>
|
||||
|
||||
namespace unittest
|
||||
{
|
||||
template <typename T>
|
||||
std::string type_name()
|
||||
{
|
||||
return demangle(typeid(T).name());
|
||||
} // end type_name()
|
||||
|
||||
// Use this with counting_iterator to avoid generating a range larger than we
|
||||
// can represent.
|
||||
template <typename T>
|
||||
typename THRUST_NS_QUALIFIER::detail::disable_if<::cuda::std::is_floating_point<T>::value, T>::type
|
||||
truncate_to_max_representable(std::size_t n)
|
||||
{
|
||||
return static_cast<T>(
|
||||
THRUST_NS_QUALIFIER::min<std::size_t>(n, static_cast<std::size_t>(::cuda::std::numeric_limits<T>::max())));
|
||||
}
|
||||
|
||||
// TODO: This probably won't work for `half`.
|
||||
template <typename T>
|
||||
typename ::cuda::std::enable_if_t<::cuda::std::is_floating_point<T>::value, T>
|
||||
truncate_to_max_representable(std::size_t n)
|
||||
{
|
||||
return THRUST_NS_QUALIFIER::min<T>(static_cast<T>(n), ::cuda::std::numeric_limits<T>::max());
|
||||
}
|
||||
} // namespace unittest
|
||||
|
||||
template <typename Iterator>
|
||||
void PRINT(Iterator first, Iterator last)
|
||||
{
|
||||
size_t n = 0;
|
||||
for (Iterator i = first; i != last; i++, n++)
|
||||
{
|
||||
std::cout << ">>> [" << n << "] = " << *i << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
void PRINT(const Container& c)
|
||||
{
|
||||
PRINT(c.begin(), c.end());
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void PRINT(const char (&c)[N])
|
||||
{
|
||||
std::cout << std::string(c, c + N) << '\n';
|
||||
}
|
||||
Reference in New Issue
Block a user