Files
project_6/cccl_upstream/thrust/thrust/detail/random_bijection.h
EngineX CI 56fd68e7dd [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
2026-07-30 09:35:51 +00:00

140 lines
4.5 KiB
C++

// SPDX-FileCopyrightText: Copyright (c) 2008-2025, NVIDIA Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! \file random_bijection.h
//! \brief An implementation of a bijective function for use in shuffling
#pragma once
#include <thrust/detail/config.h>
#include <thrust/random.h>
#include <cuda/std/__algorithm/max.h>
#include <cuda/std/__bit/integral.h>
#include <cuda/std/__type_traits/is_convertible.h>
#include <cuda/std/__type_traits/is_integral.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/cstdint>
THRUST_NAMESPACE_BEGIN
namespace detail
{
//! \brief A Feistel cipher for operating on power of two sized problems
class feistel_bijection
{
public:
using index_type = std::uint64_t;
template <class URBG>
_CCCL_HOST_DEVICE feistel_bijection(std::uint64_t m, URBG&& g)
{
// Calculate number of bits needed to represent num_elements - 1
// Prevent zero
const uint64_t max_index = (::cuda::std::max) (static_cast<uint64_t>(1), m) - 1;
const uint64_t total_bits = static_cast<uint64_t>((::cuda::std::max) (8, ::cuda::std::bit_width(max_index)));
// Half bits rounded down
L_bits = total_bits / 2;
L_mask = (1ull << L_bits) - 1;
// Half the bits rounded up
R_bits = total_bits - L_bits;
R_mask = (1ull << R_bits) - 1;
thrust::uniform_int_distribution<std::uint32_t> dist;
for (auto& k : key)
{
k = dist(g);
}
}
_CCCL_HOST_DEVICE std::uint64_t size() const
{
return 1ull << (L_bits + R_bits);
}
_CCCL_HOST_DEVICE std::uint64_t operator()(const std::uint64_t val) const
{
// Unfortunately this is duplicated with libcudacxx/include/cuda/__random/feistel_bijection.h
// We cannot use the above because thrust PRNG generators incorrectly implement URBG requirements.
// Mitchell, Rory, et al. "Bandwidth-optimal random shuffling for GPUs." ACM Transactions on Parallel Computing 9.1
// (2022): 1-20.
uint32_t L = static_cast<uint32_t>(val >> R_bits);
uint32_t R = static_cast<uint32_t>(val & R_mask);
for (const auto k : key)
{
constexpr uint64_t m0 = 0xD2B74407B1CE6E93;
const uint64_t product = m0 * L;
uint32_t F_k = (product >> 32) ^ k;
uint32_t B_k = static_cast<uint32_t>(product);
uint32_t L_prime = F_k ^ R;
uint32_t R_prime = (B_k << (R_bits - L_bits)) | R >> L_bits;
L = L_prime & L_mask;
R = R_prime & R_mask;
}
// Combine the left and right sides together to get result
return (static_cast<uint64_t>(L) << R_bits) | static_cast<uint64_t>(R);
}
private:
static constexpr std::uint32_t num_rounds = 24;
std::uint64_t R_bits;
std::uint64_t L_bits;
std::uint64_t R_mask;
std::uint64_t L_mask;
std::uint32_t key[num_rounds];
};
//! \brief Adaptor for a bijection to work with any size problem. It achieves this by iterating the bijection until
//! the result is less than n. For feistel_bijection, the worst case number of iterations required for one call to
//! operator() is O(n) with low probability. It has amortised O(1) complexity.
//! \tparam IndexType The type of the index to shuffle.
//! \tparam Bijection The bijection to use. A low quality random bijection may lead to poor work balancing between calls
//! to the operator().
template <class IndexType, class Bijection = feistel_bijection>
class random_bijection
{
private:
static_assert(::cuda::std::is_integral_v<IndexType>, "IndexType must be an integral type");
static_assert(::cuda::std::is_convertible_v<IndexType, typename Bijection::index_type>,
"IndexType must be convertible to Bijection::index_type");
Bijection bijection;
IndexType n;
public:
using index_type = IndexType;
template <class URBG>
_CCCL_HOST_DEVICE random_bijection(IndexType n, URBG&& g)
: bijection(n, ::cuda::std::forward<URBG>(g))
, n(n)
{}
_CCCL_HOST_DEVICE IndexType operator()(IndexType i) const
{
auto upcast_i = static_cast<typename Bijection::index_type>(i);
auto upcast_n = static_cast<typename Bijection::index_type>(n);
// If i >= n, then this may loop forever.
if (upcast_i >= upcast_n)
{ // Avoid infinite loop.
_CCCL_ASSERT(false, "index out of range");
return upcast_i;
}
do
{
upcast_i = bijection(upcast_i);
} while (upcast_i >= upcast_n);
return static_cast<IndexType>(upcast_i);
}
_CCCL_HOST_DEVICE IndexType size() const
{
return n;
}
};
} // namespace detail
THRUST_NAMESPACE_END