Files
TheRock/rocm-libraries/projects/rocthrust/thrust/system/detail/generic/shuffle.inl
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

191 lines
6.7 KiB
C++

/*
* Copyright 2008-20120 NVIDIA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thrust/detail/config.h>
#include <thrust/detail/temporary_array.h>
#include <thrust/iterator/discard_iterator.h>
#include <thrust/iterator/transform_iterator.h>
#include <thrust/iterator/transform_output_iterator.h>
#include <thrust/random.h>
#include <thrust/scan.h>
#include <thrust/system/detail/generic/shuffle.h>
#include <cstdint>
THRUST_NAMESPACE_BEGIN
namespace system {
namespace detail {
namespace generic {
// An implementation of a Feistel cipher for operating on 64 bit keys
class feistel_bijection {
struct round_state {
std::uint32_t left;
std::uint32_t right;
};
public:
template <class URBG>
THRUST_HOST_DEVICE feistel_bijection(std::uint64_t m, URBG&& g) {
std::uint64_t total_bits = get_cipher_bits(m);
// Half bits rounded down
left_side_bits = total_bits / 2;
left_side_mask = (1ull << left_side_bits) - 1;
// Half the bits rounded up
right_side_bits = total_bits - left_side_bits;
right_side_mask = (1ull << right_side_bits) - 1;
for (std::uint32_t i = 0; i < num_rounds; i++) {
key[i] = g();
}
}
THRUST_HOST_DEVICE std::uint64_t nearest_power_of_two() const {
return 1ull << (left_side_bits + right_side_bits);
}
THRUST_HOST_DEVICE std::uint64_t operator()(const std::uint64_t val) const {
std::uint32_t state[2] = { static_cast<std::uint32_t>( val >> right_side_bits ), static_cast<std::uint32_t>( val & right_side_mask ) };
for( std::uint32_t i = 0; i < num_rounds; i++ )
{
std::uint32_t hi, lo;
constexpr std::uint64_t M0 = UINT64_C( 0xD2B74407B1CE6E93 );
mulhilo( M0, state[0], hi, lo );
lo = ( lo << ( right_side_bits - left_side_bits ) ) | state[1] >> left_side_bits;
state[0] = ( ( hi ^ key[i] ) ^ state[1] ) & left_side_mask;
state[1] = lo & right_side_mask;
}
// Combine the left and right sides together to get result
return (static_cast<std::uint64_t>(state[0]) << right_side_bits) | static_cast<std::uint64_t>(state[1]);
}
private:
// Perform 64 bit multiplication and save result in two 32 bit int
static THRUST_HOST_DEVICE void mulhilo( std::uint64_t a, std::uint64_t b, std::uint32_t& hi, std::uint32_t& lo )
{
std::uint64_t product = a * b;
hi = static_cast<std::uint32_t>( product >> 32 );
lo = static_cast<std::uint32_t>( product );
}
// Find the nearest power of two
static THRUST_HOST_DEVICE std::uint64_t get_cipher_bits(std::uint64_t m) {
if (m <= 16) return 4;
std::uint64_t i = 0;
m--;
while (m != 0) {
i++;
m >>= 1;
}
return i;
}
static constexpr std::uint32_t num_rounds = 24;
std::uint64_t right_side_bits;
std::uint64_t left_side_bits;
std::uint64_t right_side_mask;
std::uint64_t left_side_mask;
std::uint32_t key[num_rounds];
};
struct key_flag_tuple {
std::uint64_t key;
std::uint64_t flag;
};
// scan only flags
struct key_flag_scan_op {
THRUST_HOST_DEVICE key_flag_tuple operator()(const key_flag_tuple& a,
const key_flag_tuple& b) {
return {b.key, a.flag + b.flag};
}
};
struct construct_key_flag_op {
std::uint64_t m;
feistel_bijection bijection;
THRUST_HOST_DEVICE construct_key_flag_op(std::uint64_t m,
feistel_bijection bijection)
: m(m), bijection(bijection) {}
THRUST_HOST_DEVICE key_flag_tuple operator()(std::uint64_t idx) {
auto gather_key = bijection(idx);
return key_flag_tuple{gather_key, (gather_key < m) ? 1ull : 0ull};
}
};
template <typename InputIterT, typename OutputIterT>
struct write_output_op {
std::uint64_t m;
InputIterT in;
OutputIterT out;
// flag contains inclusive scan of valid keys
// perform gather using valid keys
THRUST_EXEC_CHECK_DISABLE
THRUST_HOST_DEVICE std::size_t operator()(key_flag_tuple x) {
if (x.key < m) {
// -1 because inclusive scan
out[x.flag - 1] = in[x.key];
}
return 0; // Discarded
}
};
template <typename ExecutionPolicy, typename RandomIterator, typename URBG>
THRUST_HOST_DEVICE void shuffle(
thrust::execution_policy<ExecutionPolicy>& exec, RandomIterator first,
RandomIterator last, URBG&& g) {
using InputType = typename thrust::iterator_value_t<RandomIterator>;
// copy input to temp buffer
thrust::detail::temporary_array<InputType, ExecutionPolicy> temp(exec, first,
last);
thrust::shuffle_copy(exec, temp.begin(), temp.end(), first, g);
}
template <typename ExecutionPolicy, typename RandomIterator,
typename OutputIterator, typename URBG>
THRUST_HOST_DEVICE void shuffle_copy(
thrust::execution_policy<ExecutionPolicy>& exec, RandomIterator first,
RandomIterator last, OutputIterator result, URBG&& g) {
// m is the length of the input
// we have an available bijection of length n via a feistel cipher
std::size_t m = last - first;
feistel_bijection bijection(m, g);
std::uint64_t n = bijection.nearest_power_of_two();
// perform stream compaction over length n bijection to get length m
// pseudorandom bijection over the original input
thrust::counting_iterator<std::uint64_t> indices(0);
thrust::transform_iterator<construct_key_flag_op, decltype(indices),
key_flag_tuple>
key_flag_it(indices, construct_key_flag_op(m, bijection));
write_output_op<RandomIterator, decltype(result)> write_functor{m, first,
result};
auto gather_output_it = thrust::make_transform_output_iterator(
thrust::discard_iterator<std::size_t>(), write_functor);
// the feistel_bijection outputs a stream of permuted indices in range [0,n)
// flag each value < m and compact it, so we have a set of permuted indices in
// range [0,m) each thread gathers an input element according to its
// pseudorandom permuted index
thrust::inclusive_scan(exec, key_flag_it, key_flag_it + n, gather_output_it,
key_flag_scan_op());
}
} // end namespace generic
} // end namespace detail
} // end namespace system
THRUST_NAMESPACE_END