Files
TheRock/rocm-libraries/projects/rocthrust/thrust/system/hip/detail/set_operations.h
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

1737 lines
48 KiB
C++

/******************************************************************************
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
* Modifications Copyright (c) 2019-2025, Advanced Micro Devices, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the NVIDIA CORPORATION nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
#pragma once
#include <thrust/detail/config.h>
#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
#if THRUST_DEVICE_COMPILER == THRUST_DEVICE_COMPILER_HIP
# include <thrust/detail/alignment.h>
# include <thrust/detail/mpl/math.h>
# include <thrust/detail/temporary_array.h>
# include <thrust/distance.h>
# include <thrust/extrema.h>
# include <thrust/pair.h>
# include <thrust/set_operations.h>
# include <thrust/system/hip/detail/execution_policy.h>
# include <thrust/system/hip/detail/general/temp_storage.h>
# include <thrust/system/hip/detail/get_value.h>
# include <thrust/system/hip/detail/par_to_seq.h>
# include <thrust/system/hip/detail/util.h>
# include <cstdint>
// rocprim include
# include <rocprim/rocprim.hpp>
THRUST_NAMESPACE_BEGIN
namespace hip_rocprim
{
namespace __set_operations
{
template <bool UpperBound, class IntT, class Size, class It, class T, class Comp>
THRUST_HIP_DEVICE_FUNCTION void binary_search_iteration(It data, Size& begin, Size& end, T key, int shift, Comp comp)
{
IntT scale = (1 << shift) - 1;
Size mid = (begin + scale * end) >> shift;
T key2 = data[mid];
bool pred = UpperBound ? !comp(key, key2) : comp(key2, key);
if (pred)
{
begin = mid + 1;
}
else
{
end = mid;
}
}
template <bool UpperBound, class Size, class T, class It, class Comp>
THRUST_HIP_DEVICE_FUNCTION Size binary_search(It data, Size count, T key, Comp comp)
{
Size begin = 0;
Size end = count;
while (begin < end)
{
binary_search_iteration<UpperBound, int>(data, begin, end, key, 1, comp);
}
return begin;
}
template <bool UpperBound, class IntT, class Size, class T, class It, class Comp>
THRUST_HIP_DEVICE_FUNCTION Size biased_binary_search(It data, Size count, T key, IntT levels, Comp comp)
{
Size begin = 0;
Size end = count;
if (levels >= 4 && begin < end)
{
binary_search_iteration<UpperBound, IntT>(data, begin, end, key, 9, comp);
}
if (levels >= 3 && begin < end)
{
binary_search_iteration<UpperBound, IntT>(data, begin, end, key, 7, comp);
}
if (levels >= 2 && begin < end)
{
binary_search_iteration<UpperBound, IntT>(data, begin, end, key, 5, comp);
}
if (levels >= 1 && begin < end)
{
binary_search_iteration<UpperBound, IntT>(data, begin, end, key, 4, comp);
}
while (begin < end)
{
binary_search_iteration<UpperBound, IntT>(data, begin, end, key, 1, comp);
}
return begin;
}
template <bool UpperBound, class Size, class It1, class It2, class Comp>
THRUST_HIP_DEVICE_FUNCTION Size merge_path(It1 a, Size aCount, It2 b, Size bCount, Size diag, Comp comp)
{
using T = typename thrust::iterator_traits<It1>::value_type;
Size begin = thrust::max<Size>(0, diag - bCount);
Size end = thrust::min<Size>(diag, aCount);
while (begin < end)
{
Size mid = (begin + end) >> 1;
T aKey = a[mid];
T bKey = b[diag - 1 - mid];
bool pred = UpperBound ? comp(aKey, bKey) : !comp(bKey, aKey);
if (pred)
{
begin = mid + 1;
}
else
{
end = mid;
}
}
return begin;
}
template <class It1, class It2, class Size, class Size2, class CompareOp>
THRUST_HIP_DEVICE_FUNCTION pair<Size, Size>
balanced_path(It1 keys1, It2 keys2, Size num_keys1, Size num_keys2, Size diag, Size2 levels, CompareOp compare_op)
{
using T = typename iterator_traits<It1>::value_type;
Size index1 = merge_path<false>(keys1, num_keys1, keys2, num_keys2, diag, compare_op);
Size index2 = diag - index1;
bool star = false;
if (index2 < num_keys2)
{
T x = keys2[index2];
// Search for the beginning of the duplicate run in both A and B.
Size start1 = biased_binary_search<false>(keys1, index1, x, levels, compare_op);
Size start2 = biased_binary_search<false>(keys2, index2, x, levels, compare_op);
// The distance between x's merge path and its lower_bound is its rank.
// We add up the a and b ranks and evenly distribute them to
// get a stairstep path.
Size run1 = index1 - start1;
Size run2 = index2 - start2;
Size total_run = run1 + run2;
// Attempt to advance b and regress a.
Size advance2 = max<Size>(total_run >> 1, total_run - run1);
Size end2 = min<Size>(num_keys2, start2 + advance2 + 1);
Size run_end2 = index2 + binary_search<true>(keys2 + index2, end2 - index2, x, compare_op);
run2 = run_end2 - start2;
advance2 = min<Size>(advance2, run2);
Size advance1 = total_run - advance2;
bool round_up = (advance1 == advance2 + 1) && (advance2 < run2);
if (round_up)
{
star = true;
}
index1 = start1 + advance1;
}
return thrust::make_pair(index1, (diag - index1) + star);
} // func balanced_path
template <unsigned int BlockSize, unsigned int ItemsPerThread>
using set_operations_config = rocprim::kernel_config<BlockSize, ItemsPerThread>;
template <class Key, class Value>
struct set_operations_config_900
{
static constexpr unsigned int item_scale =
::rocprim::detail::ceiling_div<unsigned int>(::rocprim::max(sizeof(Key), sizeof(Value)), sizeof(int));
using type = set_operations_config<256, ::rocprim::max(1u, 16u / item_scale)>;
};
template <class Config,
class KeysIt1,
class KeysIt2,
class ValuesIt1,
class ValuesIt2,
class KeysOutputIt,
class ValuesOutputIt,
class Size,
class CompareOp,
class SetOp,
bool HAS_VALUES>
struct SetOpAgent
{
using key_type = typename iterator_traits<KeysIt1>::value_type;
using value_type = typename iterator_traits<ValuesIt1>::value_type;
static constexpr int BLOCK_THREADS = Config::block_size;
static constexpr int ITEMS_PER_THREAD = Config::items_per_thread;
//---------------------------------------------------------------------
// Utility functions
//---------------------------------------------------------------------
template <bool IS_FULL_TILE, class T, class It1, class It2>
THRUST_HIP_DEVICE_FUNCTION void
gmem_to_reg(T (&output)[ITEMS_PER_THREAD], It1 input1, It2 input2, int count1, int count2)
{
const unsigned int thread_id = ::rocprim::detail::block_thread_id<0>();
if (IS_FULL_TILE)
{
# pragma unroll
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD - 1; ++ITEM)
{
int idx = BLOCK_THREADS * ITEM + thread_id;
output[ITEM] = (idx < count1) ? static_cast<T>(input1[idx]) : static_cast<T>(input2[idx - count1]);
}
// last ITEM might be a conditional load even for full tiles
// please check first before attempting to load.
int ITEM = ITEMS_PER_THREAD - 1;
int idx = BLOCK_THREADS * ITEM + thread_id;
if (idx < count1 + count2)
{
output[ITEM] = (idx < count1) ? static_cast<T>(input1[idx]) : static_cast<T>(input2[idx - count1]);
}
}
else
{
# pragma unroll
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
{
int idx = BLOCK_THREADS * ITEM + thread_id;
if (idx < count1 + count2)
{
output[ITEM] = (idx < count1) ? static_cast<T>(input1[idx]) : static_cast<T>(input2[idx - count1]);
}
}
}
}
template <class T, unsigned int OutputSize>
THRUST_HIP_DEVICE_FUNCTION void
reg_to_shared(rocprim::uninitialized_array<T, OutputSize>& output, T (&input)[ITEMS_PER_THREAD])
{
const unsigned int thread_id = ::rocprim::detail::block_thread_id<0>();
# pragma unroll
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
{
int idx = BLOCK_THREADS * ITEM + thread_id;
output.emplace(idx, input[ITEM]);
}
}
template <class OutputIt, class T, class SharedIt>
void THRUST_HIP_DEVICE_FUNCTION scatter(
OutputIt output,
T (&input)[ITEMS_PER_THREAD],
SharedIt shared,
int active_mask,
Size thread_output_prefix,
Size tile_output_prefix,
int tile_output_count)
{
int local_scatter_idx = thread_output_prefix - tile_output_prefix;
# pragma unroll
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
{
if (active_mask & (1 << ITEM))
{
shared[local_scatter_idx++] = input[ITEM];
}
}
::rocprim::syncthreads();
const unsigned int thread_id = ::rocprim::detail::block_thread_id<0>();
for (int item = thread_id; item < tile_output_count; item += BLOCK_THREADS)
{
output[tile_output_prefix + item] = shared[item];
}
}
int THRUST_HIP_DEVICE_FUNCTION serial_set_op(
key_type* keys,
int keys1_beg,
int keys2_beg,
int keys1_count,
int keys2_count,
key_type (&output)[ITEMS_PER_THREAD],
int (&indices)[ITEMS_PER_THREAD],
CompareOp compare_op,
SetOp set_op)
{
int active_mask = set_op(keys, keys1_beg, keys2_beg, keys1_count, keys2_count, output, indices, compare_op);
return active_mask;
}
//---------------------------------------------------------------------
// Tile operations
//---------------------------------------------------------------------
template <bool IS_LAST_TILE, class LookBackScanState>
void THRUST_HIP_DEVICE_FUNCTION consume_tile(
Size tile_idx,
LookBackScanState& lookback_scan_state,
KeysIt1 keys1_in,
KeysIt2 keys2_in,
ValuesIt1 values1_in,
ValuesIt2 values2_in,
KeysOutputIt keys_out,
ValuesOutputIt values_out,
CompareOp compare_op,
SetOp set_op,
pair<Size, Size>* partitions,
Size* output_count)
{
using block_scan_type = ::rocprim::block_scan<Size, BLOCK_THREADS>;
using offset_scan_prefix_op_type = ::rocprim::detail::offset_lookback_scan_prefix_op<Size, LookBackScanState>;
ROCPRIM_SHARED_MEMORY union
{
struct
{
typename block_scan_type::storage_type scan;
typename offset_scan_prefix_op_type::storage_type prefix_op;
};
struct
{
int offset[BLOCK_THREADS];
union
{
// Allocate extra shmem than truly necessary
// This will permit to avoid range checks in
// serial set operations, e.g. serial_set_difference
::rocprim::uninitialized_array<key_type, BLOCK_THREADS + ITEMS_PER_THREAD * BLOCK_THREADS> keys_shared;
::rocprim::uninitialized_array<value_type, BLOCK_THREADS + ITEMS_PER_THREAD * BLOCK_THREADS> values_shared;
};
};
} storage;
pair<Size, Size> partition_beg = partitions[tile_idx + 0];
pair<Size, Size> partition_end = partitions[tile_idx + 1];
Size keys1_beg = partition_beg.first;
Size keys1_end = partition_end.first;
Size keys2_beg = partition_beg.second;
Size keys2_end = partition_end.second;
// number of keys per tile
//
int num_keys1 = static_cast<int>(keys1_end - keys1_beg);
int num_keys2 = static_cast<int>(keys2_end - keys2_beg);
// load keys into shared memory for further processing
key_type keys_loc[ITEMS_PER_THREAD];
gmem_to_reg<!IS_LAST_TILE>(keys_loc, keys1_in + keys1_beg, keys2_in + keys2_beg, num_keys1, num_keys2);
reg_to_shared(storage.keys_shared, keys_loc);
::rocprim::syncthreads();
int diag_loc = min<int>(ITEMS_PER_THREAD * threadIdx.x, num_keys1 + num_keys2);
pair<int, int> partition_loc = balanced_path(
&storage.keys_shared.get_unsafe_array()[0],
&storage.keys_shared.get_unsafe_array()[num_keys1],
num_keys1,
num_keys2,
diag_loc,
4,
compare_op);
int keys1_beg_loc = partition_loc.first;
int keys2_beg_loc = partition_loc.second;
// compute difference between next and current thread
// to obtain number of elements per thread
int value = threadIdx.x == 0 ? (num_keys1 << 16) | num_keys2 : (partition_loc.first << 16) | partition_loc.second;
int dst = threadIdx.x == 0 ? BLOCK_THREADS - 1 : threadIdx.x - 1;
storage.offset[dst] = value;
::rocprim::syncthreads();
pair<int, int> partition1_loc =
thrust::make_pair(storage.offset[threadIdx.x] >> 16, storage.offset[threadIdx.x] & 0xFFFF);
int keys1_end_loc = partition1_loc.first;
int keys2_end_loc = partition1_loc.second;
int num_keys1_loc = keys1_end_loc - keys1_beg_loc;
int num_keys2_loc = keys2_end_loc - keys2_beg_loc;
// perform serial set operation
//
int indices[ITEMS_PER_THREAD];
int active_mask = serial_set_op(
&storage.keys_shared.get_unsafe_array()[0],
keys1_beg_loc,
keys2_beg_loc + num_keys1,
num_keys1_loc,
num_keys2_loc,
keys_loc,
indices,
compare_op,
set_op);
::rocprim::syncthreads();
# if 0
if (ITEMS_PER_THREAD*threadIdx.x >= num_keys1 + num_keys2)
active_mask = 0;
# endif
// look-back scan over thread_output_count
// to compute global thread_output_base and tile_otput_count;
Size tile_output_count = 0;
Size thread_output_prefix = 0;
Size tile_output_prefix = 0;
Size thread_output_count = static_cast<Size>(__popc(active_mask));
if (tile_idx == 0) // first tile
{
block_scan_type().exclusive_scan(
thread_output_count, thread_output_prefix, Size(0), tile_output_count, storage.scan, ::rocprim::plus<Size>());
if (threadIdx.x == 0)
{
// Update tile status if this is not the last tile
if (!IS_LAST_TILE)
{
lookback_scan_state.set_complete(0, tile_output_count);
}
}
}
else
{
auto prefix_op = offset_scan_prefix_op_type(tile_idx, lookback_scan_state, storage.prefix_op);
block_scan_type().exclusive_scan(
thread_output_count, thread_output_prefix, storage.scan, prefix_op, ::rocprim::plus<Size>());
::rocprim::syncthreads();
tile_output_count = prefix_op.get_reduction();
tile_output_prefix = prefix_op.get_exclusive_prefix();
}
::rocprim::syncthreads();
// scatter results
//
scatter(keys_out,
keys_loc,
&storage.keys_shared.get_unsafe_array()[0],
active_mask,
thread_output_prefix,
tile_output_prefix,
tile_output_count);
if (HAS_VALUES)
{
value_type values_loc[ITEMS_PER_THREAD];
gmem_to_reg<!IS_LAST_TILE>(values_loc, values1_in + keys1_beg, values2_in + keys2_beg, num_keys1, num_keys2);
::rocprim::syncthreads();
reg_to_shared(storage.values_shared, values_loc);
::rocprim::syncthreads();
// gather items from shared mem
//
# pragma unroll
for (int ITEM = 0; ITEM < ITEMS_PER_THREAD; ++ITEM)
{
if (active_mask & (1 << ITEM))
{
values_loc[ITEM] = storage.values_shared.get_unsafe_array()[indices[ITEM]];
}
}
::rocprim::syncthreads();
scatter(values_out,
values_loc,
&storage.values_shared.get_unsafe_array()[0],
active_mask,
thread_output_prefix,
tile_output_prefix,
tile_output_count);
}
if (IS_LAST_TILE && threadIdx.x == 0)
{
*output_count = tile_output_prefix + tile_output_count;
}
}
};
//---------------------------------------------------------------------
// Serial set operations
//---------------------------------------------------------------------
// serial_set_intersection
// -----------------------
// emit A if A and B are in range and equal.
struct serial_set_intersection
{
// max_input_size <= 32
template <class T, class CompareOp, int ITEMS_PER_THREAD>
int THRUST_HIP_DEVICE_FUNCTION operator()(
T* keys,
int keys1_beg,
int keys2_beg,
int keys1_count,
int keys2_count,
T (&output)[ITEMS_PER_THREAD],
int (&indices)[ITEMS_PER_THREAD],
CompareOp compare_op)
{
int active_mask = 0;
int aBegin = keys1_beg;
int bBegin = keys2_beg;
int aEnd = keys1_beg + keys1_count;
int bEnd = keys2_beg + keys2_count;
T aKey = keys[aBegin];
T bKey = keys[bBegin];
# pragma unroll
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
{
bool pA = compare_op(aKey, bKey);
bool pB = compare_op(bKey, aKey);
// The outputs must come from A by definition of set intersection.
output[i] = aKey;
indices[i] = aBegin;
if ((aBegin < aEnd) && (bBegin < bEnd) && pA == pB)
{
active_mask |= 1 << i;
}
if (!pB)
{
aKey = keys[++aBegin];
}
if (!pA)
{
bKey = keys[++bBegin];
}
}
return active_mask;
}
}; // struct serial_set_intersection
// serial_set_symmetric_difference
// ---------------------
// emit A if A < B and emit B if B < A.
struct serial_set_symmetric_difference
{
// max_input_size <= 32
template <class T, class CompareOp, int ITEMS_PER_THREAD>
int THRUST_HIP_DEVICE_FUNCTION operator()(
T* keys,
int keys1_beg,
int keys2_beg,
int keys1_count,
int keys2_count,
T (&output)[ITEMS_PER_THREAD],
int (&indices)[ITEMS_PER_THREAD],
CompareOp compare_op)
{
int active_mask = 0;
int aBegin = keys1_beg;
int bBegin = keys2_beg;
int aEnd = keys1_beg + keys1_count;
int bEnd = keys2_beg + keys2_count;
int end = aEnd + bEnd;
T aKey = keys[aBegin];
T bKey = keys[bBegin];
# pragma unroll
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
{
bool pB = aBegin >= aEnd;
bool pA = !pB && bBegin >= bEnd;
if (!pA && !pB)
{
pA = compare_op(aKey, bKey);
pB = !pA && compare_op(bKey, aKey);
}
// The outputs must come from A by definition of set difference.
output[i] = pA ? aKey : bKey;
indices[i] = pA ? aBegin : bBegin;
if (aBegin + bBegin < end && pA != pB)
{
active_mask |= 1 << i;
}
if (!pB)
{
aKey = keys[++aBegin];
}
if (!pA)
{
bKey = keys[++bBegin];
}
}
return active_mask;
}
}; // struct set_symmetric_difference
// serial_set_difference
// ---------------------
// emit A if A < B
struct serial_set_difference
{
// max_input_size <= 32
template <class T, class CompareOp, int ITEMS_PER_THREAD>
int THRUST_HIP_DEVICE_FUNCTION operator()(
T* keys,
int keys1_beg,
int keys2_beg,
int keys1_count,
int keys2_count,
T (&output)[ITEMS_PER_THREAD],
int (&indices)[ITEMS_PER_THREAD],
CompareOp compare_op)
{
int active_mask = 0;
int aBegin = keys1_beg;
int bBegin = keys2_beg;
int aEnd = keys1_beg + keys1_count;
int bEnd = keys2_beg + keys2_count;
int end = aEnd + bEnd;
T aKey = keys[aBegin];
T bKey = keys[bBegin];
# pragma unroll
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
{
bool pB = aBegin >= aEnd;
bool pA = !pB && bBegin >= bEnd;
if (!pA && !pB)
{
pA = compare_op(aKey, bKey);
pB = !pA && compare_op(bKey, aKey);
}
// The outputs must come from A by definition of set difference.
output[i] = aKey;
indices[i] = aBegin;
if (aBegin + bBegin < end && pA)
{
active_mask |= 1 << i;
}
if (!pB)
{
aKey = keys[++aBegin];
}
if (!pA)
{
bKey = keys[++bBegin];
}
}
return active_mask;
}
}; // struct set_difference
// serial_set_union
// ----------------
// emit A if A <= B else emit B
struct serial_set_union
{
// max_input_size <= 32
template <class T, class CompareOp, int ITEMS_PER_THREAD>
int THRUST_HIP_DEVICE_FUNCTION operator()(
T* keys,
int keys1_beg,
int keys2_beg,
int keys1_count,
int keys2_count,
T (&output)[ITEMS_PER_THREAD],
int (&indices)[ITEMS_PER_THREAD],
CompareOp compare_op)
{
int active_mask = 0;
int aBegin = keys1_beg;
int bBegin = keys2_beg;
int aEnd = keys1_beg + keys1_count;
int bEnd = keys2_beg + keys2_count;
int end = aEnd + bEnd;
T aKey = keys[aBegin];
T bKey = keys[bBegin];
# pragma unroll
for (int i = 0; i < ITEMS_PER_THREAD; ++i)
{
bool pB = aBegin >= aEnd;
bool pA = !pB && bBegin >= bEnd;
if (!pA && !pB)
{
pA = compare_op(aKey, bKey);
pB = !pA && compare_op(bKey, aKey);
}
// Output A in case of a tie, so check if b < a.
output[i] = pB ? bKey : aKey;
indices[i] = pB ? bBegin : aBegin;
if (aBegin + bBegin < end)
{
active_mask |= 1 << i;
}
if (!pB)
{
aKey = keys[++aBegin];
}
if (!pA)
{
bKey = keys[++bBegin];
}
}
return active_mask;
}
}; // struct set_union
template <class Config,
bool HAS_VALUES,
class KeysIt1,
class KeysIt2,
class ValuesIt1,
class ValuesIt2,
class Size,
class KeysOutputIt,
class ValuesOutputIt,
class CompareOp,
class SetOp,
class LookBackScanState>
ROCPRIM_KERNEL THRUST_HIP_LAUNCH_BOUNDS_DEFAULT void lookback_set_op_kernel(
KeysIt1 keys1,
KeysIt2 keys2,
ValuesIt1 values1,
ValuesIt2 values2,
KeysOutputIt keys_output,
ValuesOutputIt values_output,
CompareOp compare_op,
SetOp set_op,
pair<Size, Size>* partitions,
Size* output_count,
LookBackScanState lookback_scan_state,
rocprim::detail::ordered_block_id<unsigned int> ordered_bid)
{
ROCPRIM_SHARED_MEMORY
typename rocprim::detail::ordered_block_id<unsigned int>::storage_type storage_ordered_bid;
const int num_tiles = gridDim.x;
const int tile_idx = ordered_bid.get(::rocprim::flat_block_thread_id(), storage_ordered_bid);
SetOpAgent<Config, KeysIt1, KeysIt2, ValuesIt1, ValuesIt2, KeysOutputIt, ValuesOutputIt, Size, CompareOp, SetOp, HAS_VALUES>
agent;
if (tile_idx < num_tiles - 1)
{
agent.template consume_tile<false>(
tile_idx,
lookback_scan_state,
keys1,
keys2,
values1,
values2,
keys_output,
values_output,
compare_op,
set_op,
partitions,
output_count);
}
else
{
agent.template consume_tile<true>(
tile_idx,
lookback_scan_state,
keys1,
keys2,
values1,
values2,
keys_output,
values_output,
compare_op,
set_op,
partitions,
output_count);
}
}
# define ROCTHRUST_HIP_SYNC_AND_RETURN_ON_ERROR(name, size, start) \
{ \
auto error = hipPeekAtLastError(); \
if (error != hipSuccess) \
return error; \
if (debug_synchronous) \
{ \
std::cout << name << "(" << size << ")"; \
auto error = hipStreamSynchronize(stream); \
if (error != hipSuccess) \
return error; \
auto end = std::chrono::high_resolution_clock::now(); \
auto d = std::chrono::duration_cast<std::chrono::duration<double>>(end - start); \
std::cout << " " << d.count() * 1000 << " ms" << '\n'; \
} \
}
template <bool HAS_VALUES,
class KeysIt1,
class KeysIt2,
class ValuesIt1,
class ValuesIt2,
class Size,
class KeysOutputIt,
class ValuesOutputIt,
class CompareOp,
class SetOp>
hipError_t THRUST_HIP_RUNTIME_FUNCTION doit_step(
void* d_temp_storage,
size_t& temp_storage_size,
KeysIt1 keys1,
KeysIt2 keys2,
ValuesIt1 values1,
ValuesIt2 values2,
Size num_keys1,
Size num_keys2,
KeysOutputIt keys_output,
ValuesOutputIt values_output,
Size* output_count,
CompareOp compare_op,
SetOp set_op,
hipStream_t stream,
bool debug_synchronous)
{
using key_type = typename std::iterator_traits<KeysIt1>::value_type;
using value_type = typename std::iterator_traits<ValuesIt1>::value_type;
using config = typename set_operations_config_900<key_type, value_type>::type;
using block_state_type = ::rocprim::detail::lookback_scan_state<Size>;
using ordered_block_id_type = ::rocprim::detail::ordered_block_id<unsigned int>;
constexpr unsigned int block_size = config::block_size;
constexpr unsigned int items_per_thread = config::items_per_thread;
constexpr unsigned int items_per_block = block_size * items_per_thread - 1;
Size keys_total = num_keys1 + num_keys2;
if (keys_total == 0)
{
return hipErrorInvalidValue;
}
hipError_t status = hipSuccess;
const unsigned int number_of_blocks = (keys_total + items_per_block - 1) / items_per_block;
// Calculate required temporary storage
size_t scan_state_bytes;
status = block_state_type::get_storage_size(number_of_blocks, stream, scan_state_bytes);
if (status != hipSuccess)
{
return status;
}
scan_state_bytes = ::rocprim::detail::align_size(scan_state_bytes);
size_t ordered_block_id_bytes = ::rocprim::detail::align_size(ordered_block_id_type::get_storage_size());
size_t partition_storage_bytes = (number_of_blocks + 1) * sizeof(pair<Size, Size>);
if (d_temp_storage == nullptr)
{
// temp_storage_size is never zero
temp_storage_size = scan_state_bytes + ordered_block_id_bytes + partition_storage_bytes;
return hipSuccess;
}
// Start point for time measurements
std::chrono::high_resolution_clock::time_point start;
if (debug_synchronous)
{
std::cout << "keys_total " << keys_total << '\n';
std::cout << "number_of_blocks " << number_of_blocks << '\n';
std::cout << "block_size " << block_size << '\n';
std::cout << "items_per_thread " << items_per_thread << '\n';
std::cout << "items_per_block " << items_per_block << '\n';
}
auto ptr = reinterpret_cast<char*>(d_temp_storage);
// Create and initialize lookback_scan_state obj
block_state_type blocks_state;
status = block_state_type::create(blocks_state, ptr, number_of_blocks, stream);
if (status != hipSuccess)
{
return status;
}
ptr += scan_state_bytes;
// Create and initialize ordered_block_id obj
auto ordered_bid = ordered_block_id_type::create(reinterpret_cast<ordered_block_id_type::id_type*>(ptr));
ptr += ordered_block_id_bytes;
pair<Size, Size>* partitions = reinterpret_cast<pair<Size, Size>*>(ptr);
if (debug_synchronous)
{
start = std::chrono::high_resolution_clock::now();
}
auto grid_size = (number_of_blocks + block_size - 1) / block_size;
hipLaunchKernelGGL(
HIP_KERNEL_NAME(rocprim::detail::init_lookback_scan_state_kernel),
dim3(grid_size),
dim3(block_size),
0,
stream,
blocks_state,
number_of_blocks,
ordered_bid);
ROCTHRUST_HIP_SYNC_AND_RETURN_ON_ERROR("init_lookback_scan_state_kernel", number_of_blocks, start)
status = __parallel_for::parallel_for(
number_of_blocks + 1,
[=] THRUST_DEVICE(Size idx) mutable {
Size partition_at = min<Size>(idx * items_per_block, num_keys1 + num_keys2);
partitions[idx] = balanced_path(keys1, keys2, num_keys1, num_keys2, partition_at, 4ll, compare_op);
},
stream);
if (status != hipSuccess)
{
return status;
}
if (debug_synchronous)
{
start = std::chrono::high_resolution_clock::now();
}
hipLaunchKernelGGL(
HIP_KERNEL_NAME(lookback_set_op_kernel<config, HAS_VALUES>),
dim3(number_of_blocks),
dim3(block_size),
0,
stream,
keys1,
keys2,
values1,
values2,
keys_output,
values_output,
compare_op,
set_op,
partitions,
output_count,
blocks_state,
ordered_bid);
ROCTHRUST_HIP_SYNC_AND_RETURN_ON_ERROR("lookback_set_op_kernel", keys_total, start)
return status;
}
# undef ROCTHRUST_HIP_SYNC_AND_RETURN_ON_ERROR
template <bool HAS_VALUES,
typename Derived,
typename KeysIt1,
typename KeysIt2,
typename ValuesIt1,
typename ValuesIt2,
typename KeysOutputIt,
typename ValuesOutputIt,
typename CompareOp,
typename SetOp>
THRUST_HIP_RUNTIME_FUNCTION pair<KeysOutputIt, ValuesOutputIt> set_operations(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ValuesIt1 values1_first,
ValuesIt2 values2_first,
KeysOutputIt keys_output,
ValuesOutputIt values_output,
CompareOp compare_op,
SetOp set_op)
{
using namespace thrust::system::hip_rocprim::temp_storage;
using size_type = typename iterator_traits<KeysIt1>::difference_type;
size_type num_keys1 = static_cast<size_type>(thrust::distance(keys1_first, keys1_last));
size_type num_keys2 = static_cast<size_type>(thrust::distance(keys2_first, keys2_last));
if (num_keys1 + num_keys2 == 0)
{
return thrust::make_pair(keys_output, values_output);
}
size_t temp_storage_bytes = 0;
hipStream_t stream = hip_rocprim::stream(policy);
bool debug_sync = THRUST_HIP_DEBUG_SYNC_FLAG;
hip_rocprim::throw_on_error(
doit_step<HAS_VALUES>(
nullptr,
temp_storage_bytes,
keys1_first,
keys2_first,
values1_first,
values2_first,
num_keys1,
num_keys2,
keys_output,
values_output,
static_cast<size_type*>(nullptr),
compare_op,
set_op,
stream,
debug_sync),
"set_operations failed on 1st step");
temp_storage_bytes = rocprim::detail::align_size(temp_storage_bytes);
size_t storage_size = 0;
void* ptr = nullptr;
void* temp_stor = nullptr;
size_type* d_output_count;
auto l_part =
make_linear_partition(make_partition(&temp_stor, temp_storage_bytes), ptr_aligned_array(&d_output_count, 1));
// Calculate storage_size including alignment
hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part));
// Allocate temporary storage.
thrust::detail::temporary_array<std::uint8_t, Derived> tmp(policy, storage_size);
ptr = static_cast<void*>(tmp.data().get());
// Create pointers with alignment
hip_rocprim::throw_on_error(partition(ptr, storage_size, l_part));
hip_rocprim::throw_on_error(
doit_step<HAS_VALUES>(
ptr,
temp_storage_bytes,
keys1_first,
keys2_first,
values1_first,
values2_first,
num_keys1,
num_keys2,
keys_output,
values_output,
d_output_count,
compare_op,
set_op,
stream,
debug_sync),
"set_operations failed on 2nd step");
std::size_t output_count = hip_rocprim::get_value(policy, d_output_count);
return thrust::make_pair(keys_output + output_count, values_output + output_count);
}
} // namespace __set_operations
//-------------------------
// Thrust API entry points
//-------------------------
THRUST_EXEC_CHECK_DISABLE
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt, class CompareOp>
OutputIt THRUST_HOST_DEVICE set_difference(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result,
CompareOp compare)
{
using items1_t = thrust::iterator_value_t<ItemsIt1>;
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
false,
Derived,
ItemsIt1,
ItemsIt2,
items1_t*,
items1_t*,
OutputIt,
items1_t*,
CompareOp,
__set_operations::serial_set_difference>) );
# if __THRUST_HAS_HIPRT__
items1_t* null_ = nullptr;
auto tmp = __set_operations::set_operations<false>(
policy,
items1_first,
items1_last,
items2_first,
items2_last,
null_,
null_,
result,
null_,
compare,
__set_operations::serial_set_difference());
result = tmp.first;
# else
result = thrust::set_difference(
cvt_to_seq(derived_cast(policy)), items1_first, items1_last, items2_first, items2_last, result, compare);
# endif
return result;
}
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt>
OutputIt THRUST_HOST_DEVICE set_difference(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result)
{
using value_type = typename thrust::iterator_value<ItemsIt1>::type;
return hip_rocprim::set_difference(
policy, items1_first, items1_last, items2_first, items2_last, result, less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt, class CompareOp>
OutputIt THRUST_HOST_DEVICE set_intersection(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result,
CompareOp compare)
{
using items1_t = thrust::iterator_value_t<ItemsIt1>;
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
false,
Derived,
ItemsIt1,
ItemsIt2,
items1_t*,
items1_t*,
OutputIt,
items1_t*,
CompareOp,
__set_operations::serial_set_intersection>) );
# if __THRUST_HAS_HIPRT__
items1_t* null_ = nullptr;
auto tmp = __set_operations::set_operations<false>(
policy,
items1_first,
items1_last,
items2_first,
items2_last,
null_,
null_,
result,
null_,
compare,
__set_operations::serial_set_intersection());
result = tmp.first;
# else
result = thrust::set_intersection(
cvt_to_seq(derived_cast(policy)), items1_first, items1_last, items2_first, items2_last, result, compare);
# endif
return result;
}
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt>
OutputIt THRUST_HOST_DEVICE set_intersection(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result)
{
using value_type = typename thrust::iterator_value<ItemsIt1>::type;
return hip_rocprim::set_intersection(
policy, items1_first, items1_last, items2_first, items2_last, result, less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt, class CompareOp>
OutputIt THRUST_HOST_DEVICE set_symmetric_difference(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result,
CompareOp compare)
{
using items1_t = thrust::iterator_value_t<ItemsIt1>;
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
false,
Derived,
ItemsIt1,
ItemsIt2,
items1_t*,
items1_t*,
OutputIt,
items1_t*,
CompareOp,
__set_operations::serial_set_symmetric_difference>) );
# if __THRUST_HAS_HIPRT__
items1_t* null_ = nullptr;
auto tmp = __set_operations::set_operations<false>(
policy,
items1_first,
items1_last,
items2_first,
items2_last,
null_,
null_,
result,
null_,
compare,
__set_operations::serial_set_symmetric_difference());
result = tmp.first;
# else
result = thrust::set_symmetric_difference(
cvt_to_seq(derived_cast(policy)), items1_first, items1_last, items2_first, items2_last, result, compare);
# endif
return result;
}
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt>
OutputIt THRUST_HOST_DEVICE set_symmetric_difference(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result)
{
using value_type = typename thrust::iterator_value<ItemsIt1>::type;
return hip_rocprim::set_symmetric_difference(
policy, items1_first, items1_last, items2_first, items2_last, result, less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt, class CompareOp>
OutputIt THRUST_HOST_DEVICE set_union(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result,
CompareOp compare)
{
using items1_t = thrust::iterator_value_t<ItemsIt1>;
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
false,
Derived,
ItemsIt1,
ItemsIt2,
items1_t*,
items1_t*,
OutputIt,
items1_t*,
CompareOp,
__set_operations::serial_set_union>) );
# if __THRUST_HAS_HIPRT__
items1_t* null_ = nullptr;
auto tmp = __set_operations::set_operations<false>(
policy,
items1_first,
items1_last,
items2_first,
items2_last,
null_,
null_,
result,
null_,
compare,
__set_operations::serial_set_union());
result = tmp.first;
# else
result = thrust::set_union(
cvt_to_seq(derived_cast(policy)), items1_first, items1_last, items2_first, items2_last, result, compare);
# endif
return result;
}
template <class Derived, class ItemsIt1, class ItemsIt2, class OutputIt>
OutputIt THRUST_HOST_DEVICE set_union(
execution_policy<Derived>& policy,
ItemsIt1 items1_first,
ItemsIt1 items1_last,
ItemsIt2 items2_first,
ItemsIt2 items2_last,
OutputIt result)
{
using value_type = typename thrust::iterator_value<ItemsIt1>::type;
return hip_rocprim::set_union(
policy, items1_first, items1_last, items2_first, items2_last, result, less<value_type>());
}
/*****************************/
/*****************************/
/***** *_by_key *****/
/*****************************/
/*****************************/
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived,
class KeysIt1,
class KeysIt2,
class ItemsIt1,
class ItemsIt2,
class KeysOutputIt,
class ItemsOutputIt,
class CompareOp>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_difference_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result,
CompareOp compare_op)
{
auto ret = thrust::make_pair(keys_result, items_result);
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
true,
Derived,
KeysIt1,
KeysIt2,
ItemsIt1,
ItemsIt2,
KeysOutputIt,
ItemsOutputIt,
CompareOp,
__set_operations::serial_set_difference>) );
# if __THRUST_HAS_HIPRT__
ret = __set_operations::set_operations<true>(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op,
__set_operations::serial_set_difference());
# else
ret = thrust::set_difference_by_key(
cvt_to_seq(derived_cast(policy)),
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op);
# endif
return ret;
}
template <class Derived, class KeysIt1, class KeysIt2, class ItemsIt1, class ItemsIt2, class KeysOutputIt, class ItemsOutputIt>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_difference_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result)
{
using value_type = typename thrust::iterator_value<KeysIt1>::type;
return hip_rocprim::set_difference_by_key(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived,
class KeysIt1,
class KeysIt2,
class ItemsIt1,
class ItemsIt2,
class KeysOutputIt,
class ItemsOutputIt,
class CompareOp>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_intersection_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result,
CompareOp compare_op)
{
auto ret = thrust::make_pair(keys_result, items_result);
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
true,
Derived,
KeysIt1,
KeysIt2,
ItemsIt1,
ItemsIt1,
KeysOutputIt,
ItemsOutputIt,
CompareOp,
__set_operations::serial_set_intersection>) );
# if __THRUST_HAS_HIPRT__
ret = __set_operations::set_operations<true>(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items1_first,
keys_result,
items_result,
compare_op,
__set_operations::serial_set_intersection());
# else
ret = thrust::set_intersection_by_key(
cvt_to_seq(derived_cast(policy)),
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
keys_result,
items_result,
compare_op);
# endif
return ret;
}
template <class Derived, class KeysIt1, class KeysIt2, class ItemsIt1, class ItemsIt2, class KeysOutputIt, class ItemsOutputIt>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_intersection_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result)
{
using value_type = typename thrust::iterator_value<KeysIt1>::type;
return hip_rocprim::set_intersection_by_key(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
keys_result,
items_result,
less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived,
class KeysIt1,
class KeysIt2,
class ItemsIt1,
class ItemsIt2,
class KeysOutputIt,
class ItemsOutputIt,
class CompareOp>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_symmetric_difference_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result,
CompareOp compare_op)
{
auto ret = thrust::make_pair(keys_result, items_result);
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
true,
Derived,
KeysIt1,
KeysIt2,
ItemsIt1,
ItemsIt2,
KeysOutputIt,
ItemsOutputIt,
CompareOp,
__set_operations::serial_set_symmetric_difference>) );
# if __THRUST_HAS_HIPRT__
ret = __set_operations::set_operations<true>(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op,
__set_operations::serial_set_symmetric_difference());
# else
ret = thrust::set_symmetric_difference_by_key(
cvt_to_seq(derived_cast(policy)),
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op);
# endif
return ret;
}
template <class Derived, class KeysIt1, class KeysIt2, class ItemsIt1, class ItemsIt2, class KeysOutputIt, class ItemsOutputIt>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_symmetric_difference_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result)
{
using value_type = typename thrust::iterator_value<KeysIt1>::type;
return hip_rocprim::set_symmetric_difference_by_key(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
less<value_type>());
}
/*****************************/
THRUST_EXEC_CHECK_DISABLE
template <class Derived,
class KeysIt1,
class KeysIt2,
class ItemsIt1,
class ItemsIt2,
class KeysOutputIt,
class ItemsOutputIt,
class CompareOp>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_union_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result,
CompareOp compare_op)
{
auto ret = thrust::make_pair(keys_result, items_result);
THRUST_HIP_PRESERVE_KERNELS_WORKAROUND(
(__set_operations::set_operations<
true,
Derived,
KeysIt1,
KeysIt2,
ItemsIt1,
ItemsIt2,
KeysOutputIt,
ItemsOutputIt,
CompareOp,
__set_operations::serial_set_union>) );
# if __THRUST_HAS_HIPRT__
ret = __set_operations::set_operations<true>(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op,
__set_operations::serial_set_union());
# else
ret = thrust::set_union_by_key(
cvt_to_seq(derived_cast(policy)),
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
compare_op);
# endif
return ret;
}
template <class Derived, class KeysIt1, class KeysIt2, class ItemsIt1, class ItemsIt2, class KeysOutputIt, class ItemsOutputIt>
pair<KeysOutputIt, ItemsOutputIt> THRUST_HOST_DEVICE set_union_by_key(
execution_policy<Derived>& policy,
KeysIt1 keys1_first,
KeysIt1 keys1_last,
KeysIt2 keys2_first,
KeysIt2 keys2_last,
ItemsIt1 items1_first,
ItemsIt2 items2_first,
KeysOutputIt keys_result,
ItemsOutputIt items_result)
{
using value_type = typename thrust::iterator_value<KeysIt1>::type;
return hip_rocprim::set_union_by_key(
policy,
keys1_first,
keys1_last,
keys2_first,
keys2_last,
items1_first,
items2_first,
keys_result,
items_result,
less<value_type>());
}
} // namespace hip_rocprim
THRUST_NAMESPACE_END
#endif