[CCCL] Add missing CCCL components: c2h, nvbench_helper, cmake, cudax, AGENTS.md
Added 863 files from NVIDIA/cccl sparse checkout: - c2h/ (27 files): Catch2 test helpers — generators, validators, runner - nvbench_helper/ (10 files): Benchmark harness utilities - cmake/ (29 files): CMake presets and build helpers - cudax/ (794 files): Experimental CUDA extensions - AGENTS.md: NVIDIA's official AI agent instructions for CCCL - CMakePresets.json: Standardized build configurations - cccl-version.json: Version tracking Also added CCCL_ASSET_MAP.md mapping all 4295 CCCL files to competition value and PRD items. cccl_upstream now covers 100% of competition-critical assets: - 27 tuning headers (SM80/90/100 benchmark data) - 32 dispatch headers (algorithm implementations) - 60 Thrust examples (correctness verification) - 217 CUB Catch2 tests (regression matrix) - 153 CUB benchmarks (parameter space search) - 18 CUB examples (API verification) - 27 test helpers + benchmark harness - 794 cudax experimental extensions
This commit is contained in:
256
cccl_upstream/c2h/include/c2h/bfloat16.cuh
Normal file
256
cccl_upstream/c2h/include/c2h/bfloat16.cuh
Normal file
@@ -0,0 +1,256 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Utilities for interacting with the opaque CUDA __nv_bfloat16 type
|
||||
*/
|
||||
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
#include <cub/util_type.cuh>
|
||||
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
|
||||
#ifdef __GNUC__
|
||||
// There's a ton of type-punning going on in this file.
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* bfloat16_t
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Host-based fp16 data type compatible and convertible with __nv_bfloat16
|
||||
*/
|
||||
struct bfloat16_t
|
||||
{
|
||||
uint16_t __x;
|
||||
|
||||
/// Constructor from __nv_bfloat16
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(const __nv_bfloat16& other)
|
||||
{
|
||||
__x = reinterpret_cast<const uint16_t&>(other);
|
||||
}
|
||||
|
||||
/// Constructor from integer
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(int a)
|
||||
{
|
||||
*this = bfloat16_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from std::size_t
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(std::size_t a)
|
||||
{
|
||||
*this = bfloat16_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from double
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(double a)
|
||||
{
|
||||
*this = bfloat16_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from unsigned long long int
|
||||
template <typename T,
|
||||
typename = typename ::cuda::std::enable_if<
|
||||
::cuda::std::is_same<T, unsigned long long int>::value
|
||||
&& (!::cuda::std::is_same<std::size_t, unsigned long long int>::value)>::type>
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(T a)
|
||||
{
|
||||
*this = bfloat16_t(float(a));
|
||||
}
|
||||
|
||||
/// Default constructor
|
||||
bfloat16_t() = default;
|
||||
|
||||
/// Constructor from float
|
||||
__host__ __device__ __forceinline__ explicit bfloat16_t(float a)
|
||||
{
|
||||
// Reference:
|
||||
// https://github.com/pytorch/pytorch/blob/44cc873fba5e5ffc4d4d4eef3bd370b653ce1ce1/c10/util/BFloat16.h#L51
|
||||
uint16_t ir;
|
||||
if (a != a)
|
||||
{
|
||||
ir = UINT16_C(0x7FFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t U32;
|
||||
float F32;
|
||||
};
|
||||
|
||||
F32 = a;
|
||||
uint32_t rounding_bias = ((U32 >> 16) & 1) + UINT32_C(0x7FFF);
|
||||
ir = static_cast<uint16_t>((U32 + rounding_bias) >> 16);
|
||||
}
|
||||
this->__x = ir;
|
||||
}
|
||||
|
||||
/// Cast to __nv_bfloat16
|
||||
__host__ __device__ __forceinline__ operator __nv_bfloat16() const
|
||||
{
|
||||
return reinterpret_cast<const __nv_bfloat16&>(__x);
|
||||
}
|
||||
|
||||
/// Cast to float
|
||||
__host__ __device__ __forceinline__ operator float() const
|
||||
{
|
||||
float f = 0;
|
||||
uint32_t* p = reinterpret_cast<uint32_t*>(&f);
|
||||
*p = uint32_t(__x) << 16;
|
||||
return f;
|
||||
}
|
||||
|
||||
/// Get raw storage
|
||||
__host__ __device__ __forceinline__ uint16_t raw() const
|
||||
{
|
||||
return this->__x;
|
||||
}
|
||||
|
||||
/// Equality
|
||||
__host__ __device__ __forceinline__ friend bool operator==(const bfloat16_t& a, const bfloat16_t& b)
|
||||
{
|
||||
return (a.__x == b.__x);
|
||||
}
|
||||
|
||||
/// Inequality
|
||||
__host__ __device__ __forceinline__ friend bool operator!=(const bfloat16_t& a, const bfloat16_t& b)
|
||||
{
|
||||
return (a.__x != b.__x);
|
||||
}
|
||||
|
||||
/// Assignment by sum
|
||||
__host__ __device__ __forceinline__ bfloat16_t& operator+=(const bfloat16_t& rhs)
|
||||
{
|
||||
*this = bfloat16_t(float(*this) + float(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply
|
||||
__host__ __device__ __forceinline__ bfloat16_t operator*(const bfloat16_t& other) const
|
||||
{
|
||||
return bfloat16_t(float(*this) * float(other));
|
||||
}
|
||||
|
||||
/// Add
|
||||
__host__ __device__ __forceinline__ bfloat16_t operator+(const bfloat16_t& other) const
|
||||
{
|
||||
return bfloat16_t(float(*this) + float(other));
|
||||
}
|
||||
|
||||
/// Sub
|
||||
__host__ __device__ __forceinline__ bfloat16_t operator-(const bfloat16_t& other) const
|
||||
{
|
||||
return bfloat16_t(float(*this) - float(other));
|
||||
}
|
||||
|
||||
/// Less-than
|
||||
__host__ __device__ __forceinline__ bool operator<(const bfloat16_t& other) const
|
||||
{
|
||||
return float(*this) < float(other);
|
||||
}
|
||||
|
||||
/// Less-than-equal
|
||||
__host__ __device__ __forceinline__ bool operator<=(const bfloat16_t& other) const
|
||||
{
|
||||
return float(*this) <= float(other);
|
||||
}
|
||||
|
||||
/// Greater-than
|
||||
__host__ __device__ __forceinline__ bool operator>(const bfloat16_t& other) const
|
||||
{
|
||||
return float(*this) > float(other);
|
||||
}
|
||||
|
||||
/// Greater-than-equal
|
||||
__host__ __device__ __forceinline__ bool operator>=(const bfloat16_t& other) const
|
||||
{
|
||||
return float(*this) >= float(other);
|
||||
}
|
||||
|
||||
/// numeric_traits<bfloat16_t>::max
|
||||
__host__ __device__ __forceinline__ static bfloat16_t(max)()
|
||||
{
|
||||
uint16_t max_word = 0x7F7F;
|
||||
return reinterpret_cast<bfloat16_t&>(max_word);
|
||||
}
|
||||
|
||||
/// numeric_traits<bfloat16_t>::lowest
|
||||
__host__ __device__ __forceinline__ static bfloat16_t lowest()
|
||||
{
|
||||
uint16_t lowest_word = 0xFF7F;
|
||||
return reinterpret_cast<bfloat16_t&>(lowest_word);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* I/O stream overloads
|
||||
******************************************************************************/
|
||||
|
||||
/// Insert formatted \p bfloat16_t into the output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, const bfloat16_t& x)
|
||||
{
|
||||
out << (float) x;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Insert formatted \p __nv_bfloat16 into the output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, const __nv_bfloat16& x)
|
||||
{
|
||||
return out << bfloat16_t(x);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Traits overloads
|
||||
******************************************************************************/
|
||||
|
||||
namespace cuda
|
||||
{
|
||||
template <>
|
||||
inline constexpr bool is_floating_point_v<bfloat16_t> = true;
|
||||
}
|
||||
|
||||
template <>
|
||||
class cuda::std::numeric_limits<bfloat16_t>
|
||||
{
|
||||
public:
|
||||
static constexpr bool is_specialized = true;
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE bfloat16_t max()
|
||||
{
|
||||
return bfloat16_t(numeric_limits<__nv_bfloat16>::max());
|
||||
}
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE bfloat16_t min()
|
||||
{
|
||||
return bfloat16_t(numeric_limits<__nv_bfloat16>::min());
|
||||
}
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE bfloat16_t lowest()
|
||||
{
|
||||
return bfloat16_t(numeric_limits<__nv_bfloat16>::lowest());
|
||||
}
|
||||
};
|
||||
|
||||
CUB_NAMESPACE_BEGIN
|
||||
|
||||
template <>
|
||||
struct NumericTraits<bfloat16_t> : BaseTraits<FLOATING_POINT, true, uint16_t, bfloat16_t>
|
||||
{};
|
||||
|
||||
CUB_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
51
cccl_upstream/c2h/include/c2h/catch2_main.h
Normal file
51
cccl_upstream/c2h/include/c2h/catch2_main.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/config/device_system.h>
|
||||
|
||||
#include <c2h/detail/generators.cuh>
|
||||
|
||||
//! @file
|
||||
//! This file includes a custom Catch2 main function. When CMake is configured to build each test as a separate
|
||||
//! executable, this header is included into each test. On the other hand, when all the tests are compiled into a single
|
||||
//! executable, this header is excluded from the tests and included into catch2_runner.cpp
|
||||
|
||||
#include <catch2/catch_session.hpp>
|
||||
|
||||
#ifdef C2H_CONFIG_MAIN
|
||||
# if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
# include <c2h/catch2_runner_helper.h>
|
||||
|
||||
# ifndef C2H_EXCLUDE_CATCH2_HELPER_IMPL
|
||||
# include "catch2_runner_helper.inl"
|
||||
# endif // !C2H_EXCLUDE_CATCH2_HELPER_IMPL
|
||||
# endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Catch::Session session;
|
||||
|
||||
# if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
int device_id{};
|
||||
|
||||
// Build a new parser on top of Catch's
|
||||
using namespace Catch::Clara;
|
||||
auto cli = session.cli() | Opt(device_id, "device")["-d"]["--device"]("device id to use");
|
||||
session.cli(cli);
|
||||
|
||||
int returnCode = session.applyCommandLine(argc, argv);
|
||||
if (returnCode != 0)
|
||||
{
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
set_device(device_id);
|
||||
# endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
c2h::detail::init_generator();
|
||||
const auto ret = session.run();
|
||||
c2h::detail::cleanup_generator();
|
||||
return ret;
|
||||
}
|
||||
#endif // C2H_CONFIG_MAIN
|
||||
7
cccl_upstream/c2h/include/c2h/catch2_runner_helper.h
Normal file
7
cccl_upstream/c2h/include/c2h/catch2_runner_helper.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
int device_guard(int device_id);
|
||||
void set_device(int device_id);
|
||||
670
cccl_upstream/c2h/include/c2h/catch2_test_helper.h
Normal file
670
cccl_upstream/c2h/include/c2h/catch2_test_helper.h
Normal file
@@ -0,0 +1,670 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#include <cuda/__memory_resource/legacy_pinned_memory_resource.h>
|
||||
#include <cuda/__nvtx/nvtx.h>
|
||||
#include <cuda/buffer>
|
||||
#include <cuda/std/bit>
|
||||
#include <cuda/std/cmath>
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/std/type_traits>
|
||||
#include <cuda/std/utility>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include <c2h/catch2_main.h>
|
||||
#include <c2h/catch2_test_macros.h>
|
||||
#include <c2h/checked_allocator.cuh>
|
||||
#include <c2h/device_policy.h>
|
||||
#include <c2h/extended_types.h>
|
||||
#include <c2h/test_util_vec.h>
|
||||
#include <c2h/utility.h>
|
||||
#include <c2h/vector.h>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators_all.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
#include <catch2/matchers/catch_matchers_templated.hpp>
|
||||
#include <catch2/matchers/catch_matchers_vector.hpp>
|
||||
|
||||
#ifndef VAR_IDX
|
||||
# define VAR_IDX 0
|
||||
#endif
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
template <typename... Ts>
|
||||
using type_list = ::cuda::std::__type_list<Ts...>;
|
||||
|
||||
template <typename TypeList>
|
||||
using size = ::cuda::std::__type_list_size<TypeList>;
|
||||
|
||||
template <std::size_t Index, typename TypeList>
|
||||
using get = ::cuda::std::__type_at_c<Index, TypeList>;
|
||||
|
||||
template <class... TypeLists>
|
||||
using cartesian_product = ::cuda::std::__type_cartesian_product<TypeLists...>;
|
||||
|
||||
template <typename T, T... Ts>
|
||||
using enum_type_list = ::cuda::std::__type_value_list<T, Ts...>;
|
||||
|
||||
template <typename T0, typename T1>
|
||||
using pair = ::cuda::std::__type_pair<T0, T1>;
|
||||
|
||||
template <typename P>
|
||||
using first = ::cuda::std::__type_pair_first<P>;
|
||||
|
||||
template <typename P>
|
||||
using second = ::cuda::std::__type_pair_second<P>;
|
||||
|
||||
template <std::size_t Start, std::size_t Size, std::size_t Stride = 1>
|
||||
using iota = ::cuda::std::__type_iota<std::size_t, Start, Size, Stride>;
|
||||
|
||||
template <typename TypeList, typename T>
|
||||
using remove = ::cuda::std::__type_remove<TypeList, T>;
|
||||
|
||||
/**
|
||||
* Return a value of type `T` with the same bitwise representation of `in`.
|
||||
* Types `T` and `U` must be the same size.
|
||||
*/
|
||||
template <typename T, typename U>
|
||||
__host__ __device__ constexpr T SafeBitCast(const U& in) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) == sizeof(U), "Types must be same size.");
|
||||
T out;
|
||||
memcpy(&out, &in, sizeof(T));
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr bool isnan(T value) noexcept
|
||||
{
|
||||
return cuda::std::isnan(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(float1 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(float2 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.y) || cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(float3 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.z) || cuda::std::isnan(val.y) || cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(float4 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.y) || cuda::std::isnan(val.x) || cuda::std::isnan(val.w) || cuda::std::isnan(val.z));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(double1 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(double2 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.y) || cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(double3 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.z) || cuda::std::isnan(val.y) || cuda::std::isnan(val.x));
|
||||
}
|
||||
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
[[nodiscard]] constexpr bool isnan(double4 val) noexcept
|
||||
{
|
||||
return (cuda::std::isnan(val.y) || cuda::std::isnan(val.x) || cuda::std::isnan(val.w) || cuda::std::isnan(val.z));
|
||||
}
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
|
||||
// TODO: move to libcu++
|
||||
#if TEST_HALF_T()
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(__half2 value) noexcept
|
||||
{
|
||||
return cuda::std::isnan(value.x) || cuda::std::isnan(value.y);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(half_t val) noexcept
|
||||
{
|
||||
const auto bits = SafeBitCast<uint16_t>(val);
|
||||
// commented bit is always true, leaving for documentation:
|
||||
return (((bits >= 0x7C01) && (bits <= 0x7FFF)) || ((bits >= 0xFC01) /*&& (bits <= 0xFFFFFFFF)*/));
|
||||
}
|
||||
|
||||
#endif // TEST_HALF_T()
|
||||
|
||||
#if TEST_BF_T()
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(__nv_bfloat162 value) noexcept
|
||||
{
|
||||
return cuda::std::isnan(value.x) || cuda::std::isnan(value.y);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isnan(bfloat16_t val) noexcept
|
||||
{
|
||||
const auto bits = SafeBitCast<uint16_t>(val);
|
||||
// commented bit is always true, leaving for documentation:
|
||||
return (((bits >= 0x7F81) && (bits <= 0x7FFF)) || ((bits >= 0xFF81) /*&& (bits <= 0xFFFFFFFF)*/));
|
||||
}
|
||||
|
||||
#endif // TEST_BF_T()
|
||||
} // namespace c2h
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class T>
|
||||
std::vector<T> to_vec(c2h::device_vector<T> const& vec)
|
||||
{
|
||||
c2h::host_vector<T> temp = vec;
|
||||
return std::vector<T>{temp.begin(), temp.end()};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<T> to_vec(c2h::host_vector<T> const& vec)
|
||||
{
|
||||
return std::vector<T>{vec.begin(), vec.end()};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<T> to_vec(std::vector<T> const& vec)
|
||||
{
|
||||
return vec;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
#define REQUIRE_APPROX_EQ(ref, out) \
|
||||
{ \
|
||||
auto vec_ref = detail::to_vec(ref); \
|
||||
auto vec_out = detail::to_vec(out); \
|
||||
REQUIRE_THAT(vec_ref, Catch::Matchers::Approx(vec_out)); \
|
||||
}
|
||||
|
||||
#define REQUIRE_APPROX_EQ_EPSILON(ref, out, eps) \
|
||||
{ \
|
||||
auto vec_ref = detail::to_vec(ref); \
|
||||
auto vec_out = detail::to_vec(out); \
|
||||
REQUIRE_THAT(vec_ref, Catch::Matchers::Approx(vec_out).epsilon(eps)); \
|
||||
}
|
||||
|
||||
#define REQUIRE_APPROX_EQ_ABS(ref, out, abs) \
|
||||
{ \
|
||||
auto vec_ref = detail::to_vec(ref); \
|
||||
auto vec_out = detail::to_vec(out); \
|
||||
REQUIRE_THAT(vec_ref, Catch::Matchers::Approx(vec_out).margin(abs)); \
|
||||
}
|
||||
|
||||
namespace c2h::detail
|
||||
{
|
||||
// Copy of Catch2::MatchExpr, but streamReconstructedExpression does not print arg
|
||||
template <typename ArgT, typename MatcherT>
|
||||
class QuietMatchExpr : public Catch::ITransientExpression
|
||||
{
|
||||
ArgT&& m_arg;
|
||||
MatcherT const& m_matcher;
|
||||
|
||||
public:
|
||||
constexpr QuietMatchExpr(ArgT&& arg, MatcherT const& matcher)
|
||||
: ITransientExpression{true, matcher.match(arg)}
|
||||
, m_arg(CATCH_FORWARD(arg))
|
||||
, m_matcher(matcher)
|
||||
{}
|
||||
|
||||
void streamReconstructedExpression(std::ostream& os) const override
|
||||
{
|
||||
os << m_matcher.toString();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ArgT, typename MatcherT>
|
||||
QuietMatchExpr(ArgT&&, MatcherT) -> QuietMatchExpr<ArgT, MatcherT>;
|
||||
} // namespace c2h::detail
|
||||
|
||||
// Copy of Catch2's INTERNAL_CHECK_THAT macro, but using QuietMatchExpr to suppress printing arg
|
||||
#define INTERNAL_CHECK_THAT_QUIET(macroName, matcher, resultDisposition, arg) \
|
||||
do \
|
||||
{ \
|
||||
Catch::AssertionHandler catchAssertionHandler( \
|
||||
macroName##_catch_sr, \
|
||||
CATCH_INTERNAL_LINEINFO, \
|
||||
CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), \
|
||||
resultDisposition); \
|
||||
INTERNAL_CATCH_TRY \
|
||||
{ \
|
||||
catchAssertionHandler.handleExpr(::c2h::detail::QuietMatchExpr(arg, matcher)); \
|
||||
} \
|
||||
INTERNAL_CATCH_CATCH(catchAssertionHandler) \
|
||||
catchAssertionHandler.complete(); \
|
||||
} while (false)
|
||||
|
||||
// Copy of Catch2's CHECK_THAT macro, but suppressing printing arg
|
||||
#define CHECK_THAT_QUIET(arg, matcher) \
|
||||
INTERNAL_CHECK_THAT_QUIET("CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg)
|
||||
|
||||
// Copy of Catch2's REQUIRE_THAT macro, but suppressing printing arg
|
||||
#define REQUIRE_THAT_QUIET(arg, matcher) \
|
||||
INTERNAL_CHECK_THAT_QUIET("REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg)
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// Returns true if values are equal, or both NaN:
|
||||
struct equal_or_nans
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(const T& a, const T& b) const
|
||||
{
|
||||
return (c2h::isnan(a) && c2h::isnan(b)) || a == b;
|
||||
}
|
||||
};
|
||||
|
||||
struct bitwise_equal
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(const T& a, const T& b) const
|
||||
{
|
||||
return ::cuda::std::memcmp(&a, &b, sizeof(T)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Catch2 Matcher that calls `std::equal` with a default-constructable custom predicate
|
||||
template <typename Range, typename Pred>
|
||||
struct CustomEqualsRangeMatcher : Catch::Matchers::MatcherBase<Range>
|
||||
{
|
||||
CustomEqualsRangeMatcher(Range const& range)
|
||||
: range{range}
|
||||
{}
|
||||
|
||||
bool match(Range const& other) const override
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
return std::equal(begin(range), end(range), begin(other), Pred{});
|
||||
}
|
||||
|
||||
std::string describe() const override
|
||||
{
|
||||
return "Equals: " + Catch::rangeToString(range);
|
||||
}
|
||||
|
||||
private:
|
||||
Range const& range;
|
||||
};
|
||||
|
||||
template <typename Range>
|
||||
auto NaNEqualsRange(const Range& range) -> CustomEqualsRangeMatcher<Range, equal_or_nans>
|
||||
{
|
||||
return CustomEqualsRangeMatcher<Range, equal_or_nans>(range);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
auto BitwiseEqualsRange(const Range& range) -> CustomEqualsRangeMatcher<Range, bitwise_equal>
|
||||
{
|
||||
return CustomEqualsRangeMatcher<Range, bitwise_equal>(range);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
#define REQUIRE_EQ_WITH_NAN_MATCHING(ref, out) \
|
||||
{ \
|
||||
auto vec_ref = detail::to_vec(ref); \
|
||||
auto vec_out = detail::to_vec(out); \
|
||||
REQUIRE_THAT(vec_ref, detail::NaNEqualsRange(vec_out)); \
|
||||
}
|
||||
|
||||
#define REQUIRE_BITWISE_EQ(ref, out) \
|
||||
{ \
|
||||
auto vec_ref = detail::to_vec(ref); \
|
||||
auto vec_out = detail::to_vec(out); \
|
||||
REQUIRE_THAT(vec_ref, detail::NaNEqualsRange(vec_out)); \
|
||||
}
|
||||
|
||||
namespace c2h::detail
|
||||
{
|
||||
template <typename T>
|
||||
struct indexed_value_t
|
||||
{
|
||||
size_t index;
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct element_compare_result_t
|
||||
{
|
||||
size_t index;
|
||||
T actual;
|
||||
T expected;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct vector_compare_result_t
|
||||
{
|
||||
size_t actual_size;
|
||||
size_t expected_size;
|
||||
size_t total_mismatches;
|
||||
std::vector<indexed_value_t<T>> good_values;
|
||||
std::vector<element_compare_result_t<T>> first_mismatches;
|
||||
std::optional<std::vector<element_compare_result_t<T>>> last_mismatches;
|
||||
};
|
||||
|
||||
template <typename LhsRange, typename RhsRange, typename T = typename LhsRange::value_type>
|
||||
auto compare_host_ranges(const LhsRange& actual, const RhsRange& expected) -> vector_compare_result_t<T>
|
||||
{
|
||||
constexpr size_t good_values_before_mismatch = 3;
|
||||
constexpr size_t first_mismatches_count = 5;
|
||||
constexpr size_t last_mismatches_count = 5;
|
||||
|
||||
vector_compare_result_t<T> result{};
|
||||
result.actual_size = actual.size();
|
||||
result.expected_size = expected.size();
|
||||
if (result.actual_size != result.expected_size)
|
||||
{
|
||||
result.total_mismatches = actual.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<element_compare_result_t<T>> mismatches;
|
||||
mismatches.reserve(actual.size()); // TODO(bgruber): this seems excessive
|
||||
for (size_t i = 0; i < actual.size(); ++i)
|
||||
{
|
||||
if (actual[i] != expected[i])
|
||||
{
|
||||
if (mismatches.empty()) // at the first mismatch
|
||||
{
|
||||
// store up to 3 good values before the first mismatch
|
||||
const size_t count = ::cuda::std::min(good_values_before_mismatch, i);
|
||||
for (size_t j = i - count; j < i; j++)
|
||||
{
|
||||
result.good_values.emplace_back(indexed_value_t<T>{j, actual[j]});
|
||||
}
|
||||
}
|
||||
mismatches.emplace_back(element_compare_result_t<T>{i, actual[i], expected[i]});
|
||||
}
|
||||
}
|
||||
result.total_mismatches = mismatches.size();
|
||||
|
||||
// Handle first mismatches
|
||||
size_t first_count = cuda::std::min<size_t>(mismatches.size(), first_mismatches_count);
|
||||
result.first_mismatches.assign(mismatches.begin(), mismatches.begin() + first_count);
|
||||
|
||||
// Handle last mismatches
|
||||
if (mismatches.size() > first_mismatches_count)
|
||||
{
|
||||
const auto start =
|
||||
mismatches.end() - cuda::std::min<size_t>(mismatches.size() - first_mismatches_count, last_mismatches_count);
|
||||
result.last_mismatches.emplace(start, mismatches.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto compare_vectors(const host_vector<T>& actual, const host_vector<T>& expected) -> vector_compare_result_t<T>
|
||||
{
|
||||
return compare_host_ranges(actual, expected);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto compare_vectors(const device_vector<T>& actual, const device_vector<T>& expected) -> vector_compare_result_t<T>
|
||||
{
|
||||
return compare_vectors<T>(host_vector<T>(actual), host_vector<T>(expected));
|
||||
}
|
||||
|
||||
template <typename T, typename... LhsProps, typename... RhsProps>
|
||||
auto compare_vectors(const cuda::buffer<T, LhsProps...>& actual, const cuda::buffer<T, RhsProps...>& expected)
|
||||
-> vector_compare_result_t<T>
|
||||
{
|
||||
const auto actual_host = cuda::make_buffer(actual.stream(), cuda::mr::legacy_pinned_memory_resource{}, actual);
|
||||
const auto expected_host = cuda::make_buffer(expected.stream(), cuda::mr::legacy_pinned_memory_resource{}, expected);
|
||||
|
||||
actual.stream().sync();
|
||||
expected.stream().sync();
|
||||
return compare_host_ranges(actual_host, expected_host);
|
||||
}
|
||||
|
||||
template <typename LhsVec, typename RhsVec, typename T = typename LhsVec::value_type>
|
||||
auto compare_vectors(const LhsVec& actual, const RhsVec& expected) -> vector_compare_result_t<T>
|
||||
{
|
||||
return compare_vectors<T>(host_vector<T>(actual), host_vector<T>(expected));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_comparison(const vector_compare_result_t<T>& res, std::ostream& os)
|
||||
{
|
||||
if (res.actual_size != res.expected_size)
|
||||
{
|
||||
os << "Actual size (" << res.actual_size << ") != expected size (" << res.expected_size << ")\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mismatch_percent = (static_cast<double>(res.total_mismatches) / res.actual_size) * 100.0;
|
||||
os << res.total_mismatches << " mismatch" << (res.total_mismatches > 1 ? "es" : "") << " (" << std::fixed
|
||||
<< std::setprecision(2) << mismatch_percent << "% of " << res.expected_size << " elements)\n";
|
||||
|
||||
// print good values
|
||||
for (const auto& [idx, v] : res.good_values)
|
||||
{
|
||||
os << "good [" << idx << "]: " << CoutCast(v) << " == " << CoutCast(v) << '\n';
|
||||
}
|
||||
|
||||
// insert dots between mismatches that are not consecutive
|
||||
size_t last_printed_idx = res.good_values.empty() ? 0 : res.good_values.back().index;
|
||||
auto print_dots = [&](size_t idx) {
|
||||
if (last_printed_idx + 1 != idx)
|
||||
{
|
||||
os << "...\n";
|
||||
}
|
||||
last_printed_idx = idx;
|
||||
};
|
||||
|
||||
// print first mismatches
|
||||
for (const auto& [idx, a, b] : res.first_mismatches)
|
||||
{
|
||||
print_dots(idx);
|
||||
os << "BAD [" << idx << "]: " << CoutCast(a) << " != " << CoutCast(b) << '\n';
|
||||
}
|
||||
|
||||
// print last mismatches if we have any
|
||||
if (res.last_mismatches)
|
||||
{
|
||||
for (const auto& [idx, a, b] : *res.last_mismatches)
|
||||
{
|
||||
print_dots(idx);
|
||||
os << "BAD [" << idx << "]: " << CoutCast(a) << " != " << CoutCast(b) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Vec>
|
||||
struct vector_matcher : Catch::Matchers::MatcherGenericBase
|
||||
{
|
||||
vector_matcher(Vec const& expected)
|
||||
: expected_vec{expected}
|
||||
{}
|
||||
|
||||
template <typename OtherVec>
|
||||
bool match(OtherVec const& actual_vec) const // TODO(Bgruber): remove const?
|
||||
{
|
||||
comparison_result = compare_vectors(actual_vec, expected_vec);
|
||||
return comparison_result.total_mismatches == 0;
|
||||
}
|
||||
|
||||
std::string describe() const override
|
||||
{
|
||||
std::stringstream ss;
|
||||
print_comparison(comparison_result, ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable vector_compare_result_t<typename Vec::value_type> comparison_result;
|
||||
Vec const& expected_vec;
|
||||
};
|
||||
} // namespace c2h::detail
|
||||
|
||||
//! Compare thrust vectors in a match expression. Example: CHECK_THAT_QUIET(vec_a, Equals(vec_v))
|
||||
template <typename T, typename Alloc>
|
||||
auto Equals(const THRUST_NS_QUALIFIER::detail::vector_base<T, Alloc>& expected)
|
||||
-> c2h::detail::vector_matcher<THRUST_NS_QUALIFIER::detail::vector_base<T, Alloc>>
|
||||
{
|
||||
return {expected};
|
||||
}
|
||||
|
||||
template <typename T, typename... Props>
|
||||
auto Equals(const cuda::buffer<T, Props...>& expected) -> c2h::detail::vector_matcher<cuda::buffer<T, Props...>>
|
||||
{
|
||||
return {expected};
|
||||
}
|
||||
|
||||
#include <cuda/std/tuple>
|
||||
#include <cuda/std/utility>
|
||||
|
||||
_CCCL_BEGIN_NAMESPACE_CUDA_STD
|
||||
template <typename T1,
|
||||
typename T2,
|
||||
// provide this operator only when the pair's content is also streamable
|
||||
::cuda::std::void_t<decltype(::cuda::std::declval<::std::ostream>()
|
||||
<< ::cuda::std::declval<T1>() << ::cuda::std::declval<T2>())>* = nullptr>
|
||||
::std::ostream& operator<<(::std::ostream& os, const pair<T1, T2>& pair)
|
||||
{
|
||||
return os << "[" << pair.first << ", " << pair.second << "]";
|
||||
}
|
||||
|
||||
template <size_t N, typename... T>
|
||||
enable_if_t<(N == sizeof...(T))> print_elem(::std::ostream&, const tuple<T...>&)
|
||||
{}
|
||||
|
||||
template <size_t N, typename... T>
|
||||
enable_if_t<(N < sizeof...(T))> print_elem(::std::ostream& os, const tuple<T...>& tup)
|
||||
{
|
||||
if constexpr (N != 0)
|
||||
{
|
||||
os << ", ";
|
||||
}
|
||||
os << ::cuda::std::get<N>(tup);
|
||||
::cuda::std::print_elem<N + 1>(os, tup);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
::std::ostream& operator<<(::std::ostream& os, const tuple<T...>& tup)
|
||||
{
|
||||
os << "[";
|
||||
::cuda::std::print_elem<0>(os, tup);
|
||||
return os << "]";
|
||||
}
|
||||
_CCCL_END_NAMESPACE_CUDA_STD
|
||||
|
||||
_CCCL_BEGIN_NAMESPACE_CUDA
|
||||
template <typename T, typename... Props>
|
||||
::std::ostream& operator<<(::std::ostream& os, const cuda::buffer<T, Props...>& buffer)
|
||||
{
|
||||
const auto host_buf = cuda::make_buffer(buffer.stream(), cuda::mr::legacy_pinned_memory_resource{}, buffer);
|
||||
|
||||
buffer.stream().sync();
|
||||
os << ::Catch::Detail::stringify(::std::vector<T>{host_buf.begin(), host_buf.end()});
|
||||
return os;
|
||||
}
|
||||
_CCCL_END_NAMESPACE_CUDA
|
||||
|
||||
template <>
|
||||
struct Catch::StringMaker<cudaError>
|
||||
{
|
||||
static auto convert(cudaError e) -> std::string
|
||||
{
|
||||
return std::to_string(cuda::std::to_underlying(e)) + " (" + cudaGetErrorString(e) + ")";
|
||||
}
|
||||
};
|
||||
|
||||
#include <c2h/custom_type.h>
|
||||
#include <c2h/generators.h>
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct nvtx_c2h_domain
|
||||
{
|
||||
static constexpr const char* name = "C2H";
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class nvtx_fixture
|
||||
{
|
||||
#if _CCCL_HAS_NVTX3()
|
||||
::nvtx3::v1::scoped_range_in<nvtx_c2h_domain> nvtx_range{Catch::getResultCapture().getCurrentTestName()};
|
||||
#endif // _CCCL_HAS_NVTX3()
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
#define C2H_TEST_NAME_IMPL(NAME, PARAM) C2H_TEST_STR(NAME) "(" C2H_TEST_STR(PARAM) ")"
|
||||
|
||||
#define C2H_TEST_NAME(NAME) C2H_TEST_NAME_IMPL(NAME, VAR_IDX)
|
||||
|
||||
#define C2H_TEST_CONCAT(A, B) C2H_TEST_CONCAT_INNER(A, B)
|
||||
#define C2H_TEST_CONCAT_INNER(A, B) A##B
|
||||
|
||||
#define C2H_TEST_IMPL(ID, NAME, TAG, ...) \
|
||||
using C2H_TEST_CONCAT(types_, ID) = c2h::cartesian_product<__VA_ARGS__>; \
|
||||
CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(::detail::nvtx_fixture, C2H_TEST_NAME(NAME), TAG, C2H_TEST_CONCAT(types_, ID))
|
||||
|
||||
#define C2H_TEST(NAME, TAG, ...) C2H_TEST_IMPL(__LINE__, NAME, TAG, __VA_ARGS__)
|
||||
|
||||
#define C2H_TEST_WITH_FIXTURE_IMPL(ID, FIXTURE, NAME, TAG, ...) \
|
||||
using C2H_TEST_CONCAT(types_, ID) = c2h::cartesian_product<__VA_ARGS__>; \
|
||||
CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(FIXTURE, C2H_TEST_NAME(NAME), TAG, C2H_TEST_CONCAT(types_, ID))
|
||||
|
||||
#define C2H_TEST_WITH_FIXTURE(FIXTURE, NAME, TAG, ...) \
|
||||
C2H_TEST_WITH_FIXTURE_IMPL(__LINE__, FIXTURE, NAME, TAG, __VA_ARGS__)
|
||||
|
||||
#define C2H_TEST_LIST_IMPL(ID, NAME, TAG, ...) \
|
||||
using C2H_TEST_CONCAT(types_, ID) = c2h::type_list<__VA_ARGS__>; \
|
||||
CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(::detail::nvtx_fixture, C2H_TEST_NAME(NAME), TAG, C2H_TEST_CONCAT(types_, ID))
|
||||
|
||||
#define C2H_TEST_LIST(NAME, TAG, ...) C2H_TEST_LIST_IMPL(__LINE__, NAME, TAG, __VA_ARGS__)
|
||||
|
||||
#define C2H_TEST_LIST_WITH_FIXTURE_IMPL(ID, FIXTURE, NAME, TAG, ...) \
|
||||
using C2H_TEST_CONCAT(types_, ID) = c2h::type_list<__VA_ARGS__>; \
|
||||
CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(FIXTURE, C2H_TEST_NAME(NAME), TAG, C2H_TEST_CONCAT(types_, ID))
|
||||
|
||||
#define C2H_TEST_LIST_WITH_FIXTURE(FIXTURE, NAME, TAG, ...) \
|
||||
C2H_TEST_LIST_WITH_FIXTURE_IMPL(__LINE__, FIXTURE, NAME, TAG, __VA_ARGS__)
|
||||
|
||||
#define C2H_TEST_STR(a) #a
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
inline std::size_t get_override_seed_count()
|
||||
{
|
||||
// Setting this environment variable forces a fixed number of seeds to be generated, regardless of the requested
|
||||
// count. Set to 1 to reduce redundant, expensive testing when using sanitizers, etc.
|
||||
static std::optional<std::string> override_str = c2h::detail::get_env("C2H_SEED_COUNT_OVERRIDE");
|
||||
static const int override_seeds = override_str ? std::atoi(override_str->c_str()) : 0;
|
||||
return override_seeds;
|
||||
}
|
||||
|
||||
inline std::size_t adjust_seed_count(std::size_t requested)
|
||||
{
|
||||
static std::size_t override_seeds = get_override_seed_count();
|
||||
return override_seeds != 0 ? override_seeds : requested;
|
||||
}
|
||||
} // namespace c2h
|
||||
|
||||
#define C2H_SEED(N) \
|
||||
c2h::seed_t \
|
||||
{ \
|
||||
GENERATE_COPY(take(c2h::adjust_seed_count(N), \
|
||||
random(::cuda::std::numeric_limits<unsigned long long int>::min(), \
|
||||
::cuda::std::numeric_limits<unsigned long long int>::max()))) \
|
||||
}
|
||||
200
cccl_upstream/c2h/include/c2h/catch2_test_macros.h
Normal file
200
cccl_upstream/c2h/include/c2h/catch2_test_macros.h
Normal file
@@ -0,0 +1,200 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda/std/detail/__config>
|
||||
|
||||
#include <nv/target>
|
||||
|
||||
#include <catch2/catch_message.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/matchers/catch_matchers.hpp>
|
||||
|
||||
// This file implements Catch2's test macros that work both in host and device code. We globally define the
|
||||
// CATCH_CONFIG_PREFIX_ALL macro to force Catch2 to prepend it's macros with CATCH_ prefix. That allows us to implement
|
||||
// the non-prefixed versions ourselves.
|
||||
//
|
||||
// In host code, we just use the CATCH_-prefixed variant, in device code we implement the functionality, so it
|
||||
// corresponds the desired functionality.
|
||||
//
|
||||
// Only a subset of the Catch2's macro are provided. If needed, feel free to extend the support. Host-only macros can
|
||||
// be determined by missing NV_IF_ELSE_TARGET wrapper and immediate dispatch to CATCH_-prefixed variant.
|
||||
|
||||
// workaround for error #3185-D: no '#pragma diagnostic push' was found to match this 'diagnostic pop'
|
||||
#if _CCCL_COMPILER(NVHPC)
|
||||
# undef CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
# undef CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("diag push")
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("diag pop")
|
||||
#endif
|
||||
// The nv_diagnostic pragmas in Catch2 macros cause cicc to hang indefinitely in CTK 13.0.
|
||||
// See NVBugs 5475335.
|
||||
#if _CCCL_VERSION_COMPARE(_CCCL_CTK_, _CCCL_CTK, ==, 13, 0)
|
||||
# undef CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
# undef CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
#endif
|
||||
// workaround for error
|
||||
// * MSVC14.39: #3185-D: no '#pragma diagnostic push' was found to match this 'diagnostic pop'
|
||||
// * MSVC14.29: internal error: assertion failed: alloc_copy_of_pending_pragma: copied pragma has source sequence entry
|
||||
// (pragma.c, line 526 in alloc_copy_of_pending_pragma)
|
||||
// see also upstream Catch2 issue: https://github.com/catchorg/Catch2/issues/2636
|
||||
#if _CCCL_COMPILER(MSVC)
|
||||
# undef CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
# undef CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
|
||||
#endif
|
||||
|
||||
// We must pass the COND as a cstring parameter, because it might contain the '%' character that would break the printf
|
||||
// formatting.
|
||||
#define C2H_INTERNAL_DEVICE_TEST_PRINT(KIND, COND) \
|
||||
::printf( \
|
||||
__FILE__ \
|
||||
":" _CCCL_TO_STRING(__LINE__) ":\n " KIND "(%s) failed\n block [%u, %u, %u], thread [%u, %u, %u]\n\n", \
|
||||
COND, \
|
||||
blockIdx.x, \
|
||||
blockIdx.y, \
|
||||
blockIdx.z, \
|
||||
threadIdx.x, \
|
||||
threadIdx.y, \
|
||||
threadIdx.z)
|
||||
|
||||
// <catch2/catch2_test_macros.hpp>
|
||||
|
||||
#define REQUIRE(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_REQUIRE(__VA_ARGS__);), ({ \
|
||||
if (!(__VA_ARGS__)) \
|
||||
{ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("REQUIRE", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
} \
|
||||
}))
|
||||
#define REQUIRE_FALSE(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_REQUIRE_FALSE(__VA_ARGS__);), ({ \
|
||||
if (__VA_ARGS__) \
|
||||
{ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("REQUIRE_FALSE", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
} \
|
||||
}))
|
||||
|
||||
#define REQUIRE_THROWS(...) CATCH_REQUIRE_THROWS(__VA_ARGS__)
|
||||
#define REQUIRE_THROWS_AS(...) CATCH_REQUIRE_THROWS_AS(__VA_ARGS__)
|
||||
#define REQUIRE_NOTHROW(...) NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_REQUIRE_NOTHROW(__VA_ARGS__);), (__VA_ARGS__;))
|
||||
|
||||
#define CHECK(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_CHECK(__VA_ARGS__);), ({ \
|
||||
if (!(__VA_ARGS__)) \
|
||||
{ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("CHECK", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
} \
|
||||
}))
|
||||
#define CHECK_FALSE(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_CHECK_FALSE(__VA_ARGS__);), ({ \
|
||||
if (__VA_ARGS__) \
|
||||
{ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("CHECK_FALSE", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
} \
|
||||
}))
|
||||
#define CHECKED_IF(...) CATCH_CHECKED_IF(__VA_ARGS__)
|
||||
#define CHECKED_ELSE(...) CATCH_CHECKED_ELSE(__VA_ARGS__)
|
||||
#define CHECK_NOFAIL(...) CATCH_CHECK_NOFAIL(__VA_ARGS__)
|
||||
|
||||
#define CHECK_THROWS(...) CATCH_CHECK_THROWS(__VA_ARGS__)
|
||||
#define CHECK_THROWS_AS(...) CATCH_CHECK_THROWS_AS(__VA_ARGS__)
|
||||
#define CHECK_NOTHROW(...) NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_CHECK_NOTHROW(__VA_ARGS__);), (__VA_ARGS__;))
|
||||
|
||||
#define TEST_CASE(...) CATCH_TEST_CASE(__VA_ARGS__)
|
||||
#define TEST_CASE_METHOD(...) CATCH_TEST_CASE_METHOD(__VA_ARGS__)
|
||||
#define METHOD_AS_TEST_CASE(...) CATCH_METHOD_AS_TEST_CASE(__VA_ARGS__)
|
||||
#define REGISTER_TEST_CASE(...) CATCH_REGISTER_TEST_CASE(__VA_ARGS__)
|
||||
#define SECTION(...) CATCH_SECTION(__VA_ARGS__)
|
||||
#define DYNAMIC_SECTION(...) CATCH_DYNAMIC_SECTION(__VA_ARGS__)
|
||||
#define FAIL(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_FAIL(__VA_ARGS__);), ({ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("FAIL", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
}))
|
||||
#define FAIL_CHECK(...) CATCH_FAIL_CHECK(__VA_ARGS__)
|
||||
#define SUCCEED(...) CATCH_SUCCEED(__VA_ARGS__)
|
||||
#define SKIP(...) CATCH_SKIP(__VA_ARGS__)
|
||||
|
||||
#define STATIC_REQUIRE(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_STATIC_REQUIRE(__VA_ARGS__);), (static_assert(__VA_ARGS__, #__VA_ARGS__);))
|
||||
#define STATIC_REQUIRE_FALSE(...) \
|
||||
NV_IF_ELSE_TARGET( \
|
||||
NV_IS_HOST, (CATCH_STATIC_REQUIRE_FALSE(__VA_ARGS__);), (static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")");))
|
||||
#define STATIC_CHECK(...) \
|
||||
NV_IF_ELSE_TARGET(NV_IS_HOST, (CATCH_STATIC_CHECK(__VA_ARGS__);), (static_assert(__VA_ARGS__, #__VA_ARGS__);))
|
||||
#define STATIC_CHECK_FALSE(...) \
|
||||
NV_IF_ELSE_TARGET( \
|
||||
NV_IS_HOST, (CATCH_STATIC_CHECK_FALSE(__VA_ARGS__);), (static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")");))
|
||||
|
||||
#define SCENARIO(...) CATCH_SCENARIO(__VA_ARGS__)
|
||||
#define SCENARIO_METHOD(...) CATCH_SCENARIO_METHOD(__VA_ARGS__)
|
||||
#define GIVEN(...) CATCH_GIVEN(__VA_ARGS__)
|
||||
#define AND_GIVEN(...) CATCH_AND_GIVEN(__VA_ARGS__)
|
||||
#define WHEN(...) CATCH_WHEN(__VA_ARGS__)
|
||||
#define AND_WHEN(...) CATCH_AND_WHEN(__VA_ARGS__)
|
||||
#define THEN(...) CATCH_THEN(__VA_ARGS__)
|
||||
#define AND_THEN(...) CATCH_AND_THEN(__VA_ARGS__)
|
||||
|
||||
// <catch2/catch_message.hpp>
|
||||
|
||||
#define INFO(...) CATCH_INFO(__VA_ARGS__)
|
||||
#define UNSCOPED_INFO(...) CATCH_UNSCOPED_INFO(__VA_ARGS__)
|
||||
#define WARN(...) CATCH_WARN(__VA_ARGS__)
|
||||
#define CAPTURE(...) CATCH_CAPTURE(__VA_ARGS__)
|
||||
|
||||
// <catch2/catch_template_test_macros.hpp>
|
||||
|
||||
#define TEMPLATE_TEST_CASE(...) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_SIG(...) CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_METHOD(...) CATCH_TEMPLATE_TEST_CASE_METHOD(__VA_ARGS__)
|
||||
#define TEMPLATE_TEST_CASE_METHOD_SIG(...) CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(__VA_ARGS__)
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE(...) CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__)
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__)
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(...) CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(__VA_ARGS__)
|
||||
#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(...) CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(__VA_ARGS__)
|
||||
#define TEMPLATE_LIST_TEST_CASE(...) CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
|
||||
#define TEMPLATE_LIST_TEST_CASE_METHOD(...) CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(__VA_ARGS__)
|
||||
|
||||
// <catch2/matchers/catch_matchers.hpp>
|
||||
|
||||
#define REQUIRE_THROWS_WITH(...) CATCH_REQUIRE_THROWS_WITH(__VA_ARGS__)
|
||||
#define REQUIRE_THROWS_MATCHES(...) CATCH_REQUIRE_THROWS_MATCHES(__VA_ARGS__)
|
||||
#define CHECK_THROWS_WITH(...) CATCH_CHECK_THROWS_WITH(__VA_ARGS__)
|
||||
#define CHECK_THROWS_MATCHES(...) CATCH_CHECK_THROWS_MATCHES(__VA_ARGS__)
|
||||
#define CHECK_THAT(...) CATCH_CHECK_THAT(__VA_ARGS__)
|
||||
#define REQUIRE_THAT(...) CATCH_REQUIRE_THAT(__VA_ARGS__)
|
||||
|
||||
// extensions
|
||||
|
||||
// Sometimes clang-cuda has problems with REQUIRE(...) when used in __device__ function - it tries to instantiate the
|
||||
// host path. This is related to clang-cuda's compilation trajectory. For these cases, we provide REQUIRE_DEVICE(...) as
|
||||
// a fallback.
|
||||
#define REQUIRE_DEVICE(...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(__VA_ARGS__)) \
|
||||
{ \
|
||||
C2H_INTERNAL_DEVICE_TEST_PRINT("REQUIRE", #__VA_ARGS__); \
|
||||
::__trap(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// Macros to require/check success of a CUDA Driver call.
|
||||
#define REQUIRE_CUDA(...) REQUIRE((__VA_ARGS__) == CUDA_SUCCESS)
|
||||
#define CHECK_CUDA(...) CHECK((__VA_ARGS__) == CUDA_SUCCESS)
|
||||
|
||||
// Macros to require/check success of a CUDA Runtime call.
|
||||
#define REQUIRE_CUDART(...) REQUIRE((__VA_ARGS__) == cudaSuccess)
|
||||
#define CHECK_CUDART(...) CHECK((__VA_ARGS__) == cudaSuccess)
|
||||
154
cccl_upstream/c2h/include/c2h/check_results.cuh
Normal file
154
cccl_upstream/c2h/include/c2h/check_results.cuh
Normal file
@@ -0,0 +1,154 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cub/detail/type_traits.cuh>
|
||||
#include <cub/util_device.cuh>
|
||||
|
||||
#include <cuda/std/complex>
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
#include <test_util.h>
|
||||
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
|
||||
template <typename T>
|
||||
void verify_results(const c2h::host_vector<T>& expected_data, const c2h::host_vector<T>& test_results)
|
||||
{
|
||||
using namespace cub::detail;
|
||||
int device_id = 0;
|
||||
CubDebugExit(cudaGetDevice(&device_id));
|
||||
int ptx_version = 0;
|
||||
CubDebugExit(CUB_NS_QUALIFIER::PtxVersion(ptx_version, device_id));
|
||||
if (ptx_version < 80 && is_any_bfloat16_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ptx_version < 53 && is_any_half_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if constexpr (cuda::std::is_floating_point_v<T>)
|
||||
{
|
||||
REQUIRE_APPROX_EQ(expected_data, test_results);
|
||||
}
|
||||
else if constexpr (cuda::std::is_same_v<T, __nv_bfloat16> || cuda::std::is_same_v<T, __half>)
|
||||
{
|
||||
constexpr auto rel_err = cuda::std::is_same_v<T, __half> ? 0.08f : 0.2f;
|
||||
REQUIRE_APPROX_EQ_EPSILON(expected_data, test_results, rel_err);
|
||||
}
|
||||
else if constexpr (cuda::std::is_same_v<T, float2>)
|
||||
{
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
REQUIRE_THAT(expected_data[i].x, Catch::Matchers::WithinRel(test_results[i].x, 0.01f));
|
||||
REQUIRE_THAT(expected_data[i].y, Catch::Matchers::WithinRel(test_results[i].y, 0.01f));
|
||||
}
|
||||
}
|
||||
else if constexpr (cuda::std::is_same_v<T, __nv_bfloat162> || cuda::std::is_same_v<T, __half2>)
|
||||
{
|
||||
constexpr auto rel_err = cuda::std::is_same_v<T, __half2> ? 0.08f : 0.2f;
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
REQUIRE_THAT(expected_data[i].x, Catch::Matchers::WithinRel(test_results[i].x, rel_err));
|
||||
REQUIRE_THAT(expected_data[i].y, Catch::Matchers::WithinRel(test_results[i].y, rel_err));
|
||||
}
|
||||
}
|
||||
else if constexpr (cuda::std::is_same_v<T, cuda::std::complex<__nv_bfloat16>>
|
||||
|| cuda::std::is_same_v<T, cuda::std::complex<__half>>)
|
||||
{
|
||||
constexpr auto rel_err = cuda::std::is_same_v<T, cuda::std::complex<__half>> ? 0.08f : 0.2f;
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
auto expected_real = static_cast<float>(expected_data[i].real());
|
||||
auto test_real = test_results[i].real();
|
||||
auto expected_imag = static_cast<float>(expected_data[i].imag());
|
||||
auto test_imag = test_results[i].imag();
|
||||
REQUIRE_THAT(expected_real, Catch::Matchers::WithinRel(test_real, rel_err));
|
||||
REQUIRE_THAT(expected_imag, Catch::Matchers::WithinRel(test_imag, rel_err));
|
||||
}
|
||||
}
|
||||
else if constexpr (cuda::std::__is_cuda_std_complex_v<T>)
|
||||
{
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
auto expected_real = expected_data[i].real();
|
||||
auto test_real = test_results[i].real();
|
||||
auto expected_imag = expected_data[i].imag();
|
||||
auto test_imag = test_results[i].imag();
|
||||
REQUIRE_THAT(expected_real, Catch::Matchers::WithinRel(test_real));
|
||||
REQUIRE_THAT(expected_imag, Catch::Matchers::WithinRel(test_imag));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE(expected_data == test_results);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void verify_results(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)
|
||||
{
|
||||
c2h::host_vector<T> test_results_host = test_results;
|
||||
verify_results(expected_data, test_results_host);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// Min/Max comparison requires bitwise identical results (excluding NaN). Vector Types require only the first element to
|
||||
// match due to how it defined the operator<
|
||||
|
||||
template <typename T>
|
||||
void verify_results_exact(const c2h::host_vector<T>& expected_data, const c2h::host_vector<T>& test_results)
|
||||
{
|
||||
using namespace cub::detail;
|
||||
int device_id = 0;
|
||||
int compute_capability_major = 0;
|
||||
int compute_capability_minor = 0;
|
||||
CubDebugExit(cudaGetDevice(&device_id));
|
||||
CubDebugExit(cudaDeviceGetAttribute(&compute_capability_major, cudaDevAttrComputeCapabilityMajor, device_id));
|
||||
CubDebugExit(cudaDeviceGetAttribute(&compute_capability_minor, cudaDevAttrComputeCapabilityMinor, device_id));
|
||||
int compute_capability = 10 * compute_capability_major + compute_capability_minor;
|
||||
if (compute_capability < 80 && is_any_bfloat16_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (compute_capability < 53 && is_any_half_v<T>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if constexpr (is_vector2_fp_type_v<T>)
|
||||
{
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
auto expected = static_cast<float>(expected_data[i].x);
|
||||
auto test_result = static_cast<float>(test_results[i].x);
|
||||
REQUIRE(expected == test_result);
|
||||
}
|
||||
}
|
||||
if constexpr (is_vector2_type_v<T>)
|
||||
{
|
||||
for (size_t i = 0; i < test_results.size(); ++i)
|
||||
{
|
||||
REQUIRE(expected_data[i].x == test_results[i].x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE_BITWISE_EQ(expected_data, test_results);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void verify_results_exact(const c2h::host_vector<T>& expected_data, const c2h::device_vector<T>& test_results)
|
||||
{
|
||||
c2h::host_vector<T> test_results_host = test_results;
|
||||
if constexpr (is_vector2_type_v<T> || cuda::is_floating_point_v<T>)
|
||||
{
|
||||
verify_results_exact(expected_data, test_results_host);
|
||||
}
|
||||
else
|
||||
{
|
||||
verify_results(expected_data, test_results_host);
|
||||
}
|
||||
}
|
||||
205
cccl_upstream/c2h/include/c2h/checked_allocator.cuh
Normal file
205
cccl_upstream/c2h/include/c2h/checked_allocator.cuh
Normal file
@@ -0,0 +1,205 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/device_allocator.h>
|
||||
#include <thrust/mr/new.h>
|
||||
#include <thrust/system/cuda/memory.h>
|
||||
#include <thrust/system/cuda/memory_resource.h>
|
||||
#include <thrust/system/cuda/pointer.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <cuda_runtime_api.h>
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline std::optional<std::string> get_env(const char* name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char* buf = nullptr;
|
||||
std::size_t len = 0;
|
||||
if (_dupenv_s(&buf, &len, name) || !buf)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
std::string val(buf);
|
||||
free(buf);
|
||||
return val;
|
||||
#else
|
||||
if (const char* v = std::getenv(name))
|
||||
{
|
||||
return std::string(v);
|
||||
}
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct memory_info
|
||||
{
|
||||
std::size_t free{};
|
||||
std::size_t total{};
|
||||
bool override{false};
|
||||
};
|
||||
|
||||
// If the environment variable C2H_DEVICE_MEMORY_LIMIT is set, the total device memory
|
||||
// will be limited to this number of bytes.
|
||||
inline std::size_t get_device_memory_limit()
|
||||
{
|
||||
static std::optional<std::string> override_str = get_env("C2H_DEVICE_MEMORY_LIMIT");
|
||||
static std::size_t result = override_str ? static_cast<std::size_t>(std::atoll(override_str->c_str())) : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool get_debug_checked_allocs()
|
||||
{
|
||||
static std::optional<std::string> debug_checked_allocs = get_env("C2H_DEBUG_CHECKED_ALLOC_FAILURES");
|
||||
static bool result = debug_checked_allocs && (std::atoi(debug_checked_allocs->c_str()) != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline cudaError_t get_device_memory(memory_info& info)
|
||||
{
|
||||
static std::size_t device_memory_limit = get_device_memory_limit();
|
||||
|
||||
cudaError_t status = cudaMemGetInfo(&info.free, &info.total);
|
||||
if (status != cudaSuccess)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
if (device_memory_limit > 0)
|
||||
{
|
||||
info.free = (std::max) (std::size_t{0}, static_cast<std::size_t>(info.free - (info.total - device_memory_limit)));
|
||||
info.total = device_memory_limit;
|
||||
info.override = true;
|
||||
}
|
||||
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
inline cudaError_t check_free_device_memory(std::size_t bytes)
|
||||
{
|
||||
memory_info info;
|
||||
cudaError_t status = get_device_memory(info);
|
||||
if (status != cudaSuccess)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
// Avoid allocating all available memory:
|
||||
constexpr std::size_t padding = 16 * 1024 * 1024; // 16 MiB
|
||||
if (info.free < (bytes + padding))
|
||||
{
|
||||
if (get_debug_checked_allocs())
|
||||
{
|
||||
const double total_GiB = static_cast<double>(info.total) / (1024 * 1024 * 1024);
|
||||
const double free_GiB = static_cast<double>(info.free) / (1024 * 1024 * 1024);
|
||||
const double requested_GiB = static_cast<double>(bytes) / (1024 * 1024 * 1024);
|
||||
const double padded_GiB = static_cast<double>(bytes + padding) / (1024 * 1024 * 1024);
|
||||
|
||||
std::cerr << "Device memory allocation failed due to insufficient free device memory.\n";
|
||||
|
||||
if (info.override)
|
||||
{
|
||||
std::cerr
|
||||
<< "Available device memory has been limited (env var C2H_DEVICE_MEMORY_LIMIT=" << get_device_memory_limit()
|
||||
<< ").\n";
|
||||
}
|
||||
|
||||
std::cerr
|
||||
<< "Total device mem: " << total_GiB << " GiB\n" //
|
||||
<< "Free device mem: " << free_GiB << " GiB\n" //
|
||||
<< "Requested device mem: " << requested_GiB << " GiB\n" //
|
||||
<< "Padded device mem: " << padded_GiB << " GiB\n";
|
||||
}
|
||||
|
||||
return cudaErrorMemoryAllocation;
|
||||
}
|
||||
|
||||
return cudaSuccess;
|
||||
}
|
||||
|
||||
// Check available memory prior to calling cudaMalloc.
|
||||
// This avoids hangups and slowdowns from allocating swap / non-device memory
|
||||
// on some platforms, namely tegra.
|
||||
inline cudaError_t checked_cuda_malloc(void** ptr, std::size_t bytes)
|
||||
{
|
||||
auto status = check_free_device_memory(bytes);
|
||||
if (status != cudaSuccess)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
return cudaMalloc(ptr, bytes);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
using checked_cuda_memory_resource = THRUST_NS_QUALIFIER::system::cuda::detail::
|
||||
cuda_memory_resource<detail::checked_cuda_malloc, cudaFree, THRUST_NS_QUALIFIER::cuda::pointer<void>>;
|
||||
|
||||
template <typename T>
|
||||
class checked_cuda_allocator
|
||||
: public THRUST_NS_QUALIFIER::mr::
|
||||
stateless_resource_allocator<T, THRUST_NS_QUALIFIER::device_ptr_memory_resource<checked_cuda_memory_resource>>
|
||||
{
|
||||
using base = THRUST_NS_QUALIFIER::mr::
|
||||
stateless_resource_allocator<T, THRUST_NS_QUALIFIER::device_ptr_memory_resource<checked_cuda_memory_resource>>;
|
||||
|
||||
public:
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = checked_cuda_allocator<U>;
|
||||
};
|
||||
|
||||
checked_cuda_allocator() = default;
|
||||
|
||||
_CCCL_HOST_DEVICE checked_cuda_allocator(const checked_cuda_allocator& other)
|
||||
: base(other)
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
_CCCL_HOST_DEVICE checked_cuda_allocator(const checked_cuda_allocator<U>& other)
|
||||
: base(other)
|
||||
{}
|
||||
|
||||
checked_cuda_allocator& operator=(const checked_cuda_allocator&) = default;
|
||||
|
||||
~checked_cuda_allocator() = default;
|
||||
};
|
||||
|
||||
struct checked_host_memory_resource final : public THRUST_NS_QUALIFIER::mr::new_delete_resource_base
|
||||
{
|
||||
void* do_allocate(std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT) final
|
||||
{
|
||||
// Some systems with integrated host/device memory have issues with allocating more memory
|
||||
// than is available. Check the amount of free memory before attempting to allocate on
|
||||
// integrated systems.
|
||||
int device = 0;
|
||||
CubDebugExit(cudaGetDevice(&device));
|
||||
cudaDeviceProp prop;
|
||||
CubDebugExit(cudaGetDeviceProperties(&prop, device));
|
||||
if (prop.integrated)
|
||||
{
|
||||
auto status = detail::check_free_device_memory(bytes + alignment + sizeof(std::size_t));
|
||||
if (status != cudaSuccess)
|
||||
{
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
|
||||
return this->new_delete_resource_base::do_allocate(bytes, alignment);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using checked_host_allocator = THRUST_NS_QUALIFIER::mr::stateless_resource_allocator<T, checked_host_memory_resource>;
|
||||
} // namespace c2h
|
||||
83
cccl_upstream/c2h/include/c2h/cpu_timer.h
Normal file
83
cccl_upstream/c2h/include/c2h/cpu_timer.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda/std/tuple>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// #define C2H_DEBUG_TIMING
|
||||
|
||||
#ifdef C2H_DEBUG_TIMING
|
||||
# define C2H_TIME_SECTION_INIT() [[maybe_unused]] c2h::cpu_timer _c2h_timer_
|
||||
# define C2H_TIME_SECTION_RESET() _c2h_timer_.reset()
|
||||
# define C2H_TIME_SECTION(label) _c2h_timer_.print_elapsed_seconds_and_reset(label)
|
||||
# define C2H_TIME_SCOPE(label) [[maybe_unused]] c2h::scoped_cpu_timer _c2h_scoped_cpu_timer_(label)
|
||||
#else
|
||||
# define C2H_TIME_SECTION_INIT() /* no-op */ []() {}()
|
||||
# define C2H_TIME_SECTION_RESET() /* no-op */ []() {}()
|
||||
# define C2H_TIME_SECTION(label) /* no-op */ []() {}()
|
||||
# define C2H_TIME_SCOPE(label) /* no-op */ []() {}()
|
||||
#endif
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
class cpu_timer
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point m_start;
|
||||
|
||||
public:
|
||||
cpu_timer()
|
||||
: m_start(std::chrono::high_resolution_clock::now())
|
||||
{}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
int elapsed_ms() const
|
||||
{
|
||||
auto duration = std::chrono::high_resolution_clock::now() - m_start;
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
|
||||
return static_cast<int>(ms.count());
|
||||
}
|
||||
|
||||
std::uint64_t elapsed_us() const
|
||||
{
|
||||
auto duration = std::chrono::high_resolution_clock::now() - m_start;
|
||||
auto us = std::chrono::duration_cast<std::chrono::microseconds>(duration);
|
||||
return static_cast<std::uint64_t>(us.count());
|
||||
}
|
||||
|
||||
void print_elapsed_seconds(const std::string& label)
|
||||
{
|
||||
printf("%0.6f s: %s\n", static_cast<float>(this->elapsed_us()) / 1000000.f, label.c_str());
|
||||
}
|
||||
|
||||
void print_elapsed_seconds_and_reset(const std::string& label)
|
||||
{
|
||||
this->print_elapsed_seconds(label);
|
||||
this->reset();
|
||||
}
|
||||
};
|
||||
|
||||
class scoped_cpu_timer
|
||||
{
|
||||
cpu_timer m_timer;
|
||||
std::string m_label;
|
||||
|
||||
public:
|
||||
explicit scoped_cpu_timer(std::string label)
|
||||
: m_label(std::move(label))
|
||||
{}
|
||||
|
||||
~scoped_cpu_timer()
|
||||
{
|
||||
m_timer.print_elapsed_seconds(m_label);
|
||||
}
|
||||
};
|
||||
} // namespace c2h
|
||||
191
cccl_upstream/c2h/include/c2h/custom_type.h
Normal file
191
cccl_upstream/c2h/include/c2h/custom_type.h
Normal file
@@ -0,0 +1,191 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda/std/limits>
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
struct custom_type_state_t
|
||||
{
|
||||
std::size_t key{};
|
||||
std::size_t val{};
|
||||
};
|
||||
|
||||
template <template <typename> class... Policies>
|
||||
class custom_type_t
|
||||
: public custom_type_state_t
|
||||
, public Policies<custom_type_t<Policies...>>...
|
||||
{
|
||||
public:
|
||||
friend __host__ std::ostream& operator<<(std::ostream& os, const custom_type_t& self)
|
||||
{
|
||||
return os << "{ " << self.key << ", " << self.val << " }";
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t TotalSize>
|
||||
struct huge_data
|
||||
{
|
||||
template <class CustomType>
|
||||
class type
|
||||
{
|
||||
static constexpr auto extra_member_bytes = (TotalSize - sizeof(custom_type_state_t));
|
||||
std::uint8_t data[extra_member_bytes];
|
||||
};
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class less_comparable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ bool operator<(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return lhs.key < rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class greater_comparable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ bool operator>(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return lhs.key > rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class lexicographical_less_comparable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ bool operator<(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return lhs.key == rhs.key ? lhs.val < rhs.val : lhs.key < rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class lexicographical_greater_comparable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ bool operator>(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return lhs.key == rhs.key ? lhs.val > rhs.val : lhs.key > rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class equal_comparable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ bool operator==(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return lhs.key == rhs.key && lhs.val == rhs.val;
|
||||
}
|
||||
|
||||
friend __host__ __device__ bool operator!=(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class subtractable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ CustomType operator-(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
CustomType result{};
|
||||
|
||||
result.key = lhs.key - rhs.key;
|
||||
result.val = lhs.val - rhs.val;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CustomType>
|
||||
class accumulateable_t
|
||||
{
|
||||
// The CUDA compiler follows the IA64 ABI for class layout, while the
|
||||
// Microsoft host compiler does not.
|
||||
char workaround_msvc{};
|
||||
|
||||
public:
|
||||
friend __host__ __device__ CustomType operator+(const CustomType& lhs, const CustomType& rhs)
|
||||
{
|
||||
CustomType result{};
|
||||
|
||||
result.key = lhs.key + rhs.key;
|
||||
result.val = lhs.val + rhs.val;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace c2h
|
||||
|
||||
template <template <typename> class... Policies>
|
||||
class cuda::std::numeric_limits<c2h::custom_type_t<Policies...>>
|
||||
{
|
||||
public:
|
||||
static constexpr bool is_specialized = true;
|
||||
|
||||
// template <class SizeT = size_t> is a workaround for cudafe++ < 13.1 + gcc < 13 replacing `numeric_limits<size_t>`
|
||||
// with `numeric_limits<conditional<is_void_v<void>, __common_type2_imp<uint64_t, uint64_t>::type, void>::type>`
|
||||
|
||||
template <class SizeT = std::size_t>
|
||||
static __host__ __device__ c2h::custom_type_t<Policies...> max()
|
||||
{
|
||||
c2h::custom_type_t<Policies...> val;
|
||||
val.key = numeric_limits<SizeT>::max();
|
||||
val.val = numeric_limits<SizeT>::max();
|
||||
return val;
|
||||
}
|
||||
|
||||
template <class SizeT = std::size_t>
|
||||
static __host__ __device__ c2h::custom_type_t<Policies...> min()
|
||||
{
|
||||
c2h::custom_type_t<Policies...> val;
|
||||
val.key = numeric_limits<SizeT>::min();
|
||||
val.val = numeric_limits<SizeT>::min();
|
||||
return val;
|
||||
}
|
||||
|
||||
template <class SizeT = std::size_t>
|
||||
static __host__ __device__ c2h::custom_type_t<Policies...> lowest()
|
||||
{
|
||||
c2h::custom_type_t<Policies...> val;
|
||||
val.key = numeric_limits<SizeT>::lowest();
|
||||
val.val = numeric_limits<SizeT>::lowest();
|
||||
return val;
|
||||
}
|
||||
};
|
||||
70
cccl_upstream/c2h/include/c2h/detail/generators.cuh
Normal file
70
cccl_upstream/c2h/include/c2h/detail/generators.cuh
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <cuda/std/complex>
|
||||
|
||||
#include <c2h/generators.h>
|
||||
|
||||
namespace c2h::detail
|
||||
{
|
||||
// called once from main to set up the generator state
|
||||
void init_generator();
|
||||
|
||||
// sets the seed and resizes the distribution vector, fills it, and returns a pointer the start of the data
|
||||
float* prepare_random_data(seed_t seed, std::size_t num_items);
|
||||
|
||||
// called once before main returns to clean up the generator state
|
||||
void cleanup_generator();
|
||||
|
||||
template <typename T, bool = ::cuda::is_floating_point_v<T>>
|
||||
struct random_to_item_t
|
||||
{
|
||||
float m_min;
|
||||
float m_max;
|
||||
|
||||
__host__ __device__ random_to_item_t(T min, T max)
|
||||
: m_min(static_cast<float>(min))
|
||||
, m_max(static_cast<float>(max))
|
||||
{}
|
||||
|
||||
__device__ T operator()(float random_value)
|
||||
{
|
||||
return static_cast<T>((m_max - m_min) * random_value + m_min);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct random_to_item_t<T, true>
|
||||
{
|
||||
using storage_t = ::cuda::std::_If<(sizeof(T) > 4), double, float>;
|
||||
storage_t m_min;
|
||||
storage_t m_max;
|
||||
|
||||
__host__ __device__ random_to_item_t(T min, T max)
|
||||
: m_min(static_cast<storage_t>(min))
|
||||
, m_max(static_cast<storage_t>(max))
|
||||
{}
|
||||
|
||||
__device__ T operator()(float random_value)
|
||||
{
|
||||
return static_cast<T>(m_max * random_value + m_min * (1.0f - random_value));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct random_to_item_t<cuda::std::complex<T>, false>
|
||||
{
|
||||
cuda::std::complex<T> m_min;
|
||||
cuda::std::complex<T> m_max;
|
||||
|
||||
__host__ __device__ random_to_item_t(cuda::std::complex<T> min, cuda::std::complex<T> max)
|
||||
: m_min(min)
|
||||
, m_max(max)
|
||||
{}
|
||||
|
||||
__device__ cuda::std::complex<T> operator()(float random_value) const
|
||||
{
|
||||
return (m_max - m_min) * cuda::std::complex<T>(random_value) + m_min;
|
||||
}
|
||||
};
|
||||
} // namespace c2h::detail
|
||||
19
cccl_upstream/c2h/include/c2h/device_policy.h
Normal file
19
cccl_upstream/c2h/include/c2h/device_policy.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/execution_policy.h>
|
||||
|
||||
#include <c2h/checked_allocator.cuh>
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
static const auto device_policy = THRUST_NS_QUALIFIER::cuda::par(checked_cuda_allocator<char>{});
|
||||
static const auto nosync_device_policy = THRUST_NS_QUALIFIER::cuda::par_nosync(checked_cuda_allocator<char>{});
|
||||
#else // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
static const auto device_policy = THRUST_NS_QUALIFIER::device;
|
||||
static const auto nosync_device_policy = THRUST_NS_QUALIFIER::device;
|
||||
#endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
} // namespace c2h
|
||||
41
cccl_upstream/c2h/include/c2h/extended_types.h
Normal file
41
cccl_upstream/c2h/include/c2h/extended_types.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
#pragma once
|
||||
|
||||
#include <cuda/__cccl_config>
|
||||
|
||||
#ifndef TEST_HALF_T
|
||||
# if _CCCL_HAS_NVFP16()
|
||||
# define TEST_HALF_T() 1
|
||||
# else
|
||||
# define TEST_HALF_T() 0
|
||||
# endif
|
||||
#endif // TEST_HALF_T
|
||||
|
||||
#ifndef TEST_BF_T
|
||||
# if _CCCL_HAS_NVBF16()
|
||||
# define TEST_BF_T() 1
|
||||
# else
|
||||
# define TEST_BF_T() 0
|
||||
# endif
|
||||
#endif // TEST_BF_T
|
||||
|
||||
#ifndef TEST_INT128
|
||||
# if _CCCL_HAS_INT128() && !_CCCL_CUDA_COMPILER(CLANG) // clang-cuda crashes with int128 in generator.cu
|
||||
# define TEST_INT128() 1
|
||||
# else
|
||||
# define TEST_INT128() 0
|
||||
# endif
|
||||
#endif // TEST_INT128
|
||||
|
||||
#if TEST_HALF_T()
|
||||
# include <cuda_fp16.h>
|
||||
|
||||
# include <c2h/half.cuh>
|
||||
#endif // TEST_HALF_T()
|
||||
|
||||
#if TEST_BF_T()
|
||||
# include <cuda_bf16.h>
|
||||
|
||||
# include <c2h/bfloat16.cuh>
|
||||
#endif // TEST_BF_T()
|
||||
70
cccl_upstream/c2h/include/c2h/fill_striped.h
Normal file
70
cccl_upstream/c2h/include/c2h/fill_striped.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
template <typename VectorT, typename = void>
|
||||
struct scalar_to_vec_t
|
||||
{
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ auto operator()(T scalar) const -> VectorT
|
||||
{
|
||||
return static_cast<VectorT>(scalar);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename VectorT>
|
||||
struct scalar_to_vec_t<VectorT, ::cuda::std::void_t<decltype(VectorT::x)>>
|
||||
{
|
||||
template <typename T>
|
||||
__host__ __device__ __forceinline__ auto operator()(T scalar) const -> VectorT
|
||||
{
|
||||
const auto c = static_cast<decltype(VectorT::x)>(scalar);
|
||||
VectorT r;
|
||||
constexpr auto components = ::cuda::std::tuple_size_v<VectorT>;
|
||||
if constexpr (components >= 1)
|
||||
{
|
||||
r.x = c;
|
||||
}
|
||||
if constexpr (components >= 2)
|
||||
{
|
||||
r.y = c;
|
||||
}
|
||||
if constexpr (components >= 3)
|
||||
{
|
||||
r.z = c;
|
||||
}
|
||||
if constexpr (components >= 4)
|
||||
{
|
||||
r.w = c;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
template <int LogicalWarpThreads, int ItemsPerThread, int ThreadsPerBlock, typename IteratorT>
|
||||
void fill_striped(IteratorT it)
|
||||
{
|
||||
using T = cub::detail::it_value_t<IteratorT>;
|
||||
|
||||
constexpr int warps_in_block = ThreadsPerBlock / LogicalWarpThreads;
|
||||
constexpr int items_per_warp = LogicalWarpThreads * ItemsPerThread;
|
||||
scalar_to_vec_t<T> convert;
|
||||
|
||||
for (int warp_id = 0; warp_id < warps_in_block; warp_id++)
|
||||
{
|
||||
const int warp_offset_val = items_per_warp * warp_id;
|
||||
|
||||
for (int lane_id = 0; lane_id < LogicalWarpThreads; lane_id++)
|
||||
{
|
||||
const int lane_offset = warp_offset_val + lane_id;
|
||||
|
||||
for (int item = 0; item < ItemsPerThread; item++)
|
||||
{
|
||||
*(it++) = convert(lane_offset + item * LogicalWarpThreads);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
166
cccl_upstream/c2h/include/c2h/generators.h
Normal file
166
cccl_upstream/c2h/include/c2h/generators.h
Normal file
@@ -0,0 +1,166 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/config/device_system.h>
|
||||
|
||||
#include <cuda/std/limits>
|
||||
|
||||
#include <c2h/custom_type.h>
|
||||
#include <c2h/vector.h>
|
||||
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
# if _CCCL_HAS_NVFP16()
|
||||
# include <cuda_fp16.h>
|
||||
# endif // _CCCL_HAS_NVFP16()
|
||||
|
||||
# if _CCCL_HAS_NVBF16()
|
||||
_CCCL_DIAG_PUSH
|
||||
_CCCL_DIAG_SUPPRESS_CLANG("-Wunused-function")
|
||||
# include <cuda_bf16.h>
|
||||
_CCCL_DIAG_POP
|
||||
# endif // _CCCL_HAS_NVBF16
|
||||
|
||||
# if _CCCL_HAS_NVFP8()
|
||||
// cuda_fp8.h resets default for C4127, so we have to guard the inclusion
|
||||
_CCCL_DIAG_PUSH
|
||||
# include <cuda_fp8.h>
|
||||
_CCCL_DIAG_POP
|
||||
# endif // _CCCL_HAS_NVFP8()
|
||||
#endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <class T>
|
||||
class value_wrapper_t
|
||||
{
|
||||
T m_val{};
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
explicit value_wrapper_t(T val)
|
||||
: m_val(val)
|
||||
{}
|
||||
explicit value_wrapper_t(int val)
|
||||
: m_val(static_cast<T>(val))
|
||||
{}
|
||||
T get() const
|
||||
{
|
||||
return m_val;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
struct seed_t : detail::value_wrapper_t<unsigned long long int>
|
||||
{
|
||||
using value_wrapper_t::value_wrapper_t;
|
||||
};
|
||||
|
||||
struct modulo_t : detail::value_wrapper_t<std::size_t>
|
||||
{
|
||||
using value_wrapper_t::value_wrapper_t;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
void gen_custom_type_state(
|
||||
seed_t seed,
|
||||
char* data,
|
||||
custom_type_state_t min,
|
||||
custom_type_state_t max,
|
||||
std::size_t elements,
|
||||
std::size_t element_size);
|
||||
|
||||
template <typename OffsetT, typename KeyT>
|
||||
void init_key_segments(::cuda::std::span<const OffsetT> segment_offsets, KeyT* d_out, std::size_t element_size);
|
||||
|
||||
template <typename T>
|
||||
void gen_values_between(seed_t seed, ::cuda::std::span<T> data, T min, T max);
|
||||
|
||||
template <typename T>
|
||||
void gen_values_cyclic(modulo_t mod, ::cuda::std::span<T> data);
|
||||
|
||||
template <typename T>
|
||||
std::size_t gen_uniform_offsets(
|
||||
seed_t seed, cuda::std::span<T> segment_offsets, T total_elements, T min_segment_size, T max_segment_size);
|
||||
} // namespace detail
|
||||
|
||||
template <template <typename> class... Ps>
|
||||
void gen(seed_t seed,
|
||||
device_vector<custom_type_t<Ps...>>& data,
|
||||
custom_type_t<Ps...> min = ::cuda::std::numeric_limits<custom_type_t<Ps...>>::lowest(),
|
||||
custom_type_t<Ps...> max = ::cuda::std::numeric_limits<custom_type_t<Ps...>>::max())
|
||||
{
|
||||
detail::gen_custom_type_state(
|
||||
seed,
|
||||
reinterpret_cast<char*>(THRUST_NS_QUALIFIER::raw_pointer_cast(data.data())),
|
||||
min,
|
||||
max,
|
||||
data.size(),
|
||||
sizeof(custom_type_t<Ps...>));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void gen(seed_t seed,
|
||||
device_vector<T>& data,
|
||||
T min = ::cuda::std::numeric_limits<T>::lowest(),
|
||||
T max = ::cuda::std::numeric_limits<T>::max())
|
||||
{
|
||||
detail::gen_values_between(seed, {THRUST_NS_QUALIFIER::raw_pointer_cast(data.data()), data.size()}, min, max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void gen(modulo_t mod, device_vector<T>& data)
|
||||
{
|
||||
detail::gen_values_cyclic(mod, ::cuda::std::span<T>{THRUST_NS_QUALIFIER::raw_pointer_cast(data.data()), data.size()});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates an array of offsets with uniformly distributed segment sizes in the range
|
||||
* between [min_segment_size, max_segment_size]. The last offset in the array corresponds to
|
||||
* `total_element`. At most `total_element+2` offsets (or `total_elements+1` segments) and, because
|
||||
* the very last offset must corresponds to `total_element`, the last segment may comprise more than
|
||||
* `max_segment_size` items.
|
||||
*/
|
||||
template <typename T>
|
||||
device_vector<T> gen_uniform_offsets(seed_t seed, T total_elements, T min_segment_size, T max_segment_size)
|
||||
{
|
||||
device_vector<T> segment_offsets(total_elements + 2);
|
||||
const auto new_size = detail::gen_uniform_offsets(
|
||||
seed,
|
||||
{THRUST_NS_QUALIFIER::raw_pointer_cast(segment_offsets.data()), segment_offsets.size()},
|
||||
total_elements,
|
||||
min_segment_size,
|
||||
max_segment_size);
|
||||
segment_offsets.resize(new_size);
|
||||
return segment_offsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates key-segment ranges from an offsets-array like the one given by
|
||||
* `gen_uniform_offset`.
|
||||
*/
|
||||
template <typename OffsetT, typename KeyT>
|
||||
void init_key_segments(const device_vector<OffsetT>& segment_offsets, device_vector<KeyT>& keys_out)
|
||||
{
|
||||
detail::init_key_segments(
|
||||
::cuda::std::span<const OffsetT>{
|
||||
THRUST_NS_QUALIFIER::raw_pointer_cast(segment_offsets.data()), segment_offsets.size()},
|
||||
THRUST_NS_QUALIFIER::raw_pointer_cast(keys_out.data()),
|
||||
sizeof(KeyT));
|
||||
}
|
||||
|
||||
template <typename OffsetT, template <typename> class... Ps>
|
||||
void init_key_segments(const device_vector<OffsetT>& segment_offsets, device_vector<custom_type_t<Ps...>>& keys_out)
|
||||
{
|
||||
detail::init_key_segments(
|
||||
::cuda::std::span<const OffsetT>{
|
||||
THRUST_NS_QUALIFIER::raw_pointer_cast(segment_offsets.data()), segment_offsets.size()},
|
||||
static_cast<custom_type_state_t*>(THRUST_NS_QUALIFIER::raw_pointer_cast(keys_out.data())),
|
||||
sizeof(custom_type_t<Ps...>));
|
||||
}
|
||||
} // namespace c2h
|
||||
345
cccl_upstream/c2h/include/c2h/half.cuh
Normal file
345
cccl_upstream/c2h/include/c2h/half.cuh
Normal file
@@ -0,0 +1,345 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Utilities for interacting with the opaque CUDA __half type
|
||||
*/
|
||||
|
||||
#include <cuda_fp16.h>
|
||||
|
||||
#include <cub/util_type.cuh>
|
||||
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iosfwd>
|
||||
|
||||
#ifdef __GNUC__
|
||||
// There's a ton of type-punning going on in this file.
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* half_t
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Host-based fp16 data type compatible and convertible with __half
|
||||
*/
|
||||
// TODO(bgruber): drop this when CTK 12.2 is the minimum, since it provides __host__ __device__ operators of __half
|
||||
struct half_t
|
||||
{
|
||||
uint16_t __x;
|
||||
|
||||
/// Constructor from __half
|
||||
__host__ __device__ __forceinline__ explicit half_t(const __half& other)
|
||||
{
|
||||
__x = reinterpret_cast<const uint16_t&>(other);
|
||||
}
|
||||
|
||||
/// Constructor from integer
|
||||
__host__ __device__ __forceinline__ explicit half_t(int a)
|
||||
{
|
||||
*this = half_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from std::size_t
|
||||
__host__ __device__ __forceinline__ explicit half_t(std::size_t a)
|
||||
{
|
||||
*this = half_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from double
|
||||
__host__ __device__ __forceinline__ explicit half_t(double a)
|
||||
{
|
||||
*this = half_t(float(a));
|
||||
}
|
||||
|
||||
/// Constructor from unsigned long long int
|
||||
template <typename T,
|
||||
typename = typename ::cuda::std::enable_if<
|
||||
::cuda::std::is_same<T, unsigned long long int>::value
|
||||
&& (!::cuda::std::is_same<std::size_t, unsigned long long int>::value)>::type>
|
||||
__host__ __device__ __forceinline__ explicit half_t(T a)
|
||||
{
|
||||
*this = half_t(float(a));
|
||||
}
|
||||
|
||||
/// Default constructor
|
||||
half_t() = default;
|
||||
|
||||
/// Constructor from float
|
||||
__host__ __device__ __forceinline__ explicit half_t(float a)
|
||||
{
|
||||
// Stolen from Norbert Juffa
|
||||
uint32_t ia = *reinterpret_cast<uint32_t*>(&a);
|
||||
uint16_t ir;
|
||||
|
||||
ir = (ia >> 16) & 0x8000;
|
||||
|
||||
if ((ia & 0x7f800000) == 0x7f800000)
|
||||
{
|
||||
if ((ia & 0x7fffffff) == 0x7f800000)
|
||||
{
|
||||
ir |= 0x7c00; /* infinity */
|
||||
}
|
||||
else
|
||||
{
|
||||
ir = 0x7fff; /* canonical NaN */
|
||||
}
|
||||
}
|
||||
else if ((ia & 0x7f800000) >= 0x33000000)
|
||||
{
|
||||
int32_t shift = (int32_t) ((ia >> 23) & 0xff) - 127;
|
||||
if (shift > 15)
|
||||
{
|
||||
ir |= 0x7c00; /* infinity */
|
||||
}
|
||||
else
|
||||
{
|
||||
ia = (ia & 0x007fffff) | 0x00800000; /* extract mantissa */
|
||||
if (shift < -14)
|
||||
{ /* denormal */
|
||||
ir |= ia >> (-1 - shift);
|
||||
ia = ia << (32 - (-1 - shift));
|
||||
}
|
||||
else
|
||||
{ /* normal */
|
||||
ir |= ia >> (24 - 11);
|
||||
ia = ia << (32 - (24 - 11));
|
||||
ir = static_cast<uint16_t>(ir + ((14 + shift) << 10));
|
||||
}
|
||||
/* IEEE-754 round to nearest of even */
|
||||
if ((ia > 0x80000000) || ((ia == 0x80000000) && (ir & 1)))
|
||||
{
|
||||
ir++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->__x = ir;
|
||||
}
|
||||
|
||||
/// Cast to __half
|
||||
__host__ __device__ __forceinline__ operator __half() const
|
||||
{
|
||||
return reinterpret_cast<const __half&>(__x);
|
||||
}
|
||||
|
||||
/// Cast to float
|
||||
__host__ __device__ __forceinline__ operator float() const
|
||||
{
|
||||
// Stolen from Andrew Kerr
|
||||
|
||||
int sign = ((this->__x >> 15) & 1);
|
||||
int exp = ((this->__x >> 10) & 0x1f);
|
||||
int mantissa = (this->__x & 0x3ff);
|
||||
std::uint32_t f = 0;
|
||||
|
||||
if (exp > 0 && exp < 31)
|
||||
{
|
||||
// normal
|
||||
exp += 112;
|
||||
f = (sign << 31) | (exp << 23) | (mantissa << 13);
|
||||
}
|
||||
else if (exp == 0)
|
||||
{
|
||||
if (mantissa)
|
||||
{
|
||||
// subnormal
|
||||
exp += 113;
|
||||
while ((mantissa & (1 << 10)) == 0)
|
||||
{
|
||||
mantissa <<= 1;
|
||||
exp--;
|
||||
}
|
||||
mantissa &= 0x3ff;
|
||||
f = (sign << 31) | (exp << 23) | (mantissa << 13);
|
||||
}
|
||||
else if (sign)
|
||||
{
|
||||
f = 0x80000000; // negative zero
|
||||
}
|
||||
else
|
||||
{
|
||||
f = 0x0; // zero
|
||||
}
|
||||
}
|
||||
else if (exp == 31)
|
||||
{
|
||||
if (mantissa)
|
||||
{
|
||||
f = 0x7fffffff; // not a number
|
||||
}
|
||||
else
|
||||
{
|
||||
f = (0xff << 23) | (sign << 31); // inf
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(sizeof(float) == sizeof(std::uint32_t), "4-byte size check");
|
||||
float ret{};
|
||||
std::memcpy(&ret, &f, sizeof(float));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Get raw storage
|
||||
__host__ __device__ __forceinline__ uint16_t raw() const
|
||||
{
|
||||
return this->__x;
|
||||
}
|
||||
|
||||
/// Equality
|
||||
__host__ __device__ __forceinline__ friend bool operator==(const half_t& a, const half_t& b)
|
||||
{
|
||||
return (a.__x == b.__x);
|
||||
}
|
||||
|
||||
/// Inequality
|
||||
__host__ __device__ __forceinline__ friend bool operator!=(const half_t& a, const half_t& b)
|
||||
{
|
||||
return (a.__x != b.__x);
|
||||
}
|
||||
|
||||
/// Assignment by sum
|
||||
__host__ __device__ __forceinline__ half_t& operator+=(const half_t& rhs)
|
||||
{
|
||||
*this = half_t(float(*this) + float(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Multiply
|
||||
__host__ __device__ __forceinline__ half_t operator*(const half_t& other) const
|
||||
{
|
||||
return half_t(float(*this) * float(other));
|
||||
}
|
||||
|
||||
/// Divide
|
||||
__host__ __device__ __forceinline__ half_t& operator/=(const half_t& other)
|
||||
{
|
||||
return *this = half_t(float(*this) / float(other));
|
||||
}
|
||||
|
||||
friend __host__ __device__ __forceinline__ half_t operator/(half_t self, const half_t& other)
|
||||
{
|
||||
return self /= other;
|
||||
}
|
||||
|
||||
/// Add
|
||||
__host__ __device__ __forceinline__ half_t operator+(const half_t& other) const
|
||||
{
|
||||
return half_t(float(*this) + float(other));
|
||||
}
|
||||
|
||||
/// Sub
|
||||
__host__ __device__ __forceinline__ half_t operator-(const half_t& other) const
|
||||
{
|
||||
return half_t(float(*this) - float(other));
|
||||
}
|
||||
|
||||
/// Less-than
|
||||
__host__ __device__ __forceinline__ bool operator<(const half_t& other) const
|
||||
{
|
||||
return float(*this) < float(other);
|
||||
}
|
||||
|
||||
/// Less-than-equal
|
||||
__host__ __device__ __forceinline__ bool operator<=(const half_t& other) const
|
||||
{
|
||||
return float(*this) <= float(other);
|
||||
}
|
||||
|
||||
/// Greater-than
|
||||
__host__ __device__ __forceinline__ bool operator>(const half_t& other) const
|
||||
{
|
||||
return float(*this) > float(other);
|
||||
}
|
||||
|
||||
/// Greater-than-equal
|
||||
__host__ __device__ __forceinline__ bool operator>=(const half_t& other) const
|
||||
{
|
||||
return float(*this) >= float(other);
|
||||
}
|
||||
|
||||
/// numeric_traits<half_t>::max
|
||||
__host__ __device__ __forceinline__ static half_t(max)()
|
||||
{
|
||||
uint16_t max_word = 0x7BFF;
|
||||
return reinterpret_cast<half_t&>(max_word);
|
||||
}
|
||||
|
||||
/// numeric_traits<half_t>::lowest
|
||||
__host__ __device__ __forceinline__ static half_t lowest()
|
||||
{
|
||||
uint16_t lowest_word = 0xFBFF;
|
||||
return reinterpret_cast<half_t&>(lowest_word);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* I/O stream overloads
|
||||
******************************************************************************/
|
||||
|
||||
/// Insert formatted \p half_t into the output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, const half_t& x)
|
||||
{
|
||||
out << (float) x;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Insert formatted \p __half into the output stream
|
||||
inline std::ostream& operator<<(std::ostream& out, const __half& x)
|
||||
{
|
||||
return out << half_t(x);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Traits overloads
|
||||
******************************************************************************/
|
||||
|
||||
namespace cuda
|
||||
{
|
||||
template <>
|
||||
inline constexpr bool is_floating_point_v<half_t> = true;
|
||||
}
|
||||
|
||||
template <>
|
||||
class cuda::std::numeric_limits<half_t>
|
||||
{
|
||||
public:
|
||||
static constexpr bool is_specialized = true;
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE half_t max()
|
||||
{
|
||||
return half_t(numeric_limits<__half>::max());
|
||||
}
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE half_t min()
|
||||
{
|
||||
return half_t(numeric_limits<__half>::min());
|
||||
}
|
||||
|
||||
static _CCCL_HOST_DEVICE _CCCL_FORCEINLINE half_t lowest()
|
||||
{
|
||||
return half_t(numeric_limits<__half>::lowest());
|
||||
}
|
||||
};
|
||||
|
||||
CUB_NAMESPACE_BEGIN
|
||||
|
||||
template <>
|
||||
struct NumericTraits<half_t> : BaseTraits<FLOATING_POINT, true, uint16_t, half_t>
|
||||
{};
|
||||
|
||||
CUB_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
104
cccl_upstream/c2h/include/c2h/operator.cuh
Normal file
104
cccl_upstream/c2h/include/c2h/operator.cuh
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
#pragma once
|
||||
|
||||
#include <cuda/functional>
|
||||
#include <cuda/std/functional>
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/type_traits>
|
||||
|
||||
#include <c2h/custom_type.h>
|
||||
#include <c2h/extended_types.h>
|
||||
#include <c2h/test_util_vec.h>
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* CUB operator to identity
|
||||
**********************************************************************************************************************/
|
||||
|
||||
template <typename Operator, typename T, typename = void>
|
||||
inline constexpr T identity_v = cuda::identity_element<Operator, T>();
|
||||
|
||||
template <typename T>
|
||||
inline const T identity_v<cuda::std::plus<>, T> = T{}; // e.g. short2, float2, complex<__half> etc.
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* half_t specializations
|
||||
**********************************************************************************************************************/
|
||||
|
||||
template <>
|
||||
inline const half_t identity_v<cuda::std::plus<>, half_t> = half_t{0.0f};
|
||||
|
||||
template <>
|
||||
inline const half_t identity_v<cuda::std::multiplies<>, half_t> = half_t{1.0f};
|
||||
|
||||
template <>
|
||||
inline const half_t identity_v<cuda::minimum<>, half_t> = cuda::std::numeric_limits<half_t>::max();
|
||||
|
||||
template <>
|
||||
inline const half_t identity_v<cuda::maximum<>, half_t> = cuda::std::numeric_limits<half_t>::lowest();
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* bfloat16_t specializations
|
||||
**********************************************************************************************************************/
|
||||
|
||||
template <>
|
||||
inline const bfloat16_t identity_v<cuda::std::plus<>, bfloat16_t> = bfloat16_t{0.0f};
|
||||
|
||||
template <>
|
||||
inline const bfloat16_t identity_v<cuda::std::multiplies<>, bfloat16_t> = bfloat16_t{1.0f};
|
||||
|
||||
template <>
|
||||
inline const bfloat16_t identity_v<cuda::minimum<>, bfloat16_t> = cuda::std::numeric_limits<bfloat16_t>::max();
|
||||
|
||||
template <>
|
||||
inline const bfloat16_t identity_v<cuda::maximum<>, bfloat16_t> = cuda::std::numeric_limits<bfloat16_t>::lowest();
|
||||
|
||||
/***********************************************************************************************************************
|
||||
* short2, ushort2, float2 specializations
|
||||
**********************************************************************************************************************/
|
||||
|
||||
template <>
|
||||
inline constexpr short2 identity_v<cuda::maximum<>, short2> =
|
||||
short2{cuda::std::numeric_limits<int16_t>::lowest(), cuda::std::numeric_limits<int16_t>::lowest()};
|
||||
|
||||
template <>
|
||||
inline constexpr ushort2 identity_v<cuda::maximum<>, ushort2> = ushort2{0, 0};
|
||||
|
||||
template <>
|
||||
inline constexpr float2 identity_v<cuda::maximum<>, float2> =
|
||||
float2{cuda::std::numeric_limits<float>::lowest(), cuda::std::numeric_limits<float>::lowest()};
|
||||
|
||||
template <>
|
||||
inline const __half2 identity_v<cuda::maximum<>, __half2> =
|
||||
__half2{cuda::std::numeric_limits<__half>::lowest(), cuda::std::numeric_limits<__half>::lowest()};
|
||||
|
||||
template <>
|
||||
inline const __nv_bfloat162 identity_v<cuda::maximum<>, __nv_bfloat162> = __nv_bfloat162{
|
||||
cuda::std::numeric_limits<__nv_bfloat16>::lowest(), cuda::std::numeric_limits<__nv_bfloat16>::lowest()};
|
||||
|
||||
template <>
|
||||
inline constexpr short2 identity_v<cuda::minimum<>, short2> =
|
||||
short2{cuda::std::numeric_limits<int16_t>::max(), cuda::std::numeric_limits<int16_t>::max()};
|
||||
|
||||
template <>
|
||||
inline constexpr ushort2 identity_v<cuda::minimum<>, ushort2> =
|
||||
ushort2{cuda::std::numeric_limits<uint16_t>::max(), cuda::std::numeric_limits<uint16_t>::max()};
|
||||
|
||||
template <>
|
||||
inline const __half2 identity_v<cuda::minimum<>, __half2> =
|
||||
__half2{cuda::std::numeric_limits<__half>::max(), cuda::std::numeric_limits<__half>::max()};
|
||||
|
||||
template <>
|
||||
inline const __nv_bfloat162 identity_v<cuda::minimum<>, __nv_bfloat162> =
|
||||
__nv_bfloat162{cuda::std::numeric_limits<__nv_bfloat16>::max(), cuda::std::numeric_limits<__nv_bfloat16>::max()};
|
||||
|
||||
template <template <typename> class... Policies>
|
||||
inline const c2h::custom_type_t<Policies...> identity_v<cuda::maximum<>, c2h::custom_type_t<Policies...>> =
|
||||
cuda::std::numeric_limits<c2h::custom_type_t<Policies...>>::lowest();
|
||||
|
||||
template <template <typename> class... Policies>
|
||||
inline const c2h::custom_type_t<Policies...> identity_v<cuda::minimum<>, c2h::custom_type_t<Policies...>> =
|
||||
cuda::std::numeric_limits<c2h::custom_type_t<Policies...>>::max();
|
||||
|
||||
struct custom_plus : cuda::std::plus<>
|
||||
{};
|
||||
416
cccl_upstream/c2h/include/c2h/test_util_vec.h
Normal file
416
cccl_upstream/c2h/include/c2h/test_util_vec.h
Normal file
@@ -0,0 +1,416 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/config/device_system.h>
|
||||
|
||||
#include <cuda/std/limits>
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <c2h/extended_types.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Console printing utilities
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* Helper for casting character types to integers for cout printing
|
||||
*/
|
||||
template <typename T>
|
||||
T CoutCast(T val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
inline int CoutCast(char val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
inline int CoutCast(unsigned char val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
inline int CoutCast(signed char val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Comparison and ostream operators for CUDA vector types
|
||||
******************************************************************************/
|
||||
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
|
||||
/**
|
||||
* Vector1 overloads
|
||||
*/
|
||||
# define C2H_VEC_OVERLOAD_1(T) \
|
||||
/* Ostream output */ \
|
||||
inline std::ostream& operator<<(std::ostream& os, const T& val) \
|
||||
{ \
|
||||
os << '(' << CoutCast(val.x) << ')'; \
|
||||
return os; \
|
||||
} \
|
||||
/* Inequality */ \
|
||||
inline __host__ __device__ constexpr bool operator!=(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x != b.x); \
|
||||
} \
|
||||
/* Equality */ \
|
||||
inline __host__ __device__ constexpr bool operator==(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x == b.x); \
|
||||
} \
|
||||
/* Max */ \
|
||||
inline __host__ __device__ constexpr bool operator>(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x > b.x); \
|
||||
} \
|
||||
/* Min */ \
|
||||
inline __host__ __device__ constexpr bool operator<(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x < b.x); \
|
||||
} \
|
||||
/* Summation (non-reference addends for VS2003 -O3 warpscan workaround */ \
|
||||
inline __host__ __device__ constexpr T operator+(T a, T b) \
|
||||
{ \
|
||||
using V = decltype(T::x); \
|
||||
return T{static_cast<V>(a.x + b.x)}; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector2 overloads
|
||||
*/
|
||||
# define C2H_VEC_OVERLOAD_2(T) \
|
||||
/* Ostream output */ \
|
||||
inline std::ostream& operator<<(std::ostream& os, const T& val) \
|
||||
{ \
|
||||
os << '(' << CoutCast(val.x) << ',' << CoutCast(val.y) << ')'; \
|
||||
return os; \
|
||||
} \
|
||||
/* Inequality */ \
|
||||
inline __host__ __device__ constexpr bool operator!=(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x != b.x) || (a.y != b.y); \
|
||||
} \
|
||||
/* Equality */ \
|
||||
inline __host__ __device__ constexpr bool operator==(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x == b.x) && (a.y == b.y); \
|
||||
} \
|
||||
/* Max */ \
|
||||
inline __host__ __device__ constexpr bool operator>(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x > b.x) \
|
||||
return true; \
|
||||
else if (b.x > a.x) \
|
||||
return false; \
|
||||
return a.y > b.y; \
|
||||
} \
|
||||
/* Min */ \
|
||||
inline __host__ __device__ constexpr bool operator<(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x < b.x) \
|
||||
return true; \
|
||||
else if (b.x < a.x) \
|
||||
return false; \
|
||||
return a.y < b.y; \
|
||||
} \
|
||||
/* Summation (non-reference addends for VS2003 -O3 warpscan workaround */ \
|
||||
inline __host__ __device__ constexpr T operator+(T a, T b) \
|
||||
{ \
|
||||
using V = decltype(T::x); \
|
||||
return T{static_cast<V>(a.x + b.x), static_cast<V>(a.y + b.y)}; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector3 overloads
|
||||
*/
|
||||
# define C2H_VEC_OVERLOAD_3(T) \
|
||||
/* Ostream output */ \
|
||||
inline std::ostream& operator<<(std::ostream& os, const T& val) \
|
||||
{ \
|
||||
os << '(' << CoutCast(val.x) << ',' << CoutCast(val.y) << ',' << CoutCast(val.z) << ')'; \
|
||||
return os; \
|
||||
} \
|
||||
/* Inequality */ \
|
||||
inline __host__ __device__ constexpr bool operator!=(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x != b.x) || (a.y != b.y) || (a.z != b.z); \
|
||||
} \
|
||||
/* Equality */ \
|
||||
inline __host__ __device__ constexpr bool operator==(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); \
|
||||
} \
|
||||
/* Max */ \
|
||||
inline __host__ __device__ constexpr bool operator>(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x > b.x) \
|
||||
return true; \
|
||||
else if (b.x > a.x) \
|
||||
return false; \
|
||||
if (a.y > b.y) \
|
||||
return true; \
|
||||
else if (b.y > a.y) \
|
||||
return false; \
|
||||
return a.z > b.z; \
|
||||
} \
|
||||
/* Min */ \
|
||||
inline __host__ __device__ constexpr bool operator<(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x < b.x) \
|
||||
return true; \
|
||||
else if (b.x < a.x) \
|
||||
return false; \
|
||||
if (a.y < b.y) \
|
||||
return true; \
|
||||
else if (b.y < a.y) \
|
||||
return false; \
|
||||
return a.z < b.z; \
|
||||
} \
|
||||
/* Summation (non-reference addends for VS2003 -O3 warpscan workaround */ \
|
||||
inline __host__ __device__ constexpr T operator+(T a, T b) \
|
||||
{ \
|
||||
using V = decltype(T::x); \
|
||||
return T{static_cast<V>(a.x + b.x), static_cast<V>(a.y + b.y), static_cast<V>(a.z + b.z)}; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Vector4 overloads
|
||||
*/
|
||||
# define C2H_VEC_OVERLOAD_4(T) \
|
||||
/* Ostream output */ \
|
||||
inline std::ostream& operator<<(std::ostream& os, const T& val) \
|
||||
{ \
|
||||
os << '(' << CoutCast(val.x) << ',' << CoutCast(val.y) << ',' << CoutCast(val.z) << ',' << CoutCast(val.w) \
|
||||
<< ')'; \
|
||||
return os; \
|
||||
} \
|
||||
/* Inequality */ \
|
||||
inline __host__ __device__ constexpr bool operator!=(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x != b.x) || (a.y != b.y) || (a.z != b.z) || (a.w != b.w); \
|
||||
} \
|
||||
/* Equality */ \
|
||||
inline __host__ __device__ constexpr bool operator==(const T& a, const T& b) \
|
||||
{ \
|
||||
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); \
|
||||
} \
|
||||
/* Max */ \
|
||||
inline __host__ __device__ constexpr bool operator>(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x > b.x) \
|
||||
return true; \
|
||||
else if (b.x > a.x) \
|
||||
return false; \
|
||||
if (a.y > b.y) \
|
||||
return true; \
|
||||
else if (b.y > a.y) \
|
||||
return false; \
|
||||
if (a.z > b.z) \
|
||||
return true; \
|
||||
else if (b.z > a.z) \
|
||||
return false; \
|
||||
return a.w > b.w; \
|
||||
} \
|
||||
/* Min */ \
|
||||
inline __host__ __device__ constexpr bool operator<(const T& a, const T& b) \
|
||||
{ \
|
||||
if (a.x < b.x) \
|
||||
return true; \
|
||||
else if (b.x < a.x) \
|
||||
return false; \
|
||||
if (a.y < b.y) \
|
||||
return true; \
|
||||
else if (b.y < a.y) \
|
||||
return false; \
|
||||
if (a.z < b.z) \
|
||||
return true; \
|
||||
else if (b.z < a.z) \
|
||||
return false; \
|
||||
return a.w < b.w; \
|
||||
} \
|
||||
/* Summation (non-reference addends for VS2003 -O3 warpscan workaround */ \
|
||||
inline __host__ __device__ constexpr T operator+(T a, T b) \
|
||||
{ \
|
||||
using V = decltype(T::x); \
|
||||
return T{ \
|
||||
static_cast<V>(a.x + b.x), static_cast<V>(a.y + b.y), static_cast<V>(a.z + b.z), static_cast<V>(a.w + b.w)}; \
|
||||
}
|
||||
|
||||
/**
|
||||
* All vector overloads
|
||||
*/
|
||||
# define C2H_VEC_OVERLOAD(VecName) \
|
||||
C2H_VEC_OVERLOAD_1(VecName##1) \
|
||||
C2H_VEC_OVERLOAD_2(VecName##2) \
|
||||
C2H_VEC_OVERLOAD_3(VecName##3) \
|
||||
C2H_VEC_OVERLOAD_4(VecName##4)
|
||||
|
||||
/**
|
||||
* Define for types
|
||||
*/
|
||||
C2H_VEC_OVERLOAD(char)
|
||||
C2H_VEC_OVERLOAD(short)
|
||||
C2H_VEC_OVERLOAD(int)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_OVERLOAD(long)
|
||||
C2H_VEC_OVERLOAD(longlong)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_OVERLOAD_4(long4_16a)
|
||||
C2H_VEC_OVERLOAD_4(long4_32a)
|
||||
C2H_VEC_OVERLOAD_4(longlong4_16a)
|
||||
C2H_VEC_OVERLOAD_4(longlong4_32a)
|
||||
# endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_OVERLOAD(uchar)
|
||||
C2H_VEC_OVERLOAD(ushort)
|
||||
C2H_VEC_OVERLOAD(uint)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_OVERLOAD(ulong)
|
||||
C2H_VEC_OVERLOAD(ulonglong)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_OVERLOAD_4(ulong4_16a)
|
||||
C2H_VEC_OVERLOAD_4(ulong4_32a)
|
||||
C2H_VEC_OVERLOAD_4(ulonglong4_16a)
|
||||
C2H_VEC_OVERLOAD_4(ulonglong4_32a)
|
||||
# endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_OVERLOAD(float)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_OVERLOAD(double)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_OVERLOAD_4(double4_16a)
|
||||
C2H_VEC_OVERLOAD_4(double4_32a)
|
||||
# endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
|
||||
// Specialize cuda::std::numeric_limits for vector types.
|
||||
|
||||
# define REPEAT_TO_LIST_1(a) a
|
||||
# define REPEAT_TO_LIST_2(a) a, a
|
||||
# define REPEAT_TO_LIST_3(a) a, a, a
|
||||
# define REPEAT_TO_LIST_4(a) a, a, a, a
|
||||
# define REPEAT_TO_LIST(N, a) _CCCL_PP_CAT(REPEAT_TO_LIST_, N)(a)
|
||||
|
||||
# define C2H_VEC_TRAITS_OVERLOAD_IMPL(T, BaseT, N) \
|
||||
_CCCL_BEGIN_NAMESPACE_CUDA_STD \
|
||||
template <> \
|
||||
class numeric_limits<T> \
|
||||
{ \
|
||||
public: \
|
||||
static constexpr bool is_specialized = true; \
|
||||
static __host__ __device__ T max() \
|
||||
{ \
|
||||
return {REPEAT_TO_LIST(N, ::cuda::std::numeric_limits<BaseT>::max())}; \
|
||||
} \
|
||||
static __host__ __device__ T min() \
|
||||
{ \
|
||||
return {REPEAT_TO_LIST(N, ::cuda::std::numeric_limits<BaseT>::min())}; \
|
||||
} \
|
||||
static __host__ __device__ T lowest() \
|
||||
{ \
|
||||
return {REPEAT_TO_LIST(N, ::cuda::std::numeric_limits<BaseT>::lowest())}; \
|
||||
} \
|
||||
}; \
|
||||
_CCCL_END_NAMESPACE_CUDA_STD
|
||||
|
||||
# define C2H_VEC_TRAITS_OVERLOAD(COMPONENT_T, BaseT) \
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(COMPONENT_T##1, BaseT, 1) \
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(COMPONENT_T##2, BaseT, 2) \
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(COMPONENT_T##3, BaseT, 3) \
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(COMPONENT_T##4, BaseT, 4)
|
||||
|
||||
C2H_VEC_TRAITS_OVERLOAD(char, signed char)
|
||||
C2H_VEC_TRAITS_OVERLOAD(short, short)
|
||||
C2H_VEC_TRAITS_OVERLOAD(int, int)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_TRAITS_OVERLOAD(long, long)
|
||||
C2H_VEC_TRAITS_OVERLOAD(longlong, long long)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
C2H_VEC_TRAITS_OVERLOAD(uchar, unsigned char)
|
||||
C2H_VEC_TRAITS_OVERLOAD(ushort, unsigned short)
|
||||
C2H_VEC_TRAITS_OVERLOAD(uint, unsigned int)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_TRAITS_OVERLOAD(ulong, unsigned long)
|
||||
C2H_VEC_TRAITS_OVERLOAD(ulonglong, unsigned long long)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
C2H_VEC_TRAITS_OVERLOAD(float, float)
|
||||
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
||||
C2H_VEC_TRAITS_OVERLOAD(double, double)
|
||||
_CCCL_SUPPRESS_DEPRECATED_POP
|
||||
|
||||
# if _CCCL_CTK_AT_LEAST(13, 0)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(long4_16a, long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(long4_32a, long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(ulong4_16a, unsigned long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(ulong4_32a, unsigned long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(longlong4_16a, long long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(longlong4_32a, long long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(ulonglong4_16a, unsigned long long, 4)
|
||||
C2H_VEC_TRAITS_OVERLOAD_IMPL(ulonglong4_32a, unsigned long long, 4)
|
||||
# endif // _CCCL_CTK_AT_LEAST(13, 0)
|
||||
|
||||
# undef C2H_VEC_TRAITS_OVERLOAD
|
||||
# undef C2H_VEC_TRAITS_OVERLOAD_IMPL
|
||||
# undef REPEAT_TO_LIST_1
|
||||
# undef REPEAT_TO_LIST_2
|
||||
# undef REPEAT_TO_LIST_3
|
||||
# undef REPEAT_TO_LIST_4
|
||||
# undef REPEAT_TO_LIST
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// vector2 type traits
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_vector2_type_v = cuda::std::__is_one_of_v<
|
||||
cuda::std::remove_cv_t<T>,
|
||||
char2,
|
||||
short2,
|
||||
int2,
|
||||
long2,
|
||||
longlong2,
|
||||
uchar2,
|
||||
ushort2,
|
||||
uint2,
|
||||
ulong2,
|
||||
ulonglong2,
|
||||
float2,
|
||||
double2
|
||||
# if TEST_HALF_T()
|
||||
,
|
||||
__half2
|
||||
# endif // TEST_HALF_T()
|
||||
# if TEST_BF_T()
|
||||
,
|
||||
__nv_bfloat162
|
||||
# endif // TEST_BF_T()
|
||||
>;
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
// vector2 floating point type traits
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_vector2_fp_type_v = cuda::std::__is_one_of_v<cuda::std::remove_cv_t<T>, float2, double2>;
|
||||
|
||||
# if TEST_HALF_T()
|
||||
|
||||
template <>
|
||||
inline constexpr bool is_vector2_fp_type_v<__half2> = true;
|
||||
|
||||
# endif // TEST_HALF_T()
|
||||
|
||||
# if TEST_BF_T()
|
||||
|
||||
template <>
|
||||
inline constexpr bool is_vector2_fp_type_v<__nv_bfloat162> = true;
|
||||
|
||||
# endif // TEST_BF_T()
|
||||
|
||||
#endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
36
cccl_upstream/c2h/include/c2h/utility.h
Normal file
36
cccl_upstream/c2h/include/c2h/utility.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2023, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#ifdef __GNUC__
|
||||
# include <cxxabi.h>
|
||||
#endif // __GNUC__
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
// TODO(bgruber): duplicated version of thrust/testing/unittest/system.h
|
||||
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 // __GNUC__ && !_NVHPC_CUDA
|
||||
return name;
|
||||
#endif // __GNUC__ && !_NVHPC_CUDA
|
||||
}
|
||||
|
||||
// TODO(bgruber): duplicated version of thrust/testing/unittest/util.h
|
||||
template <typename T>
|
||||
std::string type_name()
|
||||
{
|
||||
return demangle(typeid(T).name());
|
||||
}
|
||||
} // namespace c2h
|
||||
68
cccl_upstream/c2h/include/c2h/vector.h
Normal file
68
cccl_upstream/c2h/include/c2h/vector.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <thrust/detail/vector_base.h>
|
||||
|
||||
#include <cuda/std/type_traits>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <catch2/catch_tostring.hpp>
|
||||
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
# include <c2h/checked_allocator.cuh>
|
||||
#else
|
||||
# include <thrust/device_vector.h>
|
||||
# include <thrust/host_vector.h>
|
||||
#endif
|
||||
|
||||
namespace c2h
|
||||
{
|
||||
#if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
template <typename T>
|
||||
using host_vector = THRUST_NS_QUALIFIER::detail::vector_base<T, c2h::checked_host_allocator<T>>;
|
||||
|
||||
template <typename T>
|
||||
using device_vector = THRUST_NS_QUALIFIER::detail::vector_base<T, c2h::checked_cuda_allocator<T>>;
|
||||
#else // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
using THRUST_NS_QUALIFIER::device_vector;
|
||||
using THRUST_NS_QUALIFIER::host_vector;
|
||||
#endif // THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
|
||||
} // namespace c2h
|
||||
|
||||
// We specialize how Catch2 prints ([signed|unsigned]) char vectors for better readability. Let's print them as numbers
|
||||
// instead of characters.
|
||||
template <typename T, typename A>
|
||||
struct Catch::StringMaker<THRUST_NS_QUALIFIER::detail::vector_base<T, A>,
|
||||
::cuda::std::enable_if_t<sizeof(T) == 1 && ::cuda::std::is_fundamental_v<T>>>
|
||||
{
|
||||
// Copied from `rangeToString` in catch_tostring.hpp
|
||||
static auto convert(const THRUST_NS_QUALIFIER::detail::vector_base<T, A>& v) -> std::string
|
||||
{
|
||||
auto first = v.begin();
|
||||
auto last = v.end();
|
||||
|
||||
ReusableStringStream rss;
|
||||
rss << "{ ";
|
||||
if (first != last)
|
||||
{
|
||||
rss << Detail::stringify(static_cast<unsigned>(static_cast<T>(*first)));
|
||||
for (++first; first != last; ++first)
|
||||
{
|
||||
rss << ", " << Detail::stringify(static_cast<unsigned>(static_cast<T>(*first)));
|
||||
}
|
||||
}
|
||||
rss << " }";
|
||||
return rss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// due to an nvcc bug, the above specialization of StringMaker is ambiguous with one inside Catch2, so let's disable
|
||||
// Catch2 range formatting for vector_base with sizeof(T) == 1 entirely
|
||||
template <typename T, typename A>
|
||||
struct Catch::is_range<THRUST_NS_QUALIFIER::detail::vector_base<T, A>>
|
||||
{
|
||||
static constexpr bool value = !(sizeof(T) == 1 && ::cuda::std::is_fundamental_v<T>);
|
||||
};
|
||||
Reference in New Issue
Block a user