基于它们各自的包含全局地址的索引向量将两个不同长度的向量拼接到具有推力的共同长度的新向量

时间:2014-08-09 09:56:43

标签: c++ sorting vector cuda thrust

多年来,这个问题一直存在于我的脑海中。我从这个论坛学到了很多c ++和cuda。以前我在fortran序列代码中用大量条件语句编写了以下内容,并使用了gotos,因为我找不到一个聪明的方法来实现它。
这是问题所在。

给出4个向量:

int indx(nshape);
float dnx(nshape); 
/* nshape > nord */
int indy(nord);
float dny(nord);

indx和indy是包含全局坐标的索引向量(分别为值dnx,dny的键)。在解析这个期望的交错/拼接函数之前,它们的全局范围是未知的。所有已知的是可能的局部范围的长度可以是[0,nord * nord]以及向量indx和indy中的最大值和最小值。

我想创建包含dnx和dny原始值的相同长度的新向量dn1和dn2,但是扩展为用原始向量填充原始向量dnx和dny用于它们不包含的所有全局坐标另一个向量。它们将形成需要全局地址对齐的外部产品的载体。

我无法在网上找到任何在c ++中使用逻辑掩码的参考资料,例如在fortran中进行并行化。我的出发点是使用推力库stable_sort以升序,binary_search来比较数组,分区等。也许有一种清晰简洁的方法。

下面的示例索引和值vecs通常不会从0开始,也不会与临时索引向量的本地寻址一致,也不会出现任何奇怪的模式 - 这些值只是为了帮助说明。)

indx[]={0,2,4,6,8,10,12};  indy[]={1, 2, 3, 4};
dnx[]={99,99,99,99,99,99,99};  dny[]={66,66,66,66};

ind[]={0,1,2,3,4,6,8,10,12}
dn1[]={99,0,99,0,99,99,99,99,99}
dn2[]={0,66,66,66,66,0,0,0,0}

之前我做了类似下面的内容,其中内核应用了比较,填充和流程基于以下条件,并继续通过这些条件行之一再次进入,直到最大的本地索引超过最大向量i的长度, ei,j> nshape:

