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
1545 lines
37 KiB
C++
1545 lines
37 KiB
C++
// SPDX-FileCopyrightText: Copyright (c) 2011, Duane Merrill. All rights reserved.
|
|
// SPDX-FileCopyrightText: Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.
|
|
// SPDX-License-Identifier: BSD-3
|
|
|
|
#pragma once
|
|
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
# undef small // Windows is terrible for polluting macro namespace
|
|
#else
|
|
# include <sys/resource.h>
|
|
#endif
|
|
|
|
#include <cub/util_debug.cuh>
|
|
#include <cub/util_device.cuh>
|
|
#include <cub/util_macro.cuh>
|
|
#include <cub/util_math.cuh>
|
|
#include <cub/util_namespace.cuh>
|
|
#include <cub/util_ptx.cuh>
|
|
#include <cub/util_type.cuh>
|
|
|
|
#include <thrust/iterator/discard_iterator.h>
|
|
|
|
#include <cuda/std/algorithm>
|
|
|
|
#include <nv/target>
|
|
|
|
#include <cfloat>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "mersenne.h"
|
|
#include <c2h/catch2_test_helper.h>
|
|
#include <c2h/extended_types.h>
|
|
#include <c2h/test_util_vec.h>
|
|
|
|
/******************************************************************************
|
|
* Assertion macros
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Assert equals
|
|
*/
|
|
#define AssertEquals(a, b) \
|
|
if ((a) != (b)) \
|
|
{ \
|
|
std::cerr << "\n" << __FILE__ << ": " << __LINE__ << ": AssertEquals(" #a ", " #b ") failed.\n"; \
|
|
exit(1); \
|
|
}
|
|
|
|
#define AssertTrue(a) \
|
|
if (!(a)) \
|
|
{ \
|
|
std::cerr << "\n" << __FILE__ << ": " << __LINE__ << ": AssertTrue(" #a ") failed.\n"; \
|
|
exit(1); \
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Command-line parsing functionality
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Utility for parsing command line arguments
|
|
*/
|
|
struct CommandLineArgs
|
|
{
|
|
std::vector<std::string> keys;
|
|
std::vector<std::string> values;
|
|
std::vector<std::string> args;
|
|
cudaDeviceProp deviceProp;
|
|
float device_giga_bandwidth;
|
|
std::size_t device_free_physmem;
|
|
std::size_t device_total_physmem;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
CommandLineArgs(int argc, char** argv)
|
|
: keys(10)
|
|
, values(10)
|
|
{
|
|
using namespace std;
|
|
|
|
// Initialize mersenne generator
|
|
unsigned int mersenne_init[4] = {0x123, 0x234, 0x345, 0x456};
|
|
mersenne::init_by_array(mersenne_init, 4);
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
string arg = argv[i];
|
|
|
|
if ((arg[0] != '-') || (arg[1] != '-'))
|
|
{
|
|
args.push_back(arg);
|
|
continue;
|
|
}
|
|
|
|
string key, val;
|
|
string::size_type pos = arg.find('=');
|
|
if (pos == string::npos)
|
|
{
|
|
key = string(arg, 2, arg.length() - 2);
|
|
val = "";
|
|
}
|
|
else
|
|
{
|
|
key = string(arg, 2, pos - 2);
|
|
val = string(arg, pos + 1, arg.length() - 1);
|
|
}
|
|
|
|
keys.push_back(key);
|
|
values.push_back(val);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether a flag "--<flag>" is present in the commandline
|
|
*/
|
|
bool CheckCmdLineFlag(const char* arg_name)
|
|
{
|
|
using namespace std;
|
|
|
|
for (const auto& key : keys)
|
|
{
|
|
if (key == string(arg_name))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns number of naked (non-flag and non-key-value) commandline parameters
|
|
*/
|
|
template <typename T>
|
|
int NumNakedArgs()
|
|
{
|
|
return static_cast<int>(args.size());
|
|
}
|
|
|
|
/**
|
|
* Returns the commandline parameter for a given index (not including flags)
|
|
*/
|
|
template <typename T>
|
|
void GetCmdLineArgument(std::size_t index, T& val)
|
|
{
|
|
using namespace std;
|
|
if (index < args.size())
|
|
{
|
|
istringstream str_stream(args[index]);
|
|
str_stream >> val;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the value specified for a given commandline parameter --<flag>=<value>
|
|
*/
|
|
template <typename T>
|
|
void GetCmdLineArgument(const char* arg_name, T& val)
|
|
{
|
|
using namespace std;
|
|
|
|
for (std::size_t i = 0; i < keys.size(); ++i)
|
|
{
|
|
if (keys[i] == string(arg_name))
|
|
{
|
|
istringstream str_stream(values[i]);
|
|
str_stream >> val;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the values specified for a given commandline parameter --<flag>=<value>,<value>*
|
|
*/
|
|
template <typename T>
|
|
void GetCmdLineArguments(const char* arg_name, std::vector<T>& vals)
|
|
{
|
|
using namespace std;
|
|
|
|
if (CheckCmdLineFlag(arg_name))
|
|
{
|
|
// Clear any default values
|
|
vals.clear();
|
|
|
|
// Recover from multi-value string
|
|
for (std::size_t i = 0; i < keys.size(); ++i)
|
|
{
|
|
if (keys[i] == string(arg_name))
|
|
{
|
|
string val_string(values[i]);
|
|
istringstream str_stream(val_string);
|
|
string::size_type old_pos = 0;
|
|
string::size_type new_pos = 0;
|
|
|
|
// Iterate comma-separated values
|
|
T val;
|
|
while ((new_pos = val_string.find(',', old_pos)) != string::npos)
|
|
{
|
|
if (new_pos != old_pos)
|
|
{
|
|
str_stream.width(static_cast<std::streamsize>(new_pos - old_pos));
|
|
str_stream >> val;
|
|
vals.push_back(val);
|
|
}
|
|
|
|
// skip over comma
|
|
str_stream.ignore(1);
|
|
old_pos = new_pos + 1;
|
|
}
|
|
|
|
// Read last value
|
|
str_stream >> val;
|
|
vals.push_back(val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The number of pairs parsed
|
|
*/
|
|
int ParsedArgc()
|
|
{
|
|
return (int) keys.size();
|
|
}
|
|
|
|
/**
|
|
* Initialize device
|
|
*/
|
|
cudaError_t DeviceInit(int dev = -1)
|
|
{
|
|
cudaError_t error = cudaSuccess;
|
|
|
|
do
|
|
{
|
|
int deviceCount;
|
|
error = CubDebug(cudaGetDeviceCount(&deviceCount));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (deviceCount == 0)
|
|
{
|
|
fprintf(stderr, "No devices supporting CUDA.\n");
|
|
exit(1);
|
|
}
|
|
if (dev < 0)
|
|
{
|
|
GetCmdLineArgument("device", dev);
|
|
}
|
|
if ((dev > deviceCount - 1) || (dev < 0))
|
|
{
|
|
dev = 0;
|
|
}
|
|
|
|
error = CubDebug(cudaSetDevice(dev));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
CubDebugExit(cudaMemGetInfo(&device_free_physmem, &device_total_physmem));
|
|
|
|
int ptx_version = 0;
|
|
error = CubDebug(CUB_NS_QUALIFIER::PtxVersion(ptx_version));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
error = CubDebug(cudaGetDeviceProperties(&deviceProp, dev));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (deviceProp.major < 1)
|
|
{
|
|
fprintf(stderr, "Device does not support CUDA.\n");
|
|
exit(1);
|
|
}
|
|
|
|
int memoryClockRate{};
|
|
error = CubDebug(cudaDeviceGetAttribute(&memoryClockRate, cudaDevAttrMemoryClockRate, dev));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
int memoryBusWidth{};
|
|
error = CubDebug(cudaDeviceGetAttribute(&memoryBusWidth, cudaDevAttrGlobalMemoryBusWidth, dev));
|
|
if (error)
|
|
{
|
|
break;
|
|
}
|
|
|
|
device_giga_bandwidth = float(memoryBusWidth) * static_cast<float>(memoryClockRate) * 2 / 8 / 1000 / 1000;
|
|
|
|
if (!CheckCmdLineFlag("quiet"))
|
|
{
|
|
printf(
|
|
"Using device %d: %s (PTX version %d, SM%d, %d SMs, "
|
|
"%lld free / %lld total MB physmem, "
|
|
"%.3f GB/s @ %d kHz mem clock, ECC %s)\n",
|
|
dev,
|
|
deviceProp.name,
|
|
ptx_version,
|
|
deviceProp.major * 100 + deviceProp.minor * 10,
|
|
deviceProp.multiProcessorCount,
|
|
(unsigned long long) device_free_physmem / 1024 / 1024,
|
|
(unsigned long long) device_total_physmem / 1024 / 1024,
|
|
device_giga_bandwidth,
|
|
memoryClockRate,
|
|
(deviceProp.ECCEnabled) ? "on" : "off");
|
|
fflush(stdout);
|
|
}
|
|
|
|
} while (false);
|
|
|
|
return error;
|
|
}
|
|
};
|
|
|
|
// Gets the amount of global memory of the current device.
|
|
inline std::size_t TotalGlobalMem()
|
|
{
|
|
int device = 0;
|
|
CubDebugExit(cudaGetDevice(&device));
|
|
std::size_t free_mem = 0, total_mem = 0;
|
|
CubDebugExit(cudaMemGetInfo(&free_mem, &total_mem));
|
|
return total_mem;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Random bits generator
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Generates random keys.
|
|
*
|
|
* We always take the second-order byte from rand() because the higher-order
|
|
* bits returned by rand() are commonly considered more uniformly distributed
|
|
* than the lower-order bits.
|
|
*
|
|
* We can decrease the entropy level of keys by adopting the technique
|
|
* of Thearling and Smith in which keys are computed from the bitwise AND of
|
|
* multiple random samples:
|
|
*
|
|
* entropy_reduction | Effectively-unique bits per key
|
|
* -----------------------------------------------------
|
|
* -1 | 0
|
|
* 0 | 32
|
|
* 1 | 25.95 (81%)
|
|
* 2 | 17.41 (54%)
|
|
* 3 | 10.78 (34%)
|
|
* 4 | 6.42 (20%)
|
|
* ... | ...
|
|
*
|
|
*/
|
|
template <typename K>
|
|
void RandomBits(K& key, int entropy_reduction = 0, int begin_bit = 0, int end_bit = sizeof(K) * 8)
|
|
{
|
|
constexpr int NUM_BYTES = sizeof(K);
|
|
constexpr int WORD_BYTES = sizeof(unsigned int);
|
|
constexpr int NUM_WORDS = (NUM_BYTES + WORD_BYTES - 1) / WORD_BYTES;
|
|
|
|
unsigned int word_buff[NUM_WORDS];
|
|
|
|
if (entropy_reduction == -1)
|
|
{
|
|
memset((void*) &key, 0, sizeof(key));
|
|
return;
|
|
}
|
|
|
|
if (end_bit < 0)
|
|
{
|
|
end_bit = sizeof(K) * 8;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
// Generate random word_buff
|
|
for (int j = 0; j < NUM_WORDS; j++)
|
|
{
|
|
int current_bit = j * WORD_BYTES * 8;
|
|
|
|
unsigned int word = 0xffffffff;
|
|
word &= 0xffffffff << ::cuda::std::max(0, begin_bit - current_bit);
|
|
word &= 0xffffffff >> ::cuda::std::max(0, (current_bit + (WORD_BYTES * 8)) - end_bit);
|
|
|
|
for (int i = 0; i <= entropy_reduction; i++)
|
|
{
|
|
// Grab some of the higher bits from rand (better entropy, supposedly)
|
|
word &= mersenne::genrand_int32();
|
|
}
|
|
|
|
word_buff[j] = word;
|
|
}
|
|
|
|
memcpy(&key, word_buff, sizeof(K));
|
|
|
|
K copy = key;
|
|
if (!c2h::isnan(copy))
|
|
{
|
|
break; // avoids NaNs when generating random floating point numbers
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Randomly select number between [0:max)
|
|
template <typename T>
|
|
T RandomValue(T max)
|
|
{
|
|
unsigned int bits;
|
|
unsigned int max_int = (unsigned int) -1;
|
|
do
|
|
{
|
|
RandomBits(bits);
|
|
} while (bits == max_int);
|
|
|
|
return (T) ((double(bits) / double(max_int)) * double(max));
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Test value initialization utilities
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Test problem generation options
|
|
*/
|
|
enum GenMode
|
|
{
|
|
UNIFORM, // Assign to '2', regardless of integer seed
|
|
INTEGER_SEED, // Assign to integer seed
|
|
RANDOM, // Assign to random, regardless of integer seed
|
|
RANDOM_BIT, // Assign to randomly chosen 0 or 1, regardless of integer seed
|
|
RANDOM_MINUS_PLUS_ZERO, // Assign to random, with some values being -0.0 or +0.0 patterns
|
|
};
|
|
|
|
/**
|
|
* Initialize value
|
|
*/
|
|
#pragma nv_exec_check_disable
|
|
template <typename T>
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, T& value, std::size_t index = 0)
|
|
{
|
|
// RandomBits is host-only.
|
|
NV_IF_ELSE_TARGET(
|
|
NV_IS_HOST,
|
|
({
|
|
switch (gen_mode)
|
|
{
|
|
case RANDOM:
|
|
RandomBits(value);
|
|
break;
|
|
case RANDOM_BIT: {
|
|
char c;
|
|
RandomBits(c, 0, 0, 1);
|
|
value = static_cast<T>((c > 0) ? 1 : -1);
|
|
break;
|
|
}
|
|
case RANDOM_MINUS_PLUS_ZERO: {
|
|
// Replace roughly 1/128 of values with -0.0 or +0.0, and
|
|
// generate the rest randomly
|
|
using UnsignedBits = typename CUB_NS_QUALIFIER::Traits<T>::UnsignedBits;
|
|
char c;
|
|
RandomBits(c);
|
|
if (c == 0)
|
|
{
|
|
// Replace 1/256 of values with +0.0 bit pattern
|
|
value = c2h::SafeBitCast<T>(UnsignedBits(0));
|
|
}
|
|
else if (c == 1)
|
|
{
|
|
// Replace 1/256 of values with -0.0 bit pattern
|
|
value = c2h::SafeBitCast<T>(UnsignedBits(UnsignedBits(1) << (sizeof(UnsignedBits) * 8) - 1));
|
|
}
|
|
else
|
|
{
|
|
// 127/128 of values are random
|
|
RandomBits(value);
|
|
}
|
|
break;
|
|
}
|
|
case UNIFORM:
|
|
value = 2;
|
|
break;
|
|
case INTEGER_SEED:
|
|
default:
|
|
value = static_cast<T>(index);
|
|
break;
|
|
}
|
|
}),
|
|
({
|
|
switch (gen_mode)
|
|
{
|
|
case RANDOM:
|
|
case RANDOM_BIT:
|
|
case RANDOM_MINUS_PLUS_ZERO:
|
|
_CubLog("%s\n", "cub::InitValue cannot generate random numbers on device.");
|
|
cuda::std::terminate();
|
|
break;
|
|
case UNIFORM:
|
|
value = 2;
|
|
break;
|
|
case INTEGER_SEED:
|
|
default:
|
|
value = static_cast<T>(index);
|
|
break;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Initialize value (bool)
|
|
*/
|
|
#pragma nv_exec_check_disable
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, bool& value, std::size_t index = 0)
|
|
{
|
|
// RandomBits is host-only.
|
|
NV_IF_ELSE_TARGET(
|
|
NV_IS_HOST,
|
|
({
|
|
switch (gen_mode)
|
|
{
|
|
case RANDOM:
|
|
case RANDOM_BIT:
|
|
char c;
|
|
RandomBits(c, 0, 0, 1);
|
|
value = (c > 0);
|
|
break;
|
|
case UNIFORM:
|
|
value = true;
|
|
break;
|
|
case INTEGER_SEED:
|
|
default:
|
|
value = (index > 0);
|
|
break;
|
|
}
|
|
}),
|
|
({
|
|
switch (gen_mode)
|
|
{
|
|
case RANDOM:
|
|
case RANDOM_BIT:
|
|
case RANDOM_MINUS_PLUS_ZERO:
|
|
_CubLog("%s\n", "cub::InitValue cannot generate random numbers on device.");
|
|
cuda::std::terminate();
|
|
break;
|
|
case UNIFORM:
|
|
value = true;
|
|
break;
|
|
case INTEGER_SEED:
|
|
default:
|
|
value = (index > 0);
|
|
break;
|
|
}
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* cub::NullType test initialization
|
|
*/
|
|
__host__ __device__ __forceinline__ void
|
|
InitValue(GenMode /* gen_mode */, CUB_NS_QUALIFIER::NullType& /* value */, std::size_t /* index */ = 0)
|
|
{}
|
|
|
|
/**
|
|
* cub::KeyValuePair<OffsetT, ValueT>test initialization
|
|
*/
|
|
#pragma nv_exec_check_disable
|
|
template <typename KeyT, typename ValueT>
|
|
__host__ __device__ __forceinline__ void
|
|
InitValue(GenMode gen_mode, CUB_NS_QUALIFIER::KeyValuePair<KeyT, ValueT>& value, std::size_t index = 0)
|
|
{
|
|
InitValue(gen_mode, value.value, index);
|
|
|
|
// This specialization only appears to be used by test_warp_scan.
|
|
// It initializes with uniform values and random keys, so we need to
|
|
// protect the call to the host-only RandomBits.
|
|
NV_IF_ELSE_TARGET(
|
|
NV_IS_HOST,
|
|
({
|
|
// Assign corresponding flag with a likelihood of the last bit
|
|
// being set with entropy-reduction level 3
|
|
RandomBits(value.key, 3);
|
|
value.key = (value.key & 0x1);
|
|
}),
|
|
({
|
|
_CubLog("%s\n", "cub::InitValue cannot generate random numbers on device.");
|
|
cuda::std::terminate();
|
|
}));
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Comparison and ostream operators
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* KeyValuePair ostream operator
|
|
*/
|
|
template <typename Key, typename Value>
|
|
std::ostream& operator<<(std::ostream& os, const CUB_NS_QUALIFIER::KeyValuePair<Key, Value>& val)
|
|
{
|
|
os << '(' << CoutCast(val.key) << ',' << CoutCast(val.value) << ')';
|
|
return os;
|
|
}
|
|
|
|
#if _CCCL_HAS_NVFP16()
|
|
|
|
inline std::ostream& operator<<(std::ostream& stream, const __half2& value)
|
|
{
|
|
stream << "(" << value.x << "," << value.y << ")";
|
|
return stream;
|
|
}
|
|
|
|
#endif // _CCCL_HAS_HALF
|
|
|
|
#if _CCCL_HAS_NVBF16()
|
|
|
|
inline std::ostream& operator<<(std::ostream& stream, const __nv_bfloat162& value)
|
|
{
|
|
stream << "(" << value.x << "," << value.y << ")";
|
|
return stream;
|
|
}
|
|
|
|
#endif // _CCCL_HAS_NVBF16
|
|
|
|
#if TEST_INT128()
|
|
inline std::ostream& operator<<(std::ostream& os, __uint128_t val)
|
|
{
|
|
constexpr int max_digits = 40;
|
|
char buffer[max_digits] = {};
|
|
char* digit = buffer + max_digits;
|
|
static constexpr char ascii[] = "0123456789";
|
|
|
|
do
|
|
{
|
|
digit--;
|
|
*digit = ascii[val % 10];
|
|
val /= 10;
|
|
} while (val != 0);
|
|
|
|
for (; digit != buffer + max_digits; digit++)
|
|
{
|
|
os << *digit;
|
|
}
|
|
|
|
return os;
|
|
}
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, __int128_t val)
|
|
{
|
|
if (val < 0)
|
|
{
|
|
__uint128_t tmp = -val;
|
|
os << '-' << tmp;
|
|
}
|
|
else
|
|
{
|
|
__uint128_t tmp = val;
|
|
os << tmp;
|
|
}
|
|
|
|
return os;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* Comparison and ostream operators for CUDA vector types
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Vector1 overloads
|
|
*/
|
|
#define CUB_VEC_OVERLOAD_1_OLD(T) \
|
|
/* Test initialization */ \
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, T& value, std::size_t index = 0) \
|
|
{ \
|
|
InitValue(gen_mode, value.x, index); \
|
|
}
|
|
|
|
/**
|
|
* Vector2 overloads
|
|
*/
|
|
#define CUB_VEC_OVERLOAD_2_OLD(T) \
|
|
/* Test initialization */ \
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, T& value, std::size_t index = 0) \
|
|
{ \
|
|
InitValue(gen_mode, value.x, index); \
|
|
InitValue(gen_mode, value.y, index); \
|
|
}
|
|
|
|
/**
|
|
* Vector3 overloads
|
|
*/
|
|
#define CUB_VEC_OVERLOAD_3_OLD(T) \
|
|
/* Test initialization */ \
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, T& value, std::size_t index = 0) \
|
|
{ \
|
|
InitValue(gen_mode, value.x, index); \
|
|
InitValue(gen_mode, value.y, index); \
|
|
InitValue(gen_mode, value.z, index); \
|
|
}
|
|
|
|
/**
|
|
* Vector4 overloads
|
|
*/
|
|
#define CUB_VEC_OVERLOAD_4_OLD(T) \
|
|
/* Test initialization */ \
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, T& value, std::size_t index = 0) \
|
|
{ \
|
|
InitValue(gen_mode, value.x, index); \
|
|
InitValue(gen_mode, value.y, index); \
|
|
InitValue(gen_mode, value.z, index); \
|
|
InitValue(gen_mode, value.w, index); \
|
|
}
|
|
|
|
/**
|
|
* All vector overloads
|
|
*/
|
|
#define CUB_VEC_OVERLOAD_OLD(COMPONENT_T) \
|
|
CUB_VEC_OVERLOAD_1_OLD(COMPONENT_T##1) \
|
|
CUB_VEC_OVERLOAD_2_OLD(COMPONENT_T##2) \
|
|
CUB_VEC_OVERLOAD_3_OLD(COMPONENT_T##3) \
|
|
CUB_VEC_OVERLOAD_4_OLD(COMPONENT_T##4)
|
|
|
|
/**
|
|
* Define for types
|
|
*/
|
|
CUB_VEC_OVERLOAD_OLD(char)
|
|
CUB_VEC_OVERLOAD_OLD(short)
|
|
CUB_VEC_OVERLOAD_OLD(int)
|
|
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
|
_CCCL_SUPPRESS_DEPRECATED_NVRTC_DIAG
|
|
CUB_VEC_OVERLOAD_OLD(long)
|
|
CUB_VEC_OVERLOAD_OLD(longlong)
|
|
_CCCL_SUPPRESS_DEPRECATED_POP
|
|
#if _CCCL_CTK_AT_LEAST(13, 0)
|
|
CUB_VEC_OVERLOAD_4_OLD(long4_16a)
|
|
CUB_VEC_OVERLOAD_4_OLD(long4_32a)
|
|
CUB_VEC_OVERLOAD_4_OLD(longlong4_16a)
|
|
CUB_VEC_OVERLOAD_4_OLD(longlong4_32a)
|
|
#endif // _CCCL_CTK_AT_LEAST(13, 0)
|
|
CUB_VEC_OVERLOAD_OLD(uchar)
|
|
CUB_VEC_OVERLOAD_OLD(ushort)
|
|
CUB_VEC_OVERLOAD_OLD(uint)
|
|
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
|
_CCCL_SUPPRESS_DEPRECATED_NVRTC_DIAG
|
|
CUB_VEC_OVERLOAD_OLD(ulong)
|
|
CUB_VEC_OVERLOAD_OLD(ulonglong)
|
|
_CCCL_SUPPRESS_DEPRECATED_POP
|
|
#if _CCCL_CTK_AT_LEAST(13, 0)
|
|
CUB_VEC_OVERLOAD_4_OLD(ulong4_16a)
|
|
CUB_VEC_OVERLOAD_4_OLD(ulong4_32a)
|
|
CUB_VEC_OVERLOAD_4_OLD(ulonglong4_16a)
|
|
CUB_VEC_OVERLOAD_4_OLD(ulonglong4_32a)
|
|
#endif // _CCCL_CTK_AT_LEAST(13, 0)
|
|
CUB_VEC_OVERLOAD_OLD(float)
|
|
_CCCL_SUPPRESS_DEPRECATED_PUSH
|
|
_CCCL_SUPPRESS_DEPRECATED_NVRTC_DIAG
|
|
CUB_VEC_OVERLOAD_OLD(double)
|
|
_CCCL_SUPPRESS_DEPRECATED_POP
|
|
#if _CCCL_CTK_AT_LEAST(13, 0)
|
|
CUB_VEC_OVERLOAD_4_OLD(double4_16a)
|
|
CUB_VEC_OVERLOAD_4_OLD(double4_32a)
|
|
#endif // _CCCL_CTK_AT_LEAST(13, 0)
|
|
|
|
//---------------------------------------------------------------------
|
|
// Complex data type TestFoo
|
|
//---------------------------------------------------------------------
|
|
|
|
/**
|
|
* TestFoo complex data type
|
|
*/
|
|
struct TestFoo
|
|
{
|
|
using x_t = long long;
|
|
using y_t = int;
|
|
using z_t = short;
|
|
using w_t = char;
|
|
|
|
x_t x;
|
|
y_t y;
|
|
z_t z;
|
|
w_t w;
|
|
|
|
// Factory
|
|
static __host__ __device__ __forceinline__ TestFoo MakeTestFoo(long long x, int y, short z, char w)
|
|
{
|
|
TestFoo retval = {x, y, z, w};
|
|
return retval;
|
|
}
|
|
|
|
// Assignment from int operator
|
|
__host__ __device__ __forceinline__ TestFoo& operator=(int b)
|
|
{
|
|
x = static_cast<x_t>(b);
|
|
y = static_cast<y_t>(b);
|
|
z = static_cast<z_t>(b);
|
|
w = static_cast<w_t>(b);
|
|
return *this;
|
|
}
|
|
|
|
// Summation operator
|
|
__host__ __device__ __forceinline__ TestFoo operator+(const TestFoo& b) const
|
|
{
|
|
return MakeTestFoo(x + b.x, y + b.y, static_cast<short>(z + b.z), static_cast<char>(w + b.w));
|
|
}
|
|
|
|
// Inequality operator
|
|
__host__ __device__ __forceinline__ bool operator!=(const TestFoo& b) const
|
|
{
|
|
return (x != b.x) || (y != b.y) || (z != b.z) || (w != b.w);
|
|
}
|
|
|
|
// Equality operator
|
|
__host__ __device__ __forceinline__ bool operator==(const TestFoo& b) const
|
|
{
|
|
return (x == b.x) && (y == b.y) && (z == b.z) && (w == b.w);
|
|
}
|
|
|
|
// Less than operator
|
|
__host__ __device__ __forceinline__ bool operator<(const TestFoo& b) const
|
|
{
|
|
if (x < b.x)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.x < x)
|
|
{
|
|
return false;
|
|
}
|
|
if (y < b.y)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.y < y)
|
|
{
|
|
return false;
|
|
}
|
|
if (z < b.z)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.z < z)
|
|
{
|
|
return false;
|
|
}
|
|
return w < b.w;
|
|
}
|
|
|
|
// Greater than operator
|
|
__host__ __device__ __forceinline__ bool operator>(const TestFoo& b) const
|
|
{
|
|
if (x > b.x)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.x > x)
|
|
{
|
|
return false;
|
|
}
|
|
if (y > b.y)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.y > y)
|
|
{
|
|
return false;
|
|
}
|
|
if (z > b.z)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.z > z)
|
|
{
|
|
return false;
|
|
}
|
|
return w > b.w;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* TestFoo ostream operator
|
|
*/
|
|
inline std::ostream& operator<<(std::ostream& os, const TestFoo& val)
|
|
{
|
|
os << '(' << val.x << ',' << val.y << ',' << val.z << ',' << CoutCast(val.w) << ')';
|
|
return os;
|
|
}
|
|
|
|
/**
|
|
* TestFoo test initialization
|
|
*/
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, TestFoo& value, std::size_t index = 0)
|
|
{
|
|
InitValue(gen_mode, value.x, index);
|
|
InitValue(gen_mode, value.y, index);
|
|
InitValue(gen_mode, value.z, index);
|
|
InitValue(gen_mode, value.w, index);
|
|
}
|
|
|
|
template <>
|
|
class ::cuda::std::numeric_limits<TestFoo>
|
|
{
|
|
public:
|
|
static constexpr bool is_specialized = true;
|
|
|
|
__host__ __device__ static TestFoo max()
|
|
{
|
|
return TestFoo::MakeTestFoo(
|
|
numeric_limits<long long>::max(),
|
|
numeric_limits<int>::max(),
|
|
numeric_limits<short>::max(),
|
|
numeric_limits<char>::max());
|
|
}
|
|
|
|
__host__ __device__ static TestFoo lowest()
|
|
{
|
|
return TestFoo::MakeTestFoo(
|
|
numeric_limits<long long>::lowest(),
|
|
numeric_limits<int>::lowest(),
|
|
numeric_limits<short>::lowest(),
|
|
numeric_limits<char>::lowest());
|
|
}
|
|
};
|
|
|
|
//---------------------------------------------------------------------
|
|
// Complex data type TestBar (with optimizations for fence-free warp-synchrony)
|
|
//---------------------------------------------------------------------
|
|
|
|
/**
|
|
* TestBar complex data type
|
|
*/
|
|
struct TestBar
|
|
{
|
|
long long x;
|
|
int y;
|
|
|
|
// Constructor
|
|
__host__ __device__ __forceinline__ TestBar()
|
|
: x(0)
|
|
, y(0)
|
|
{}
|
|
|
|
// Constructor
|
|
__host__ __device__ __forceinline__ TestBar(int b)
|
|
: x(b)
|
|
, y(b)
|
|
{}
|
|
|
|
// Constructor
|
|
__host__ __device__ __forceinline__ TestBar(long long x, int y)
|
|
: x(x)
|
|
, y(y)
|
|
{}
|
|
|
|
// Assignment from int operator
|
|
__host__ __device__ __forceinline__ TestBar& operator=(int b)
|
|
{
|
|
x = b;
|
|
y = b;
|
|
return *this;
|
|
}
|
|
|
|
// Summation operator
|
|
__host__ __device__ __forceinline__ TestBar operator+(const TestBar& b) const
|
|
{
|
|
return TestBar(x + b.x, y + b.y);
|
|
}
|
|
|
|
// Inequality operator
|
|
__host__ __device__ __forceinline__ bool operator!=(const TestBar& b) const
|
|
{
|
|
return (x != b.x) || (y != b.y);
|
|
}
|
|
|
|
// Equality operator
|
|
__host__ __device__ __forceinline__ bool operator==(const TestBar& b) const
|
|
{
|
|
return (x == b.x) && (y == b.y);
|
|
}
|
|
|
|
// Less than operator
|
|
__host__ __device__ __forceinline__ bool operator<(const TestBar& b) const
|
|
{
|
|
if (x < b.x)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.x < x)
|
|
{
|
|
return false;
|
|
}
|
|
return y < b.y;
|
|
}
|
|
|
|
// Greater than operator
|
|
__host__ __device__ __forceinline__ bool operator>(const TestBar& b) const
|
|
{
|
|
if (x > b.x)
|
|
{
|
|
return true;
|
|
}
|
|
else if (b.x > x)
|
|
{
|
|
return false;
|
|
}
|
|
return y > b.y;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* TestBar ostream operator
|
|
*/
|
|
inline std::ostream& operator<<(std::ostream& os, const TestBar& val)
|
|
{
|
|
os << '(' << val.x << ',' << val.y << ')';
|
|
return os;
|
|
}
|
|
|
|
/**
|
|
* TestBar test initialization
|
|
*/
|
|
__host__ __device__ __forceinline__ void InitValue(GenMode gen_mode, TestBar& value, std::size_t index = 0)
|
|
{
|
|
InitValue(gen_mode, value.x, index);
|
|
InitValue(gen_mode, value.y, index);
|
|
}
|
|
|
|
template <>
|
|
class cuda::std::numeric_limits<TestBar>
|
|
{
|
|
public:
|
|
static constexpr bool is_specialized = true;
|
|
|
|
__host__ __device__ static TestBar max()
|
|
{
|
|
return TestBar(numeric_limits<long long>::max(), numeric_limits<int>::max());
|
|
}
|
|
|
|
__host__ __device__ static TestBar lowest()
|
|
{
|
|
return TestBar(numeric_limits<long long>::lowest(), numeric_limits<int>::lowest());
|
|
}
|
|
};
|
|
|
|
/******************************************************************************
|
|
* Helper routines for list comparison and display
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Compares the equivalence of two arrays
|
|
*/
|
|
template <typename S, typename T, typename OffsetT>
|
|
int CompareResults(T* computed, S* reference, OffsetT len, bool verbose = true)
|
|
{
|
|
for (OffsetT i = 0; i < len; i++)
|
|
{
|
|
if (computed[i] != reference[i])
|
|
{
|
|
if (verbose)
|
|
{
|
|
std::cout << "INCORRECT: [" << i << "]: " << CoutCast(computed[i]) << " != " << CoutCast(reference[i]);
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Compares the equivalence of two arrays
|
|
*/
|
|
template <typename OffsetT>
|
|
int CompareResults(float* computed, float* reference, OffsetT len, bool verbose = true)
|
|
{
|
|
for (OffsetT i = 0; i < len; i++)
|
|
{
|
|
if (computed[i] != reference[i])
|
|
{
|
|
float difference = std::abs(computed[i] - reference[i]);
|
|
float fraction = difference / std::abs(reference[i]);
|
|
|
|
if (fraction > 0.00015)
|
|
{
|
|
if (verbose)
|
|
{
|
|
std::cout
|
|
<< "INCORRECT: [" << i << "]: "
|
|
<< "(computed) " << CoutCast(computed[i]) << " != " << CoutCast(reference[i])
|
|
<< " (difference:" << difference << ", fraction: " << fraction << ")";
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Compares the equivalence of two arrays
|
|
*/
|
|
template <typename OffsetT>
|
|
int CompareResults(
|
|
CUB_NS_QUALIFIER::NullType* computed, CUB_NS_QUALIFIER::NullType* reference, OffsetT len, bool verbose = true)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Compares the equivalence of two arrays
|
|
*/
|
|
template <typename OffsetT>
|
|
int CompareResults(double* computed, double* reference, OffsetT len, bool verbose = true)
|
|
{
|
|
for (OffsetT i = 0; i < len; i++)
|
|
{
|
|
if (computed[i] != reference[i])
|
|
{
|
|
double difference = std::abs(computed[i] - reference[i]);
|
|
double fraction = difference / std::abs(reference[i]);
|
|
|
|
if (fraction > 0.00015)
|
|
{
|
|
if (verbose)
|
|
{
|
|
std::cout << "INCORRECT: [" << i << "]: " << CoutCast(computed[i]) << " != " << CoutCast(reference[i])
|
|
<< " (difference:" << difference << ", fraction: " << fraction << ")";
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Verify the contents of a device array match those
|
|
* of a host array
|
|
*/
|
|
inline int CompareDeviceResults(
|
|
CUB_NS_QUALIFIER::NullType* /* h_reference */,
|
|
CUB_NS_QUALIFIER::NullType* /* d_data */,
|
|
std::size_t /* num_items */,
|
|
bool /* verbose */ = true,
|
|
bool /* display_data */ = false)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Verify the contents of a device array match those
|
|
* of a host array
|
|
*/
|
|
template <typename S, typename OffsetT>
|
|
int CompareDeviceResults(
|
|
S* /*h_reference*/,
|
|
THRUST_NS_QUALIFIER::discard_iterator<OffsetT> /*d_data*/,
|
|
std::size_t /*num_items*/,
|
|
bool /*verbose*/ = true,
|
|
bool /*display_data*/ = false)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Verify the contents of a device array match those
|
|
* of a host array
|
|
*/
|
|
template <typename S, typename T>
|
|
int CompareDeviceResults(
|
|
S* h_reference, T* d_data, std::size_t num_items, bool verbose = true, bool display_data = false)
|
|
{
|
|
if (num_items == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Allocate array on host
|
|
T* h_data = (T*) malloc(num_items * sizeof(T));
|
|
|
|
// Copy data back
|
|
cudaMemcpy(h_data, d_data, sizeof(T) * num_items, cudaMemcpyDeviceToHost);
|
|
|
|
// Display data
|
|
if (display_data)
|
|
{
|
|
printf("Reference:\n");
|
|
for (std::size_t i = 0; i < num_items; i++)
|
|
{
|
|
std::cout << CoutCast(h_reference[i]) << ", ";
|
|
}
|
|
printf("\n\nComputed:\n");
|
|
for (std::size_t i = 0; i < num_items; i++)
|
|
{
|
|
std::cout << CoutCast(h_data[i]) << ", ";
|
|
}
|
|
printf("\n\n");
|
|
}
|
|
|
|
// Check
|
|
int retval = CompareResults(h_data, h_reference, num_items, verbose);
|
|
|
|
// Cleanup
|
|
if (h_data)
|
|
{
|
|
free(h_data);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* Verify the contents of a device array match those
|
|
* of a device array
|
|
*/
|
|
template <typename T>
|
|
int CompareDeviceDeviceResults(
|
|
T* d_reference, T* d_data, std::size_t num_items, bool verbose = true, bool display_data = false)
|
|
{
|
|
// Allocate array on host
|
|
T* h_reference = (T*) malloc(num_items * sizeof(T));
|
|
T* h_data = (T*) malloc(num_items * sizeof(T));
|
|
|
|
// Copy data back
|
|
cudaMemcpy(h_reference, d_reference, sizeof(T) * num_items, cudaMemcpyDeviceToHost);
|
|
cudaMemcpy(h_data, d_data, sizeof(T) * num_items, cudaMemcpyDeviceToHost);
|
|
|
|
// Display data
|
|
if (display_data)
|
|
{
|
|
printf("Reference:\n");
|
|
for (std::size_t i = 0; i < num_items; i++)
|
|
{
|
|
std::cout << CoutCast(h_reference[i]) << ", ";
|
|
}
|
|
printf("\n\nComputed:\n");
|
|
for (std::size_t i = 0; i < num_items; i++)
|
|
{
|
|
std::cout << CoutCast(h_data[i]) << ", ";
|
|
}
|
|
printf("\n\n");
|
|
}
|
|
|
|
// Check
|
|
int retval = CompareResults(h_data, h_reference, num_items, verbose);
|
|
|
|
// Cleanup
|
|
if (h_reference)
|
|
{
|
|
free(h_reference);
|
|
}
|
|
if (h_data)
|
|
{
|
|
free(h_data);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* Print the contents of a host array
|
|
*/
|
|
inline void DisplayResults(CUB_NS_QUALIFIER::NullType* /* h_data */, std::size_t /* num_items */) {}
|
|
|
|
/**
|
|
* Print the contents of a host array
|
|
*/
|
|
template <typename InputIteratorT>
|
|
void DisplayResults(InputIteratorT h_data, std::size_t num_items)
|
|
{
|
|
// Display data
|
|
for (std::size_t i = 0; i < num_items; i++)
|
|
{
|
|
std::cout << CoutCast(h_data[i]) << ", ";
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/**
|
|
* Print the contents of a device array
|
|
*/
|
|
template <typename T>
|
|
void DisplayDeviceResults(T* d_data, std::size_t num_items)
|
|
{
|
|
// Allocate array on host
|
|
T* h_data = (T*) malloc(num_items * sizeof(T));
|
|
|
|
// Copy data back
|
|
cudaMemcpy(h_data, d_data, sizeof(T) * num_items, cudaMemcpyDeviceToHost);
|
|
|
|
DisplayResults(h_data, num_items);
|
|
|
|
// Cleanup
|
|
if (h_data)
|
|
{
|
|
free(h_data);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Segment descriptor generation
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Initialize segments
|
|
*/
|
|
template <typename OffsetT>
|
|
void InitializeSegments(OffsetT num_items, int num_segments, OffsetT* h_segment_offsets, bool verbose = false)
|
|
{
|
|
if (num_segments <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OffsetT expected_segment_length = ::cuda::ceil_div(num_items, OffsetT(num_segments));
|
|
OffsetT offset = 0;
|
|
for (int i = 0; i < num_segments; ++i)
|
|
{
|
|
h_segment_offsets[i] = offset;
|
|
|
|
OffsetT segment_length = RandomValue((expected_segment_length * 2) + 1);
|
|
offset += segment_length;
|
|
offset = ::cuda::std::min(offset, num_items);
|
|
}
|
|
h_segment_offsets[num_segments] = num_items;
|
|
|
|
if (verbose)
|
|
{
|
|
printf("Segment offsets: ");
|
|
DisplayResults(h_segment_offsets, num_segments + 1);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Timing
|
|
******************************************************************************/
|
|
|
|
struct CpuTimer
|
|
{
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER ll_freq;
|
|
LARGE_INTEGER ll_start;
|
|
LARGE_INTEGER ll_stop;
|
|
|
|
CpuTimer()
|
|
{
|
|
QueryPerformanceFrequency(&ll_freq);
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
QueryPerformanceCounter(&ll_start);
|
|
}
|
|
|
|
void Stop()
|
|
{
|
|
QueryPerformanceCounter(&ll_stop);
|
|
}
|
|
|
|
float ElapsedMillis()
|
|
{
|
|
double start = double(ll_start.QuadPart) / double(ll_freq.QuadPart);
|
|
double stop = double(ll_stop.QuadPart) / double(ll_freq.QuadPart);
|
|
|
|
return float((stop - start) * 1000);
|
|
}
|
|
|
|
#else
|
|
|
|
rusage start;
|
|
rusage stop;
|
|
|
|
void Start()
|
|
{
|
|
getrusage(RUSAGE_SELF, &start);
|
|
}
|
|
|
|
void Stop()
|
|
{
|
|
getrusage(RUSAGE_SELF, &stop);
|
|
}
|
|
|
|
float ElapsedMillis()
|
|
{
|
|
float sec = static_cast<float>(stop.ru_utime.tv_sec - start.ru_utime.tv_sec);
|
|
float usec = static_cast<float>(stop.ru_utime.tv_usec - start.ru_utime.tv_usec);
|
|
|
|
return (sec * 1000) + (usec / 1000);
|
|
}
|
|
|
|
#endif
|
|
};
|
|
|
|
struct GpuTimer
|
|
{
|
|
cudaEvent_t start;
|
|
cudaEvent_t stop;
|
|
|
|
GpuTimer()
|
|
{
|
|
cudaEventCreate(&start);
|
|
cudaEventCreate(&stop);
|
|
}
|
|
|
|
~GpuTimer()
|
|
{
|
|
cudaEventDestroy(start);
|
|
cudaEventDestroy(stop);
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
cudaEventRecord(start, nullptr);
|
|
}
|
|
|
|
void Stop()
|
|
{
|
|
cudaEventRecord(stop, nullptr);
|
|
}
|
|
|
|
float ElapsedMillis()
|
|
{
|
|
float elapsed;
|
|
cudaEventSynchronize(stop);
|
|
cudaEventElapsedTime(&elapsed, start, stop);
|
|
return elapsed;
|
|
}
|
|
};
|
|
|
|
template <int ELEMENTS_PER_OBJECT_ = 128>
|
|
struct HugeDataType
|
|
{
|
|
static constexpr int ELEMENTS_PER_OBJECT = ELEMENTS_PER_OBJECT_;
|
|
|
|
__device__ __host__ HugeDataType()
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
data[i] = 0;
|
|
}
|
|
}
|
|
|
|
__device__ __host__ HugeDataType(const HugeDataType& rhs)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
data[i] = rhs.data[i];
|
|
}
|
|
}
|
|
|
|
explicit __device__ __host__ HugeDataType(int val)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
data[i] = val;
|
|
}
|
|
}
|
|
|
|
__device__ __host__ HugeDataType& operator=(const HugeDataType& rhs)
|
|
{
|
|
if (this != &rhs)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
data[i] = rhs.data[i];
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
int data[ELEMENTS_PER_OBJECT];
|
|
};
|
|
|
|
template <int ELEMENTS_PER_OBJECT>
|
|
inline __device__ __host__ bool
|
|
operator==(const HugeDataType<ELEMENTS_PER_OBJECT>& lhs, const HugeDataType<ELEMENTS_PER_OBJECT>& rhs)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
if (lhs.data[i] != rhs.data[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <int ELEMENTS_PER_OBJECT>
|
|
inline __device__ __host__ bool
|
|
operator<(const HugeDataType<ELEMENTS_PER_OBJECT>& lhs, const HugeDataType<ELEMENTS_PER_OBJECT>& rhs)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
if (lhs.data[i] < rhs.data[i])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename DataType, int ELEMENTS_PER_OBJECT>
|
|
__device__ __host__ bool operator!=(const HugeDataType<ELEMENTS_PER_OBJECT>& lhs, const DataType& rhs)
|
|
{
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
if (lhs.data[i] != rhs)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <int ELEMENTS_PER_OBJECT>
|
|
std::ostream& operator<<(std::ostream& os, const HugeDataType<ELEMENTS_PER_OBJECT>& val)
|
|
{
|
|
os << '(';
|
|
for (int i = 0; i < ELEMENTS_PER_OBJECT; i++)
|
|
{
|
|
os << CoutCast(val.data[i]);
|
|
if (i < ELEMENTS_PER_OBJECT - 1)
|
|
{
|
|
os << ',';
|
|
}
|
|
}
|
|
os << ')';
|
|
return os;
|
|
}
|