[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:
EngineX CI
2026-07-30 09:35:51 +00:00
parent b4d01f481e
commit 56fd68e7dd
8871 changed files with 1454674 additions and 0 deletions

View 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);

View 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));

View File

@@ -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));

View 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);