Я недавно столкнулся с проблемами производительности при использовании Thrust
библиотека. Это происходит из-за выделения памяти в основании большой структуры вложенных циклов. Это, очевидно, нежелательно, при идеальном выполнении с использованием предварительно выделенной плиты глобальной памяти. Я хотел бы удалить или улучшить нарушающий код одним из трех способов:
Хотя третий вариант был бы моим обычным предпочтительным выбором, операция, которую я хочу выполнить, copy_if
/select_if
операция типа, в которой возвращаются как данные, так и индексы. Написание собственного ядра, вероятно, будет изобретать велосипед, и поэтому я предпочел бы пойти с одним из двух других вариантов.
Я слышал замечательные вещи о CUB, и поэтому я вижу в этом идеальный шанс использовать его в гневе. Что я хотел бы знать, это:
Как реализовать CUB? select_if
с возвращенными индексами?
Можно ли это сделать с ArgIndexInputIterator
а функтор как так?
struct GreaterThan
{
int compare;
__host__ __device__ __forceinline__
GreaterThan(int compare) : compare(compare) {}
__host__ __device__ __forceinline__
bool operator()(const cub::ArgIndexInputIterator<int> &a) const {
return (a.value > compare);
}
};
со следующим в основной части кода:
//d_in = device int array
//d_temp_storage = some preallocated blockint threshold_value;
GreaterThan select_op(threshold_value);
cub::ArgIndexInputIterator<int> input_itr(d_in);
cub::ArgIndexInputIterator<int> output_itr(d_out); //????CubDebugExit(DeviceSelect::If(d_temp_storage, temp_storage_bytes, input_itr, output_itr, d_num_selected, num_items, select_op));
Будет ли это попытаться сделать какое-либо распределение памяти под капотом?
РЕДАКТИРОВАТЬ:
Таким образом, исходя из комментария Роберта Кровеллы, функтор должен взять продукт разыменования cub::ArgIndexInputIterator<int>
, который должен быть cub::ItemOffsetPair<int>
сделать функтор сейчас:
struct GreaterThan
{
int compare;
__host__ __device__ __forceinline__
GreaterThan(int compare) : compare(compare) {}
__host__ __device__ __forceinline__
bool operator()(const cub::ItemOffsetPair<int,int> &a) const {
return (a.value > compare);
}
};
и в коде, d_out
должен быть массив устройств cub::ItemOffsetPair<int,int>
:
//d_in = device int array
//d_temp_storage = some preallocated block
cub::ItemOffsetPair<int,int> * d_out;
//allocate d_out
int threshold_value;
GreaterThan select_op(threshold_value);
cub::ArgIndexInputIterator<int,int> input_itr(d_in);
CubDebugExit(DeviceSelect::If(d_temp_storage, temp_storage_bytes, input_itr, d_out, d_num_selected, num_items, select_op));
После некоторой возни и раздумий, я смог получить простой код в соответствии с тем, что вы предлагаете работать:
$ cat t348.cu
#include <cub/cub.cuh>
#include <stdio.h>
#define DSIZE 6
struct GreaterThan
{
__host__ __device__ __forceinline__
bool operator()(const cub::ItemOffsetPair<int, ptrdiff_t> &a) const {
return (a.value > DSIZE/2);
}
};
int main(){
int num_items = DSIZE;
int *d_in;
cub::ItemOffsetPair<int,ptrdiff_t> * d_out;
int *d_num_selected;
int *d_temp_storage = NULL;
size_t temp_storage_bytes = 0;
cudaMalloc((void **)&d_in, num_items*sizeof(int));
cudaMalloc((void **)&d_num_selected, sizeof(int));
cudaMalloc((void **)&d_out, num_items*sizeof(cub::ItemOffsetPair<int,ptrdiff_t>));
int h_in[DSIZE] = {5, 4, 3, 2, 1, 0};
cudaMemcpy(d_in, h_in, num_items*sizeof(int), cudaMemcpyHostToDevice);
cub::ArgIndexInputIterator<int *> input_itr(d_in);cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, input_itr, d_out, d_num_selected, num_items, GreaterThan());
cudaMalloc(&d_temp_storage, temp_storage_bytes);
cub::DeviceSelect::If(d_temp_storage, temp_storage_bytes, input_itr, d_out, d_num_selected, num_items, GreaterThan());
int h_num_selected = 0;
cudaMemcpy(&h_num_selected, d_num_selected, sizeof(int), cudaMemcpyDeviceToHost);
cub::ItemOffsetPair<int, ptrdiff_t> h_out[h_num_selected];
cudaMemcpy(h_out, d_out, h_num_selected*sizeof(cub::ItemOffsetPair<int, ptrdiff_t>), cudaMemcpyDeviceToHost);
for (int i =0 ; i < h_num_selected; i++)
printf("index: %d, offset: %d, value: %d\n", i, h_out[i].offset, h_out[i].value);
return 0;
}
$ nvcc -arch=sm_20 -o t348 t348.cu
$ ./t348
index: 0, offset: 0, value: 5
index: 1, offset: 1, value: 4
$
RHEL 6.2, cub v1.2.2, CUDA 5.5
Недавно я столкнулся с проблемами производительности при использовании библиотеки Thrust. Эти
исходить из тяги, выделяющей память в основании большой структуры вложенного цикла. Это
очевидно нежелательный, с идеальным исполнением с использованием предварительно выделенной плиты глобальной памяти.
Thrust позволяет настроить распределение временной памяти при выполнении алгоритма.
Увидеть Пример нестандартной_распределения чтобы увидеть, как создать кеш для предварительно выделенной плиты.