提问人:huzzm 提问时间:2/27/2022 更新时间:2/27/2022 访问量:369
cub::D eviceRadixSort 在指定结束位时失败
cub::DeviceRadixSort fails when specifying end bit
问:
我正在使用 CUB 库的 GPU 基数排序算法对 N 个 32 位无符号整数进行排序,这些整数的值都只利用其 32 位中的 k,从最低有效位开始。
因此,我在调用 cub::D eviceRadixSort::SortKeys 时指定了位子范围 [begin_bit, end_bit),以期提高排序性能。我正在使用最新版本的 CUB (1.16.0)。
但是,当尝试对 10 亿个具有某些指定位范围 [begin_bit=0, end_bit=k) 和 k = {20,19,18} 的键进行排序时,SortKeys 崩溃(不是确定性的,但几乎总是)并报告非法内存访问错误,例如./cub_sort_test 1000000000 0 20
我分别在 CUDA 版本 11.4 和 11.2 的 Volta 和 Ampere NVIDIA GPU 上对此进行了测试。以前有没有人遇到过这个问题,和/或知道解决方法?下面是最小的、可重现的示例代码:
// HOW TO BUILD: nvcc -O3 -std=c++17 -Xcompiler -fopenmp cub_sort_test.cu -o cub_sort_test
#include <cub/cub.cuh>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/system/cuda/experimental/pinned_allocator.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <parallel/algorithm>
#include <random>
#include <vector>
#include <iostream>
#define DEBUG
#ifdef DEBUG
#define CheckCudaError(instruction) \
{ AssertNoCudaError((instruction), __FILE__, __LINE__); }
#else
#define CheckCudaError(instruction) instruction
#endif
inline void AssertNoCudaError(cudaError_t error_code, const char* file, int line) {
if (error_code != cudaSuccess) {
std::cout << "Error: " << cudaGetErrorString(error_code) << " " << file << " " << line << "\n";
}
}
template <typename T>
using PinnedHostVector = thrust::host_vector<T, thrust::system::cuda::experimental::pinned_allocator<T>>;
std::mt19937 SeedRandomGenerator(uint32_t distribution_seed) {
const size_t seeds_bytes = sizeof(std::mt19937::result_type) * std::mt19937::state_size;
const size_t seeds_length = seeds_bytes / sizeof(std::seed_seq::result_type);
std::vector<std::seed_seq::result_type> seeds(seeds_length);
std::generate(seeds.begin(), seeds.end(), [&]() {
distribution_seed = (distribution_seed << 1) | (distribution_seed >> (-1 & 31));
return distribution_seed;
});
std::seed_seq seed_sequence(seeds.begin(), seeds.end());
return std::mt19937{seed_sequence};
}
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: ./cub-sort-test <num_keys> <gpu_id> <bit_entropy>" << std::endl;
return -1;
}
size_t num_keys = std::stoull(argv[1]);
int gpu = std::stoi(argv[2]);
size_t bit_entropy = std::stoi(argv[3]);
cudaStream_t stream;
CheckCudaError(cudaSetDevice(gpu));
CheckCudaError(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
PinnedHostVector<uint32_t> keys(num_keys);
#pragma omp parallel num_threads(64)
{
uint32_t max = (1 << bit_entropy) - 1;
if (bit_entropy == sizeof(uint32_t) * 8) {
max = std::numeric_limits<uint32_t>::max();
} else if (bit_entropy == 1) {
max = 2;
}
std::mt19937 random_generator = SeedRandomGenerator(2147483647 + static_cast<size_t>(omp_get_thread_num()));
std::uniform_real_distribution<double> uniform_dist(0, max);
#pragma omp for schedule(static)
for (size_t i = 0; i < num_keys; ++i) {
keys[i] = static_cast<uint32_t>(uniform_dist(random_generator));
}
}
thrust::device_vector<uint32_t> device_vector(num_keys);
thrust::copy(keys.begin(), keys.end(), device_vector.begin());
CheckCudaError(cudaDeviceSynchronize());
size_t num_temporary_bytes = 0;
cub::DeviceRadixSort::SortKeys(
NULL, num_temporary_bytes, thrust::raw_pointer_cast(device_vector.data()),
thrust::raw_pointer_cast(device_vector.data()), num_keys, 0, bit_entropy + 1, stream); // bit subrange is [begin_bit, end_bit), thus bit_entropy + 1
uint8_t* temporary_storage = nullptr;
CheckCudaError(cudaMalloc(reinterpret_cast<void**>(&temporary_storage), num_temporary_bytes));
cub::DeviceRadixSort::SortKeys(
(void*)temporary_storage, num_temporary_bytes, thrust::raw_pointer_cast(device_vector.data()),
thrust::raw_pointer_cast(device_vector.data()), num_keys, 0, bit_entropy + 1, stream);
CheckCudaError(cudaStreamSynchronize(stream));
thrust::copy(device_vector.begin(), device_vector.end(), keys.begin());
CheckCudaError(cudaFree(temporary_storage));
if (std::is_sorted(keys.begin(), keys.end()) == false) {
std::cout << "Error: Sorting failed." << std::endl;
}
return 0;
}
答:
4赞
Abator Abetor
2/27/2022
#1
代码的问题在于您没有正确使用。 不能就地工作。您需要为排序后的数据提供单独的输出缓冲区。SortKeys
SortKeys
#include <cub/cub.cuh>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/system/cuda/experimental/pinned_allocator.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <parallel/algorithm>
#include <random>
#include <vector>
#include <iostream>
#define DEBUG
#ifdef DEBUG
#define CheckCudaError(instruction) \
{ AssertNoCudaError((instruction), __FILE__, __LINE__); }
#else
#define CheckCudaError(instruction) instruction
#endif
inline void AssertNoCudaError(cudaError_t error_code, const char* file, int line) {
if (error_code != cudaSuccess) {
std::cout << "Error: " << cudaGetErrorString(error_code) << " " << file << " " << line << "\n";
}
}
template <typename T>
using PinnedHostVector = thrust::host_vector<T, thrust::system::cuda::experimental::pinned_allocator<T>>;
std::mt19937 SeedRandomGenerator(uint32_t distribution_seed) {
const size_t seeds_bytes = sizeof(std::mt19937::result_type) * std::mt19937::state_size;
const size_t seeds_length = seeds_bytes / sizeof(std::seed_seq::result_type);
std::vector<std::seed_seq::result_type> seeds(seeds_length);
std::generate(seeds.begin(), seeds.end(), [&]() {
distribution_seed = (distribution_seed << 1) | (distribution_seed >> (-1 & 31));
return distribution_seed;
});
std::seed_seq seed_sequence(seeds.begin(), seeds.end());
return std::mt19937{seed_sequence};
}
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: ./cub-sort-test <num_keys> <gpu_id> <bit_entropy>" << std::endl;
return -1;
}
size_t num_keys = std::stoull(argv[1]);
int gpu = std::stoi(argv[2]);
size_t bit_entropy = std::stoi(argv[3]);
cudaStream_t stream;
CheckCudaError(cudaSetDevice(gpu));
CheckCudaError(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
PinnedHostVector<uint32_t> keys(num_keys);
#pragma omp parallel num_threads(64)
{
uint32_t max = (1 << bit_entropy) - 1;
if (bit_entropy == sizeof(uint32_t) * 8) {
max = std::numeric_limits<uint32_t>::max();
} else if (bit_entropy == 1) {
max = 2;
}
std::mt19937 random_generator = SeedRandomGenerator(2147483647 + static_cast<size_t>(omp_get_thread_num()));
std::uniform_real_distribution<double> uniform_dist(0, max);
#pragma omp for schedule(static)
for (size_t i = 0; i < num_keys; ++i) {
keys[i] = static_cast<uint32_t>(uniform_dist(random_generator));
}
}
thrust::device_vector<uint32_t> device_vector(num_keys);
thrust::copy(keys.begin(), keys.end(), device_vector.begin());
thrust::device_vector<uint32_t> device_vector_sorted(num_keys);
CheckCudaError(cudaDeviceSynchronize());
size_t num_temporary_bytes = 0;
cub::DeviceRadixSort::SortKeys(
NULL, num_temporary_bytes, thrust::raw_pointer_cast(device_vector.data()),
thrust::raw_pointer_cast(device_vector_sorted.data()), num_keys, 0, bit_entropy + 1, stream); // bit subrange is [begin_bit, end_bit), thus bit_entropy + 1
uint8_t* temporary_storage = nullptr;
CheckCudaError(cudaMalloc(reinterpret_cast<void**>(&temporary_storage), num_temporary_bytes));
cub::DeviceRadixSort::SortKeys(
(void*)temporary_storage, num_temporary_bytes, thrust::raw_pointer_cast(device_vector.data()),
thrust::raw_pointer_cast(device_vector_sorted.data()), num_keys, 0, bit_entropy + 1, stream);
CheckCudaError(cudaStreamSynchronize(stream));
thrust::copy(device_vector_sorted.begin(), device_vector_sorted.end(), keys.begin());
CheckCudaError(cudaFree(temporary_storage));
if (std::is_sorted(keys.begin(), keys.end()) == false) {
std::cout << "Error: Sorting failed." << std::endl;
}
return 0;
}
如果排序后不再使用未排序的数组并且可以被覆盖,我建议使用重载,这需要减少内存使用。否则,将分配一个临时键数组,因为输入无法被覆盖。DoubleBuffer<Keys>
const Key*
评论
0赞
huzzm
2/28/2022
你是对的。有趣的是,当end_bit设置为默认值时(至少在我测试它时),它不会崩溃,这导致我假设位范围是问题所在......
评论