CUB 分段还原不产生结果
CUB segmented reduction not producing results
我正在尝试使用 CUB's segmented-reduction 原语求和,但我坚持使用它。
这是我的代码:
int main() {
const int N = 7;
const int num_segments = 3;
int d_offsets[]= {0,3,3,7};
int *h_data = (int *)malloc(N * sizeof(int));
int *h_result = (int *)malloc(num_segments * sizeof(int));
for (int i=0; i<N; i++) {
h_data[i] = 3;
}
int *d_data;
cudaMalloc((int**)&d_data, N * sizeof(int));
cudaMemcpy(d_data, h_data, N * sizeof(int), cudaMemcpyHostToDevice);
int *d_result;
cudaMalloc((int**)&d_result, num_segments * sizeof(int));
void *d_temp_storage = NULL;
size_t temp_storage_bytes = 0;
cudaMalloc((void**)&d_temp_storage, temp_storage_bytes);
cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, d_data, d_result,
num_segments, d_offsets, d_offsets + 1);
cudaMemcpy(h_result, d_result, num_segments*sizeof(int), cudaMemcpyDeviceToHost);
printf("Results:\n");
for (int i=0; i<num_segments; i++) {
printf("CUB: %d\n", h_result[i]);
}
}
但结果我得到了这个:
Results:
CUB: 0
CUB: 0
CUB: 0
我不知道到底是什么问题。
在实际示例中,我有一个非常大的数组,其中的段数等于 400。
我可以优化代码,这样我就不需要为 d_offsets
.
声明和分配内存吗
您确实没有认真尝试调试您的代码:
- 您缺少
d_results
的内存分配(您已修复)
- 您试图为
d_offsets
中的设备内存地址传递主机内存地址。当然,这会导致 CUDA 运行 时间错误 - 但是
- 您没有检查 运行时间错误。
- 您只调用了 CUB 函数一次 - 虽然您必须 运行 它两次才能真正执行任何操作:一次使用
nullptr
作为开始 space,以获取scratch space 大小,然后再用实际的 scratch space 做工作。这很烦人 API 但这就是它的工作原理。
当您自己没有花时间调试您的代码时,浪费 SO 社区的时间是不合适的。
不过,您仍然可以做一些事情来 避免 不得不检查错误,至少,这是使用某种类型的库来为您做这件事(例如通过抛出错误)。如果你这样做了 - 例如,使用我的 CUDA Runtime API wrappers(抱歉自插),并为你需要的一切正确分配内存,你最终会得到这样的东西:
#include <cub/cub.cuh>
#include <cuda/api_wrappers.h>
#include <vector>
#include <cstdlib>
int main() {
const int N = 7;
const int num_segments = 3;
auto h_offsets = std::vector<int> {0,3,3,7};
auto h_data = std::vector<int>(N);
auto h_results = std::vector<int>(num_segments);
std::fill(h_data.begin(), h_data.end(), 3);
auto current_device = cuda::device::current::get();
auto d_offsets = cuda::memory::device::make_unique<int[]>(
current_device, h_offsets.size());
auto d_data = cuda::memory::device::make_unique<int[]>(
current_device, N);
cuda::memory::copy(
d_offsets.get(), &h_offsets[0], h_offsets.size() * sizeof(int));
cuda::memory::copy(
d_data.get(), &h_data[0], h_data.size() * sizeof(int));
auto d_results = cuda::memory::device::make_unique<int[]>(
current_device, num_segments);
auto d_start_offsets = d_offsets.get();
auto d_end_offsets = d_start_offsets + 1; // aliasing, see CUB documentation
size_t temp_storage_bytes = 0;
// This call merely obtains a value for temp_storage_bytes, passed here
// as a non-const reference; other arguments are unused
cub::DeviceSegmentedReduce::Sum(
nullptr, temp_storage_bytes, d_data.get(), d_results.get(),
num_segments, d_start_offsets, d_end_offsets);
auto d_temp_storage = cuda::memory::device::make_unique<char[]>(
current_device, temp_storage_bytes);
cub::DeviceSegmentedReduce::Sum(
d_temp_storage.get(), temp_storage_bytes, d_data.get(),
d_results.get(), num_segments, d_start_offsets, d_end_offsets);
cuda::memory::copy(
&h_results[0], d_results.get(), num_segments * sizeof(int));
std::cout << "Results:\n";
for (int i=0; i<num_segments; i++) {
std::cout << "Segment " << i << " data sums up to " << h_results[i] << "\n";
}
return EXIT_SUCCESS;
}
有效:
Results:
Segment 0 data sums up to 9
Segment 1 data sums up to 0
Segment 2 data sums up to 12
补充提示:
- 始终调查编译器警告。
- 使用
cuda-memcheck
避免内存泄漏/在错误的device/host端初始化等
- 如果您直接使用 CUDA 运行时 API,您 必须 check every call for errors.
我正在尝试使用 CUB's segmented-reduction 原语求和,但我坚持使用它。
这是我的代码:
int main() {
const int N = 7;
const int num_segments = 3;
int d_offsets[]= {0,3,3,7};
int *h_data = (int *)malloc(N * sizeof(int));
int *h_result = (int *)malloc(num_segments * sizeof(int));
for (int i=0; i<N; i++) {
h_data[i] = 3;
}
int *d_data;
cudaMalloc((int**)&d_data, N * sizeof(int));
cudaMemcpy(d_data, h_data, N * sizeof(int), cudaMemcpyHostToDevice);
int *d_result;
cudaMalloc((int**)&d_result, num_segments * sizeof(int));
void *d_temp_storage = NULL;
size_t temp_storage_bytes = 0;
cudaMalloc((void**)&d_temp_storage, temp_storage_bytes);
cub::DeviceSegmentedReduce::Sum(d_temp_storage, temp_storage_bytes, d_data, d_result,
num_segments, d_offsets, d_offsets + 1);
cudaMemcpy(h_result, d_result, num_segments*sizeof(int), cudaMemcpyDeviceToHost);
printf("Results:\n");
for (int i=0; i<num_segments; i++) {
printf("CUB: %d\n", h_result[i]);
}
}
但结果我得到了这个:
Results:
CUB: 0
CUB: 0
CUB: 0
我不知道到底是什么问题。
在实际示例中,我有一个非常大的数组,其中的段数等于 400。
我可以优化代码,这样我就不需要为 d_offsets
.
您确实没有认真尝试调试您的代码:
- 您缺少
d_results
的内存分配(您已修复) - 您试图为
d_offsets
中的设备内存地址传递主机内存地址。当然,这会导致 CUDA 运行 时间错误 - 但是 - 您没有检查 运行时间错误。
- 您只调用了 CUB 函数一次 - 虽然您必须 运行 它两次才能真正执行任何操作:一次使用
nullptr
作为开始 space,以获取scratch space 大小,然后再用实际的 scratch space 做工作。这很烦人 API 但这就是它的工作原理。
当您自己没有花时间调试您的代码时,浪费 SO 社区的时间是不合适的。
不过,您仍然可以做一些事情来 避免 不得不检查错误,至少,这是使用某种类型的库来为您做这件事(例如通过抛出错误)。如果你这样做了 - 例如,使用我的 CUDA Runtime API wrappers(抱歉自插),并为你需要的一切正确分配内存,你最终会得到这样的东西:
#include <cub/cub.cuh>
#include <cuda/api_wrappers.h>
#include <vector>
#include <cstdlib>
int main() {
const int N = 7;
const int num_segments = 3;
auto h_offsets = std::vector<int> {0,3,3,7};
auto h_data = std::vector<int>(N);
auto h_results = std::vector<int>(num_segments);
std::fill(h_data.begin(), h_data.end(), 3);
auto current_device = cuda::device::current::get();
auto d_offsets = cuda::memory::device::make_unique<int[]>(
current_device, h_offsets.size());
auto d_data = cuda::memory::device::make_unique<int[]>(
current_device, N);
cuda::memory::copy(
d_offsets.get(), &h_offsets[0], h_offsets.size() * sizeof(int));
cuda::memory::copy(
d_data.get(), &h_data[0], h_data.size() * sizeof(int));
auto d_results = cuda::memory::device::make_unique<int[]>(
current_device, num_segments);
auto d_start_offsets = d_offsets.get();
auto d_end_offsets = d_start_offsets + 1; // aliasing, see CUB documentation
size_t temp_storage_bytes = 0;
// This call merely obtains a value for temp_storage_bytes, passed here
// as a non-const reference; other arguments are unused
cub::DeviceSegmentedReduce::Sum(
nullptr, temp_storage_bytes, d_data.get(), d_results.get(),
num_segments, d_start_offsets, d_end_offsets);
auto d_temp_storage = cuda::memory::device::make_unique<char[]>(
current_device, temp_storage_bytes);
cub::DeviceSegmentedReduce::Sum(
d_temp_storage.get(), temp_storage_bytes, d_data.get(),
d_results.get(), num_segments, d_start_offsets, d_end_offsets);
cuda::memory::copy(
&h_results[0], d_results.get(), num_segments * sizeof(int));
std::cout << "Results:\n";
for (int i=0; i<num_segments; i++) {
std::cout << "Segment " << i << " data sums up to " << h_results[i] << "\n";
}
return EXIT_SUCCESS;
}
有效:
Results:
Segment 0 data sums up to 9
Segment 1 data sums up to 0
Segment 2 data sums up to 12
补充提示:
- 始终调查编译器警告。
- 使用
cuda-memcheck
避免内存泄漏/在错误的device/host端初始化等 - 如果您直接使用 CUDA 运行时 API,您 必须 check every call for errors.