3种不同尺寸向量的推力复数变换

Yan*_*ael 5 cuda thrust

你好我在C +中有这个循环,我试图将它转换为推力但没有得到相同的结果......任何想法?谢谢

C++代码

for (i=0;i<n;i++) 
    for (j=0;j<n;j++) 
      values[i]=values[i]+(binv[i*n+j]*d[j]);
Run Code Online (Sandbox Code Playgroud)

推力代码

thrust::fill(values.begin(), values.end(), 0);
thrust::transform(make_zip_iterator(make_tuple(
                thrust::make_permutation_iterator(values.begin(), thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexDivFunctor(n))),
                binv.begin(),
                thrust::make_permutation_iterator(d.begin(), thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexModFunctor(n))))),
                make_zip_iterator(make_tuple(
                thrust::make_permutation_iterator(values.begin(), thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexDivFunctor(n))) + n,
                binv.end(),
                thrust::make_permutation_iterator(d.begin(), thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexModFunctor(n))) + n)),
                thrust::make_permutation_iterator(values.begin(), thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexDivFunctor(n))),
                function1()
                );
Run Code Online (Sandbox Code Playgroud)

推力功能

struct IndexDivFunctor: thrust::unary_function<int, int>
{
  int n;

  IndexDivFunctor(int n_) : n(n_) {}

  __host__ __device__
  int operator()(int idx)
  {
    return idx / n;
  }
};

struct IndexModFunctor: thrust::unary_function<int, int>
{
  int n;

  IndexModFunctor(int n_) : n(n_) {}

  __host__ __device__
  int operator()(int idx)
  {
    return idx % n;
  }
};


struct function1
{
  template <typename Tuple>
  __host__ __device__
  double operator()(Tuple v)
  {
    return thrust::get<0>(v) + thrust::get<1>(v) * thrust::get<2>(v);
  }
};
Run Code Online (Sandbox Code Playgroud)

tal*_*ies 4

首先,一些一般性评论。你的循环

for (i=0;i<n;i++) 
    for (j=0;j<n;j++) 
      v[i]=v[i]+(B[i*n+j]*d[j]);
Run Code Online (Sandbox Code Playgroud)

相当于标准BLAS gemv操作

在此输入图像描述

其中矩阵按行主序存储。在设备上执行此操作的最佳方法是使用 CUBLAS,而不是由推力原语构建的东西。

话虽如此,您发布的推力代码绝对不可能执行您的串行代码的操作。您看到的错误不是浮点关联性的结果。从根本上讲thrust::transform,将提供给输入迭代器的每个元素的函子应用,并将结果存储在输出迭代器上。为了产生与您发布的循环相同的结果,调用thrust::transform需要对您发布的 fmad 仿函数执行 (n*n) 次操作。显然事实并非如此。此外,不能保证将以thrust::transform安全的方式执行求和/归约操作,避免内存竞争。

正确的解决方案可能是这样的:

  1. 使用 Thrust::transform 计算Bd元素的 (n*n) 乘积
  2. 使用 Throw::reduce_by_key 将乘积减少为部分和,得到Bd
  3. 使用 Throw::transform 将生成的矩阵向量乘积添加到v以产生最终结果。

在代码中,首先定义一个函子,如下所示:

struct functor
{
  template <typename Tuple>
  __host__ __device__
  double operator()(Tuple v)
  {
    return thrust::get<0>(v) * thrust::get<1>(v);
  }
};
Run Code Online (Sandbox Code Playgroud)

然后执行以下操作来计算矩阵向量乘法

  typedef thrust::device_vector<int> iVec;
  typedef thrust::device_vector<double> dVec;

  typedef thrust::counting_iterator<int> countIt;
  typedef thrust::transform_iterator<IndexDivFunctor, countIt> columnIt;
  typedef thrust::transform_iterator<IndexModFunctor, countIt> rowIt;

  // Assuming the following allocations on the device
  dVec B(n*n), v(n), d(n);

  // transformation iterators mapping to vector rows and columns
  columnIt cv_begin = thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexDivFunctor(n));
  columnIt cv_end   = cv_begin + (n*n);

  rowIt rv_begin = thrust::make_transform_iterator(thrust::make_counting_iterator(0), IndexModFunctor(n));
  rowIt rv_end   = rv_begin + (n*n);

  dVec temp(n*n);
  thrust::transform(make_zip_iterator(
                      make_tuple(
                        B.begin(),
                        thrust::make_permutation_iterator(d.begin(),rv_begin) ) ),
                    make_zip_iterator(
                      make_tuple(
                        B.end(),
                        thrust::make_permutation_iterator(d.end(),rv_end) ) ),
                    temp.begin(),
                    functor());

  iVec outkey(n);
  dVec Bd(n);
  thrust::reduce_by_key(cv_begin, cv_end, temp.begin(), outkey.begin(), Bd.begin());
  thrust::transform(v.begin(), v.end(), Bd.begin(), v.begin(), thrust::plus<double>());
Run Code Online (Sandbox Code Playgroud)

dgemv当然,与使用CUBLAS 等专门设计的矩阵向量乘法代码相比,这是一种非常低效的计算方式。