[INFRA] Import NVIDIA/CCCL upstream as optimization reference library
CCCL (CUDA C++ Core Libraries) provides: - CUB: device/block/warp-level GPU primitives (reduce, scan, sort, topk) - Thrust: high-level parallel algorithms (transform_reduce, sort, scan) - libcudacxx: CUDA C++ standard library (atomics, barriers, memory) - cudax: experimental features (memory resources, allocators) - Tuning policies: per-SM hardware-specific algorithm parameters Competition optimization vectors mapped to CCCL: - Output TPS (83% weight): warp_reduce, block_reduce, device_topk - Input TPS (14% weight): device_scan, block_load, prefetch - Cache TPS (3% weight): prefix caching strategy patterns - Memory (0.9 util): pooled/cached/buddy allocators Source: https://github.com/NVIDIA/cccl (shallow clone, HEAD only) License: Apache-2.0
This commit is contained in:
216
cccl_upstream/cub/benchmarks/bench/transform/tile/babelstream.cu
Normal file
216
cccl_upstream/cub/benchmarks/bench/transform/tile/babelstream.cu
Normal file
@@ -0,0 +1,216 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
// Tile variant of the BabelStream transform bench. The lambdas of the base benchmark are replaced by
|
||||
// named, stateless ops that register a tile_operator substitute (gated). Under --enable-tile +
|
||||
// CCCL_ENABLE_EXPERIMENTAL_TILE_TRANSFORM_DISPATCH the dispatch hook routes them to the tile kernel; otherwise this
|
||||
// is the standard CUB transform path. This file disappears once tile dispatch is fully transparent.
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
# include <cuda_tile.h>
|
||||
#endif
|
||||
|
||||
// Stateless scalar ops, used at the call site in both build modes. Constants are baked in so the ops
|
||||
// stay stateless (the tile substitute must be trivially default constructible): with startScalar == -2,
|
||||
// `c * scalar` is `-(c + c)`, `b + scalar * c` is `b - c - c`, etc.
|
||||
struct mul_op
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class B>
|
||||
_CCCL_API auto operator()(B b) const
|
||||
{
|
||||
return -(b + b);
|
||||
}
|
||||
};
|
||||
struct add_op
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class A, class B>
|
||||
_CCCL_API auto operator()(A a, B b) const
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
struct triad_op
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class B, class C>
|
||||
_CCCL_API auto operator()(B b, C c) const
|
||||
{
|
||||
return b - c - c;
|
||||
}
|
||||
};
|
||||
struct nstream_op
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class A, class B, class C>
|
||||
_CCCL_API auto operator()(A a, B b, C c) const
|
||||
{
|
||||
return a + b - c - c;
|
||||
}
|
||||
};
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
CUB_NAMESPACE_BEGIN
|
||||
namespace detail::transform::tile
|
||||
{
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<mul_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<add_op, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<triad_op, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<nstream_op, T, 3> = true;
|
||||
template <>
|
||||
struct tile_operator<mul_op>
|
||||
{
|
||||
using type = mul_op;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<add_op>
|
||||
{
|
||||
using type = add_op;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<triad_op>
|
||||
{
|
||||
using type = triad_op;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<nstream_op>
|
||||
{
|
||||
using type = nstream_op;
|
||||
};
|
||||
} // namespace detail::transform::tile
|
||||
CUB_NAMESPACE_END
|
||||
#endif // _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
|
||||
// The tile path does not support __int128 (no tensor_span/partition_view for it), so the type axis
|
||||
// omits it relative to the base babelstream bench.
|
||||
#ifdef TUNE_T
|
||||
using element_types = nvbench::type_list<TUNE_T>;
|
||||
#else
|
||||
using element_types = nvbench::type_list<nvbench::int8_t, nvbench::int16_t, nvbench::float32_t, nvbench::float64_t>;
|
||||
#endif
|
||||
|
||||
inline auto array_size_powers = nvbench::range(16, 32, 4);
|
||||
|
||||
// Same constant inputs as the base bench so nstream maintains a consistent workload.
|
||||
inline constexpr auto startA = 11;
|
||||
inline constexpr auto startB = 2;
|
||||
inline constexpr auto startC = 1;
|
||||
inline constexpr auto startScalar = -2;
|
||||
static_assert(startA == (startA + startB + startScalar * startC), "nstream must have a consistent workload");
|
||||
|
||||
template <typename T>
|
||||
static void mul(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
const bool unaligned = state.get_string("Aligned") == "no";
|
||||
thrust::device_vector<T> b(n + unaligned, startB);
|
||||
thrust::device_vector<T> c(n + unaligned, startC);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(state, cuda::std::tuple{c.begin() + unaligned}, b.begin() + unaligned, n, mul_op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(mul, NVBENCH_TYPE_AXES(element_types))
|
||||
.set_name("tile_mul")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_string_axis("Aligned", {"yes", "no"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", array_size_powers);
|
||||
|
||||
template <typename T>
|
||||
static void add(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
const bool unaligned = state.get_string("Aligned") == "no";
|
||||
thrust::device_vector<T> a(n + unaligned, startA);
|
||||
thrust::device_vector<T> b(n + unaligned, startB);
|
||||
thrust::device_vector<T> c(n + unaligned, startC);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(2 * n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(
|
||||
state, cuda::std::tuple{a.begin() + unaligned, b.begin() + unaligned}, c.begin() + unaligned, n, add_op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(add, NVBENCH_TYPE_AXES(element_types))
|
||||
.set_name("tile_add")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_string_axis("Aligned", {"yes", "no"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", array_size_powers);
|
||||
|
||||
template <typename T>
|
||||
static void triad(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
const bool unaligned = state.get_string("Aligned") == "no";
|
||||
thrust::device_vector<T> a(n + unaligned, startA);
|
||||
thrust::device_vector<T> b(n + unaligned, startB);
|
||||
thrust::device_vector<T> c(n + unaligned, startC);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(2 * n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(
|
||||
state, cuda::std::tuple{b.begin() + unaligned, c.begin() + unaligned}, a.begin() + unaligned, n, triad_op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(triad, NVBENCH_TYPE_AXES(element_types))
|
||||
.set_name("tile_triad")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_string_axis("Aligned", {"yes", "no"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", array_size_powers);
|
||||
|
||||
template <typename T>
|
||||
static void nstream(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
const bool unaligned = state.get_string("Aligned") == "no";
|
||||
thrust::device_vector<T> a(n + unaligned, startA);
|
||||
thrust::device_vector<T> b(n + unaligned, startB);
|
||||
thrust::device_vector<T> c(n + unaligned, startC);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(3 * n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(
|
||||
state,
|
||||
cuda::std::tuple{a.begin() + unaligned, b.begin() + unaligned, c.begin() + unaligned},
|
||||
a.begin() + unaligned,
|
||||
n,
|
||||
nstream_op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(nstream, NVBENCH_TYPE_AXES(element_types))
|
||||
.set_name("tile_nstream")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_string_axis("Aligned", {"yes", "no"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", array_size_powers);
|
||||
69
cccl_upstream/cub/benchmarks/bench/transform/tile/copy.cu
Normal file
69
cccl_upstream/cub/benchmarks/bench/transform/tile/copy.cu
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
// Pure copy (identity transform) -- measures plain load/store bandwidth through the tile
|
||||
// load_masked/store_masked path. The identity op registers a tile_operator substitute (gated); under
|
||||
// --enable-tile + CCCL_ENABLE_EXPERIMENTAL_TILE_TRANSFORM_DISPATCH the dispatch hook routes it to the tile kernel,
|
||||
// otherwise it falls through to CUB's standard transform. This file disappears once tile dispatch is
|
||||
// fully transparent.
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
# include <cuda_tile.h>
|
||||
#endif
|
||||
|
||||
struct identity
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class T>
|
||||
_CCCL_API auto operator()(T v) const
|
||||
{
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
CUB_NAMESPACE_BEGIN
|
||||
namespace detail::transform::tile
|
||||
{
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<identity, T, 1> = true;
|
||||
template <>
|
||||
struct tile_operator<identity>
|
||||
{
|
||||
using type = identity;
|
||||
};
|
||||
} // namespace detail::transform::tile
|
||||
CUB_NAMESPACE_END
|
||||
#endif // _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
|
||||
#ifdef TUNE_T
|
||||
using element_types = nvbench::type_list<TUNE_T>;
|
||||
#else
|
||||
using element_types = nvbench::type_list<nvbench::int8_t, nvbench::int16_t, nvbench::int32_t, nvbench::float64_t>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
static void copy(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
|
||||
thrust::device_vector<T> in = generate(n);
|
||||
thrust::device_vector<T> out(n, thrust::no_init);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(state, cuda::std::tuple{in.begin()}, out.begin(), n, identity{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(copy, NVBENCH_TYPE_AXES(element_types))
|
||||
.set_name("tile_copy")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", nvbench::range(16, 32, 4));
|
||||
@@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
// Tile variant of the grayscale transform bench. Unlike the base bench (a single rgb_t<T> struct
|
||||
// input), this uses three separate R/G/B streams so the inputs are plain element types the tile path
|
||||
// can vectorize. The named rgb_to_y op registers a tile_operator substitute (gated). This file
|
||||
// disappears once tile dispatch is fully transparent.
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
# include <cuda_tile.h>
|
||||
#endif
|
||||
|
||||
struct rgb_to_y
|
||||
{
|
||||
_CCCL_EXEC_CHECK_DISABLE
|
||||
template <class R, class G, class B>
|
||||
_CCCL_API auto operator()(R r, G g, B b) const
|
||||
{
|
||||
constexpr float w_r = 0.2989f;
|
||||
constexpr float w_g = 0.587f;
|
||||
constexpr float w_b = 0.114f;
|
||||
return w_r * r + w_g * g + w_b * b;
|
||||
}
|
||||
};
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
CUB_NAMESPACE_BEGIN
|
||||
namespace detail::transform::tile
|
||||
{
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<rgb_to_y, T, 3> = true;
|
||||
template <>
|
||||
struct tile_operator<rgb_to_y>
|
||||
{
|
||||
using type = rgb_to_y;
|
||||
};
|
||||
} // namespace detail::transform::tile
|
||||
CUB_NAMESPACE_END
|
||||
#endif // _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
|
||||
#ifdef TUNE_T
|
||||
using value_types = nvbench::type_list<TUNE_T>;
|
||||
#else
|
||||
using value_types = nvbench::type_list<nvbench::float32_t, nvbench::float64_t>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
static void grayscale(nvbench::state& state, nvbench::type_list<T>)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
|
||||
thrust::device_vector<T> r = generate(n);
|
||||
thrust::device_vector<T> g = generate(n);
|
||||
thrust::device_vector<T> b = generate(n);
|
||||
thrust::device_vector<T> out(n, thrust::no_init);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(3 * n); // matches the base bench's rgb_t<T> = 3 * sizeof(T)
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(state, cuda::std::tuple{r.begin(), g.begin(), b.begin()}, out.begin(), n, rgb_to_y{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
NVBENCH_BENCH_TYPES(grayscale, NVBENCH_TYPE_AXES(value_types))
|
||||
.set_name("tile_grayscale")
|
||||
.set_type_axes_names({"T{ct}"})
|
||||
.add_int64_power_of_two_axis("Elements{io}", nvbench::range(16, 32, 4));
|
||||
493
cccl_upstream/cub/benchmarks/bench/transform/tile/pytorch.cu
Normal file
493
cccl_upstream/cub/benchmarks/bench/transform/tile/pytorch.cu
Normal file
@@ -0,0 +1,493 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
// Tile variant of the PyTorch-style transform benches. Each named op registers a tile_operator
|
||||
// substitute (gated); MUFU-heavy ops also opt into tile_mufu_heavy_v so the tile policy picker caps
|
||||
// items/thread at the vector width on sub-4-byte types. Under --enable-tile +
|
||||
// CCCL_ENABLE_EXPERIMENTAL_TILE_TRANSFORM_DISPATCH the dispatch hook routes them to the tile kernel; otherwise this
|
||||
// is the standard CUB path. This file disappears once tile dispatch is fully transparent.
|
||||
|
||||
#include <cuda_bf16.h>
|
||||
#include <cuda_fp16.h>
|
||||
|
||||
#include <cuda/std/cmath>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
# include <cuda_tile.h>
|
||||
#endif
|
||||
|
||||
// Scalar ops the user passes to Transform. Sub-4-byte input types compute in float and cast back,
|
||||
// matching the tile substitutes below.
|
||||
template <class T>
|
||||
__host__ __device__ float to_f(T v)
|
||||
{
|
||||
return static_cast<float>(v);
|
||||
}
|
||||
template <class T>
|
||||
__host__ __device__ T from_f(float f)
|
||||
{
|
||||
return static_cast<T>(f);
|
||||
}
|
||||
|
||||
struct relu_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
float f = to_f(v);
|
||||
return from_f<T>(f > 0.0f ? f : 0.0f);
|
||||
}
|
||||
};
|
||||
struct sigmoid_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
float f = to_f(v);
|
||||
return from_f<T>(1.0f / (1.0f + ::cuda::std::exp(-f)));
|
||||
}
|
||||
};
|
||||
struct tanh_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
return from_f<T>(::cuda::std::tanh(to_f(v)));
|
||||
}
|
||||
};
|
||||
struct gelu_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
constexpr float k0 = 0.7978845608028654f, k1 = 0.044715f;
|
||||
float f = to_f(v);
|
||||
return from_f<T>(0.5f * f * (1.0f + ::cuda::std::tanh(k0 * (f + k1 * f * f * f))));
|
||||
}
|
||||
};
|
||||
struct sin_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
return from_f<T>(::cuda::std::sin(to_f(v)));
|
||||
}
|
||||
};
|
||||
struct exp_op
|
||||
{
|
||||
template <class T>
|
||||
__host__ __device__ T operator()(T v) const
|
||||
{
|
||||
return from_f<T>(::cuda::std::exp(to_f(v)));
|
||||
}
|
||||
};
|
||||
|
||||
struct binary_add
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
struct binary_sub
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
struct binary_mul
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
};
|
||||
struct binary_div
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
};
|
||||
struct binary_le
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ A operator()(A a, B b) const
|
||||
{
|
||||
return static_cast<A>(a <= b);
|
||||
}
|
||||
};
|
||||
struct binary_ge
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ A operator()(A a, B b) const
|
||||
{
|
||||
return static_cast<A>(a >= b);
|
||||
}
|
||||
};
|
||||
struct binary_fmin
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
};
|
||||
struct binary_fmax
|
||||
{
|
||||
template <class A, class B>
|
||||
__host__ __device__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
};
|
||||
|
||||
#if _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
namespace ct = ::cuda::tiles;
|
||||
|
||||
template <class T>
|
||||
__tile__ auto as_float(T v)
|
||||
{
|
||||
return ct::element_cast<float>(v);
|
||||
}
|
||||
template <class T, class F>
|
||||
__tile__ auto from_float(F f)
|
||||
{
|
||||
return ct::element_cast<ct::tile_element_t<T>>(f);
|
||||
}
|
||||
|
||||
struct tile_relu
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
auto f = as_float(v);
|
||||
return from_float<T>(ct::select(f > 0.0f, f, f - f));
|
||||
}
|
||||
};
|
||||
struct tile_sigmoid
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
auto f = as_float(v);
|
||||
return from_float<T>(1.0f / (1.0f + ct::exp(-f)));
|
||||
}
|
||||
};
|
||||
struct tile_tanh
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
return from_float<T>(ct::tanh(as_float(v)));
|
||||
}
|
||||
};
|
||||
struct tile_gelu
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
constexpr float k0 = 0.7978845608028654f, k1 = 0.044715f;
|
||||
auto f = as_float(v);
|
||||
return from_float<T>(0.5f * f * (1.0f + ct::tanh(k0 * (f + k1 * f * f * f))));
|
||||
}
|
||||
};
|
||||
struct tile_sin
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
return from_float<T>(ct::sin(as_float(v)));
|
||||
}
|
||||
};
|
||||
struct tile_exp
|
||||
{
|
||||
template <class T>
|
||||
__tile__ auto operator()(T v) const
|
||||
{
|
||||
return from_float<T>(ct::exp(as_float(v)));
|
||||
}
|
||||
};
|
||||
|
||||
struct tile_binary_add
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
struct tile_binary_sub
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
struct tile_binary_mul
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
};
|
||||
struct tile_binary_div
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return a / b;
|
||||
}
|
||||
};
|
||||
struct tile_binary_le
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return ct::element_cast<ct::tile_element_t<A>>(a <= b);
|
||||
}
|
||||
};
|
||||
struct tile_binary_ge
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return ct::element_cast<ct::tile_element_t<A>>(a >= b);
|
||||
}
|
||||
};
|
||||
struct tile_binary_fmin
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return ct::select(a < b, a, b);
|
||||
}
|
||||
};
|
||||
struct tile_binary_fmax
|
||||
{
|
||||
template <class A, class B>
|
||||
__tile__ auto operator()(A a, B b) const
|
||||
{
|
||||
return ct::select(a > b, a, b);
|
||||
}
|
||||
};
|
||||
|
||||
CUB_NAMESPACE_BEGIN
|
||||
namespace detail::transform::tile
|
||||
{
|
||||
// Unary
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<relu_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<sigmoid_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<tanh_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<gelu_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<sin_op, T, 1> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<exp_op, T, 1> = true;
|
||||
template <>
|
||||
struct tile_operator<relu_op>
|
||||
{
|
||||
using type = tile_relu;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<sigmoid_op>
|
||||
{
|
||||
using type = tile_sigmoid;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<tanh_op>
|
||||
{
|
||||
using type = tile_tanh;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<gelu_op>
|
||||
{
|
||||
using type = tile_gelu;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<sin_op>
|
||||
{
|
||||
using type = tile_sin;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<exp_op>
|
||||
{
|
||||
using type = tile_exp;
|
||||
};
|
||||
|
||||
// MUFU-heavy unary ops: hint the tile policy picker to cap items/thread at the vector width on
|
||||
// sub-4-byte types.
|
||||
template <>
|
||||
inline constexpr bool tile_mufu_heavy_v<sigmoid_op> = true;
|
||||
template <>
|
||||
inline constexpr bool tile_mufu_heavy_v<tanh_op> = true;
|
||||
template <>
|
||||
inline constexpr bool tile_mufu_heavy_v<gelu_op> = true;
|
||||
template <>
|
||||
inline constexpr bool tile_mufu_heavy_v<sin_op> = true;
|
||||
template <>
|
||||
inline constexpr bool tile_mufu_heavy_v<exp_op> = true;
|
||||
|
||||
// Binary
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_add, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_sub, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_mul, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_div, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_le, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_ge, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_fmin, T, 2> = true;
|
||||
template <class T>
|
||||
inline constexpr bool tile_eligible_v<binary_fmax, T, 2> = true;
|
||||
template <>
|
||||
struct tile_operator<binary_add>
|
||||
{
|
||||
using type = tile_binary_add;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_sub>
|
||||
{
|
||||
using type = tile_binary_sub;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_mul>
|
||||
{
|
||||
using type = tile_binary_mul;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_div>
|
||||
{
|
||||
using type = tile_binary_div;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_le>
|
||||
{
|
||||
using type = tile_binary_le;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_ge>
|
||||
{
|
||||
using type = tile_binary_ge;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_fmin>
|
||||
{
|
||||
using type = tile_binary_fmin;
|
||||
};
|
||||
template <>
|
||||
struct tile_operator<binary_fmax>
|
||||
{
|
||||
using type = tile_binary_fmax;
|
||||
};
|
||||
} // namespace detail::transform::tile
|
||||
CUB_NAMESPACE_END
|
||||
#endif // _CCCL_CUB_TILE_TRANSFORM_DISPATCH_ENABLED()
|
||||
|
||||
#ifdef TUNE_T
|
||||
using element_types = nvbench::type_list<TUNE_T>;
|
||||
#else
|
||||
using element_types = nvbench::type_list<
|
||||
# if _CCCL_HAS_NVFP16() && _CCCL_CTK_AT_LEAST(12, 2)
|
||||
__half,
|
||||
# endif
|
||||
# if _CCCL_HAS_NVBF16() && _CCCL_CTK_AT_LEAST(12, 2)
|
||||
__nv_bfloat16,
|
||||
# endif
|
||||
nvbench::float32_t>;
|
||||
#endif
|
||||
|
||||
template <typename Op, typename T>
|
||||
static void run_unary(nvbench::state& state)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
thrust::device_vector<T> in(n, T(1));
|
||||
thrust::device_vector<T> out(n, thrust::no_init);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(state, cuda::std::tuple{in.begin()}, out.begin(), n, Op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
template <typename Op, typename T>
|
||||
static void run_binary(nvbench::state& state)
|
||||
try
|
||||
{
|
||||
const auto n = state.get_int64("Elements{io}");
|
||||
thrust::device_vector<T> a(n, T(1));
|
||||
thrust::device_vector<T> b(n, T(1));
|
||||
thrust::device_vector<T> out(n, thrust::no_init);
|
||||
|
||||
state.add_element_count(n);
|
||||
state.add_global_memory_reads<T>(2 * n);
|
||||
state.add_global_memory_writes<T>(n);
|
||||
bench_transform(state, cuda::std::tuple{a.begin(), b.begin()}, out.begin(), n, Op{});
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
state.skip("Skipping: out of memory.");
|
||||
}
|
||||
|
||||
inline auto pt_sizes = nvbench::range(16, 32, 4);
|
||||
|
||||
#define UNARY_BENCH(name, op) \
|
||||
template <typename T> \
|
||||
static void name##_bench(nvbench::state& state, nvbench::type_list<T>) \
|
||||
{ \
|
||||
run_unary<op, T>(state); \
|
||||
} \
|
||||
NVBENCH_BENCH_TYPES(name##_bench, NVBENCH_TYPE_AXES(element_types)) \
|
||||
.set_name("tile_" #name) \
|
||||
.set_type_axes_names({"T{ct}"}) \
|
||||
.add_int64_power_of_two_axis("Elements{io}", pt_sizes)
|
||||
|
||||
UNARY_BENCH(relu, relu_op);
|
||||
UNARY_BENCH(sigmoid, sigmoid_op);
|
||||
UNARY_BENCH(tanh, tanh_op);
|
||||
UNARY_BENCH(gelu, gelu_op);
|
||||
UNARY_BENCH(sin, sin_op);
|
||||
UNARY_BENCH(exp, exp_op);
|
||||
|
||||
#define BINARY_BENCH(name, op) \
|
||||
template <typename T> \
|
||||
static void name##_bench(nvbench::state& state, nvbench::type_list<T>) \
|
||||
{ \
|
||||
run_binary<op, T>(state); \
|
||||
} \
|
||||
NVBENCH_BENCH_TYPES(name##_bench, NVBENCH_TYPE_AXES(element_types)) \
|
||||
.set_name("tile_pt_" #name) \
|
||||
.set_type_axes_names({"T{ct}"}) \
|
||||
.add_int64_power_of_two_axis("Elements{io}", pt_sizes)
|
||||
|
||||
BINARY_BENCH(add, binary_add);
|
||||
BINARY_BENCH(sub, binary_sub);
|
||||
BINARY_BENCH(mul, binary_mul);
|
||||
BINARY_BENCH(div, binary_div);
|
||||
BINARY_BENCH(le, binary_le);
|
||||
BINARY_BENCH(ge, binary_ge);
|
||||
BINARY_BENCH(fmin, binary_fmin);
|
||||
BINARY_BENCH(fmax, binary_fmax);
|
||||
Reference in New Issue
Block a user