3
if(indx[i] < indy[j]{kernel_1; i++; if(i > nshape){return}; goto 3}
if(indx[i] == indy[j]){kernel_2;i++;j++; if(i || j > nshape) {return}; goto 3}
if(indx[i] > indy[j]{kernel_3, j++, if(j>nshape){return}; goto 3}

对于mongrel伪代码抱歉。我非常期待任何想法或更好的解决方案与c ++,cuda,推力。 非常感谢。 标记

1 个答案:

答案 0 :(得分:1)

我遇到的一种方法是进行并行二进制搜索,从全局索引向量中获取每个值,并查看它是否在键向量中匹配。如果它在键向量中有匹配,则将相应的值放在结果中。如果它在关键向量中没有匹配,则在结果中放置0。

所以对于每个职位:

ind[]={0,1,2,3,4,6,8,10,12}

查看是否存在匹配的索引:

indy[]={1, 2, 3, 4};

我们将使用并行二进制搜索来执行此操作,并返回相应的索引(indy中的匹配值)。

如果我们找到匹配项,那么在结果中的相关位置,我们将放置对应于indy[matching_index]的值,即。 dny[matching_index]。否则在结果中加零。

在推力的情况下,我能够将其减少到两个推力调用。

第一个是thrust::lower_bound操作,它实际上是矢量化/并行二进制搜索。就像在CUDA情况下一样,我们使用二进制搜索来获取全局向量的每个元素(ind)并查看关键向量中是否存在匹配(例如indx),返回索引键向量中的匹配位置(下界)。

第二个电话使用thrust::for_each有点复杂。我们为extend_functor操作创建了一个特殊的仿函数(for_each),该函数使用指向键向量开始的指针(例如indx)进行初始化,其长度以及指向值向量的指针(例如dnx)。然后extend_functor采用全局向量的3元组,下界向量和结果向量值,并执行剩余步骤。如果下限值在键向量的长度内,则检查下限是否在键向量和全局向量之间产生匹配。如果是,则将相应的值放入结果向量中,否则将0置于结果向量中。

以下代码使用CUDA实现此功能,并使用推力。

#include <iostream>
#include <thrust/device_vector.h>
#include <thrust/binary_search.h>
#include <thrust/copy.h>

#define MAX_DSIZE 32768
#define nTPB 256


struct extend_functor
{
  int vec_len;
  int *vec1;
  float *vec2;
  extend_functor(int *_vec1, int _vec_len, float *_vec2) : vec1(_vec1), vec2(_vec2),  vec_len(_vec_len) {};
  template <typename Tuple>
  __device__ __host__
  void operator()(const Tuple &my_tuple) {
    float result = 0.0f;
    if (thrust::get<1>(my_tuple) < vec_len)
      if (thrust::get<0>(my_tuple) == vec1[thrust::get<1>(my_tuple)]) result = vec2[thrust::get<1>(my_tuple)];
    thrust::get<2>(my_tuple) = result;
  }

};

// binary search, looking for key in a

__device__ void bsearch_range(const int *a, const int key, const unsigned len_a, unsigned *idx){
  unsigned lower = 0;
  unsigned upper = len_a;
  unsigned midpt;
  while (lower < upper){
    midpt = (lower + upper)>>1;
    if (a[midpt] < key) lower = midpt +1;
    else upper = midpt;
    }
  *idx = lower;
  return;
  }


// k is the key vector
// g is the global index vector
// v is the value vector
// r is the result vector

__global__ void extend_kernel(const int *k, const unsigned len_k, const int *g,  const unsigned len_g, const float *v, float *r){
  unsigned idx = (blockDim.x * blockIdx.x) + threadIdx.x;
  if (idx < len_g) {
    unsigned my_idx;
    int g_key = g[idx];
    bsearch_range(k, g_key, len_k, &my_idx);
    int my_key = -1;
    if (my_idx < len_k)
      my_key = k[my_idx];
    float my_val;
    if (g_key == my_key) my_val = v[my_idx];
    else my_val = 0.0f;
    r[idx] = my_val;
    }
}


int main(){

  int len_x = 7;
  int len_y = 4;
  int len_g = 9;
  int indx[]={0,2,4,6,8,10,12};
  int indy[]={1, 2, 3, 4};
  float dnx[]={91.0f,92.0f,93.0f,94.0f,95.0f,96.0f,97.0f};
  float dny[]={61.0f,62.0f,63.0f,64.0f};
  int ind[]={0,1,2,3,4,6,8,10,12};

  int *h_k, *d_k, *h_g, *d_g;
  float *h_v, *d_v, *h_r, *d_r;

  h_k = (int *)malloc(MAX_DSIZE*sizeof(int));
  h_g = (int *)malloc(MAX_DSIZE*sizeof(int));
  h_v = (float *)malloc(MAX_DSIZE*sizeof(float));
  h_r = (float *)malloc(MAX_DSIZE*sizeof(float));

  cudaMalloc(&d_k, MAX_DSIZE*sizeof(int));
  cudaMalloc(&d_g, MAX_DSIZE*sizeof(int));
  cudaMalloc(&d_v, MAX_DSIZE*sizeof(float));
  cudaMalloc(&d_r, MAX_DSIZE*sizeof(float));

// test case x

  memcpy(h_k, indx, len_x*sizeof(int));
  memcpy(h_g, ind, len_g*sizeof(int));
  memcpy(h_v, dnx, len_x*sizeof(float));

  cudaMemcpy(d_k, h_k, len_x*sizeof(int), cudaMemcpyHostToDevice);
  cudaMemcpy(d_g, h_g, len_g*sizeof(int), cudaMemcpyHostToDevice);
  cudaMemcpy(d_v, h_v, len_x*sizeof(float), cudaMemcpyHostToDevice);
  extend_kernel<<<(len_g+nTPB-1)/nTPB, nTPB >>>(d_k, len_x, d_g, len_g, d_v, d_r);
  cudaMemcpy(h_r, d_r, len_g*sizeof(float), cudaMemcpyDeviceToHost);

  std::cout << "case x result: ";
  for (int i=0; i < len_g; i++)
    std::cout << h_r[i] << " ";
  std::cout << std::endl;


// test case y

  memcpy(h_k, indy, len_y*sizeof(int));
  memcpy(h_g, ind, len_g*sizeof(int));
  memcpy(h_v, dny, len_y*sizeof(float));

  cudaMemcpy(d_k, h_k, len_y*sizeof(int), cudaMemcpyHostToDevice);
  cudaMemcpy(d_g, h_g, len_g*sizeof(int), cudaMemcpyHostToDevice);
  cudaMemcpy(d_v, h_v, len_y*sizeof(float), cudaMemcpyHostToDevice);
  extend_kernel<<<(len_g+nTPB-1)/nTPB, nTPB >>>(d_k, len_y, d_g, len_g, d_v, d_r);
  cudaMemcpy(h_r, d_r, len_g*sizeof(float), cudaMemcpyDeviceToHost);

  std::cout << "case y result: ";
  for (int i=0; i < len_g; i++)
    std::cout << h_r[i] << " ";
  std::cout << std::endl;

// using thrust

  thrust::device_vector<int> tind(ind, ind+len_g);
  thrust::device_vector<int> tindx(indx, indx+len_x);
  thrust::device_vector<float> tdnx(dnx, dnx+len_x);
  thrust::device_vector<float> tresult(len_g);

  thrust::device_vector<int> tbound(len_g);
  thrust::lower_bound(tindx.begin(), tindx.end(), tind.begin(), tind.end(), tbound.begin());
  thrust::for_each(thrust::make_zip_iterator(thrust::make_tuple(tind.begin(), tbound.begin(), tresult.begin())), thrust::make_zip_iterator(thrust::make_tuple(tind.end(), tbound.end(), tresult.end())), extend_functor(thrust::raw_pointer_cast(tindx.data()), len_x, thrust::raw_pointer_cast(tdnx.data())));

  std::cout << "thrust case x result: ";
  thrust::copy(tresult.begin(), tresult.end(), std::ostream_iterator<float>(std::cout, " "));
  std::cout << std::endl;
  return 0;
}