Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion c/parallel/src/reduce.cu
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,13 @@ std::string get_device_reduce_kernel_name(
check(cccl_type_name_from_nvrtc<cuda::std::identity>(&transform_op_t));

return std::format(
"cub::detail::reduce::DeviceReduceKernel<{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}>",
"cub::detail::reduce::DeviceReduceKernel<{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}>",
policy_selector_t,
stable_reduction_order ? "true" : "false",
input_iterator_t,
stable_reduction_order ? std::string(accum_t) + "*" : output_iterator_t,
offset_t,
offset_t,
reduction_op_t,
accum_t,
init_t,
Expand Down
98 changes: 98 additions & 0 deletions cub/benchmarks/bench/reduce/deferred_nondeterministic.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause

#include <cub/device/device_reduce.cuh>

#include <thrust/detail/raw_pointer_cast.h>
#include <thrust/device_vector.h>

#include <cuda/argument>
#include <cuda/execution.determinism.h>
#include <cuda/execution.require.h>
#include <cuda/std/functional>

#include <cstddef>

#include <nvbench_helper.cuh>

#include <nvbench/range.cuh>
#include <nvbench/types.cuh>

// %RANGE% TUNE_ITEMS_PER_THREAD ipt 3:24:1
// %RANGE% TUNE_THREADS_PER_BLOCK tpb 128:1024:32
// %RANGE% TUNE_ITEMS_PER_VEC_LOAD_POW2 ipv 1:2:1

#if !TUNE_BASE
template <typename AccumT>
struct policy_selector
{
[[nodiscard]] _CCCL_HOST_DEVICE constexpr auto operator()(cuda::compute_capability) const -> cub::ReducePolicy
{
const auto [items, threads] =
cub::detail::scale_mem_bound(TUNE_THREADS_PER_BLOCK, TUNE_ITEMS_PER_THREAD, int{sizeof(AccumT)});
const auto policy = cub::ReducePassPolicy{
threads,
items,
1 << TUNE_ITEMS_PER_VEC_LOAD_POW2,
cub::BLOCK_REDUCE_WARP_REDUCTIONS_NONDETERMINISTIC,
cub::LOAD_DEFAULT};
return {policy, {}};
}
};
#endif // !TUNE_BASE

template <typename T, typename OffsetT>
void nondeterministic_sum(nvbench::state& state, nvbench::type_list<T, OffsetT>)
{
using op_t = cuda::std::plus<>;
using init_value_t = T;

// Retrieve axis parameters
const auto elements = static_cast<std::size_t>(state.get_int64("Elements{io}"));

thrust::device_vector<T> in = generate(elements);
thrust::device_vector<T> out(1, thrust::no_init);
thrust::device_vector<OffsetT> device_num_items(1, static_cast<OffsetT>(elements));

auto d_in = thrust::raw_pointer_cast(in.data());
auto d_out = thrust::raw_pointer_cast(out.data());
auto d_num_items = thrust::raw_pointer_cast(device_num_items.data());

// Enable throughput calculations and add "Size" column to results.
state.add_element_count(elements);
state.add_global_memory_reads<T>(elements, "Size");
state.add_global_memory_writes<T>(1);

caching_allocator_t alloc;
state.exec(nvbench::exec_tag::gpu | nvbench::exec_tag::no_batch, [&](nvbench::launch& launch) {
auto env = cub_bench_env(
alloc,
launch,
cuda::execution::require(cuda::execution::determinism::not_guaranteed)
#if !TUNE_BASE
,
cuda::execution::tune(policy_selector<cuda::std::__accumulator_t<op_t, T, init_value_t>>{})
#endif // !TUNE_BASE
);
_CCCL_TRY_CUDA_API(
cub::DeviceReduce::Reduce,
"Reduce failed",
d_in,
d_out,
cuda::args::deferred{d_num_items},
op_t{},
init_value_t{},
env);
});
}

#ifdef TUNE_T
using value_types = nvbench::type_list<TUNE_T>;
#else
using value_types = nvbench::type_list<int32_t, int64_t, float, double>;
#endif

NVBENCH_BENCH_TYPES(nondeterministic_sum, NVBENCH_TYPE_AXES(value_types, offset_types))
.set_name("base")
.set_type_axes_names({"T{ct}", "OffsetT{ct}"})
.add_int64_power_of_two_axis("Elements{io}", nvbench::range(16, 28, 4));
86 changes: 86 additions & 0 deletions cub/benchmarks/bench/reduce/deferred_sum.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
Comment thread
NaderAlAwar marked this conversation as resolved.
Outdated

#include <cub/device/device_reduce.cuh>

#include <thrust/detail/raw_pointer_cast.h>
#include <thrust/device_vector.h>

#include <cuda/argument>
#include <cuda/std/functional>

#include <nvbench_helper.cuh>

#include <nvbench/range.cuh>
#include <nvbench/types.cuh>

// %RANGE% TUNE_ITEMS_PER_THREAD ipt 7:24:1
// %RANGE% TUNE_THREADS_PER_BLOCK tpb 128:1024:32
// %RANGE% TUNE_ITEMS_PER_VEC_LOAD_POW2 ipv 1:2:1

#if !TUNE_BASE
template <typename AccumT>
struct policy_selector
{
[[nodiscard]] _CCCL_HOST_DEVICE constexpr auto operator()(cuda::compute_capability) const -> cub::ReducePolicy
{
const auto [items, threads] =
cub::detail::scale_mem_bound(TUNE_THREADS_PER_BLOCK, TUNE_ITEMS_PER_THREAD, int{sizeof(AccumT)});
const auto policy = cub::ReducePassPolicy{
threads, items, 1 << TUNE_ITEMS_PER_VEC_LOAD_POW2, cub::BLOCK_REDUCE_WARP_REDUCTIONS, cub::LOAD_DEFAULT};
return {policy, policy};
}
};
#endif // !TUNE_BASE

using op_t = cuda::std::plus<>;

template <typename T, typename OffsetT>
void reduce(nvbench::state& state, nvbench::type_list<T, OffsetT>)
{
using init_value_t = T;

// Retrieve axis parameters
const auto elements = state.get_int64("Elements{io}");

thrust::device_vector<T> in = generate(elements);
thrust::device_vector<T> out(1, thrust::default_init);
thrust::device_vector<OffsetT> device_num_items(1, static_cast<OffsetT>(elements));

auto d_in = thrust::raw_pointer_cast(in.data());
auto d_out = thrust::raw_pointer_cast(out.data());
auto d_num_items = thrust::raw_pointer_cast(device_num_items.data());

// Enable throughput calculations and add "Size" column to results.
state.add_element_count(elements);
state.add_global_memory_reads<T>(elements, "Size");
state.add_global_memory_writes<T>(1);

caching_allocator_t alloc;
state.exec(nvbench::exec_tag::gpu | nvbench::exec_tag::no_batch, [&](nvbench::launch& launch) {
auto env = cub_bench_env(
alloc,
launch
#if !TUNE_BASE
,
cuda::execution::tune(policy_selector<cuda::std::__accumulator_t<op_t, T, init_value_t>>{})
#endif // !TUNE_BASE
);
_CCCL_TRY_CUDA_API(
cub::DeviceReduce::Reduce,
"Reduce failed",
d_in,
d_out,
cuda::args::deferred{d_num_items},
op_t{},
init_value_t{},
env);
});
}

using value_types = all_types;

NVBENCH_BENCH_TYPES(reduce, NVBENCH_TYPE_AXES(value_types, offset_types))
.set_name("base")
.set_type_axes_names({"T{ct}", "OffsetT{ct}"})
.add_int64_power_of_two_axis("Elements{io}", nvbench::range(16, 28, 4));
63 changes: 63 additions & 0 deletions cub/cub/detail/deferred_parameter.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#pragma once

#include <cub/config.cuh>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
# pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
# pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
# pragma system_header
#endif // no system header

#include <cuda/argument>
Comment thread
NaderAlAwar marked this conversation as resolved.
Outdated
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/__utility/declval.h>

CUB_NAMESPACE_BEGIN

namespace detail
{
#if !_CCCL_COMPILER(NVRTC)
//! Normalizes a single-value argument without reading a deferred source.
Comment thread
NaderAlAwar marked this conversation as resolved.
Outdated
//! Immediate values are converted to TargetT. Deferred arguments are unwrapped to their source, erasing bounds from
//! the kernel type and payload.
template <typename TargetT, typename ParameterT>
[[nodiscard]] CUB_RUNTIME_FUNCTION _CCCL_FORCEINLINE constexpr auto normalize_parameter(ParameterT parameter) noexcept
Comment thread
NaderAlAwar marked this conversation as resolved.
Outdated
{
using args_traits_t = ::cuda::args::__traits<ParameterT>;
static_assert(args_traits_t::is_single_value, "parameter must contain a single value");

if constexpr (args_traits_t::is_deferred)
{
return ::cuda::args::__unwrap(parameter);
}
else
{
return static_cast<TargetT>(::cuda::args::__unwrap(parameter));
}
}

template <typename TargetT, typename ParameterT>
using normalized_parameter_t = decltype(normalize_parameter<TargetT>(::cuda::std::declval<ParameterT>()));
#endif // !_CCCL_COMPILER(NVRTC)

//! Resolves a normalized parameter to TargetT, reading element zero when the parameter is a deferred source.
template <typename TargetT, typename ParameterT>
[[nodiscard]] _CCCL_DEVICE _CCCL_FORCEINLINE TargetT resolve_parameter(ParameterT parameter) noexcept
Comment thread
NaderAlAwar marked this conversation as resolved.
Outdated
{
if constexpr (::cuda::std::is_same_v<ParameterT, TargetT>)
{
return parameter;
}
else
{
return static_cast<TargetT>(parameter[0]);
}
}
} // namespace detail

CUB_NAMESPACE_END
Loading
Loading