在device_vector

时间:2019-04-08 18:40:40

标签: c++ cuda iterator thrust

我正在基于基础迭代器定义iterator_adaptor。 整个过程在使用host_vectors时都有效,但是当我将其应用于device_vectors时,编译器将引发错误:引用非const的初始值必须为左值。

#include <thrust/iterator/iterator_adaptor.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>

struct BoxIterator{

  unsigned int m_loc;
 typedef int difference_type;
  typedef double* pointer;
  typedef double& reference;
  typedef double value_type;
  typedef thrust::random_access_device_iterator_tag iterator_category;
  __host__ __device__
  BoxIterator() : m_loc(0){}

  __host__ __device__
  void operator++()
  {
    ++m_loc;
  }
  __host__ __device__
  void advance(int n)
  {
    m_loc += n;
  }
  __host__ __device__
  void operator--()
  {
    advance(-1);
  }

  __host__ __device__
  void operator+=(int n)
  {
    advance(n);
  }
  __host__ __device__
  void begin()
  {
    m_loc = 0;
  }

  __host__ __device__ 
  bool operator==(const BoxIterator & other) const
  {return  m_loc==other.m_loc;}

  __host__ __device__
  bool equal(const BoxIterator & other) const
  {
    return m_loc==other.m_loc;
  }

  __host__ __device__
   difference_type distance_to(const BoxIterator & other) const
   {
     return other.m_loc - this->m_loc;
   }
   __host__ __device__
   BoxIterator operator+(int n)
   {
     BoxIterator tmp = *this;
     tmp.m_loc += n;
     return tmp;
   }
  __host__ __device__
  BoxIterator(const BoxIterator & other)
  {
    m_loc = other.m_loc;
  }
__host__ __device__
  BoxIterator & operator=(const BoxIterator & other)
  {
    m_loc = other.m_loc;
    return *this;
  }

};


template <typename LatticeIt, typename Container>
class SubVolumeIterator : public thrust::iterator_adaptor<SubVolumeIterator<LatticeIt, Container>,
                                                          LatticeIt
                                                          >
{
  public:
    typedef thrust::iterator_adaptor<SubVolumeIterator<LatticeIt, Container>,
                                     LatticeIt
                                     >
        super_t;
    __host__
    SubVolumeIterator(const LatticeIt &It, Container &FAB, int N) : super_t(It),
                                                                    v(FAB.begin()),
                                                                    offset(N) {}
    friend class thrust::iterator_core_access;
  private:
    decltype(Container().begin()) v;
    int offset;
    __host__ __device__
        typename super_t::reference
       dereference() const
    {
      return *(v + offset); //+this->base().m_loc); // this gives an error: initial value of reference to non-const must be an lvalue
                                // when thrust::copy is used on a device_vector. Compiles fine with a host_vector.
    }
};

int main()
{
    thrust::host_vector<double> HV(100);
    thrust::device_vector<double> DV(100);
    thrust::device_vector<double> DV1(100);
    BoxIterator bit;

    SubVolumeIterator<decltype(bit), decltype(HV)> HIt(bit, HV, 1);

    SubVolumeIterator<decltype(bit), decltype(HV)> HIt_end(bit + 20, HV, 1);

    thrust::fill(HIt, HIt_end, 5.); // this compiles fine

    for (int i = 1; i < 21; ++i)
    {
      std::cout << HV[i] << std::endl;
    }
{
   SubVolumeIterator<decltype(DV.begin()), decltype(DV)> DIt(DV.begin(), DV, 5);

    SubVolumeIterator<decltype(DV.begin()), decltype(DV)> DIt_end(DV.begin() + 20, DV, 5);

    thrust::fill(DIt,DIt_end , -5.); // this compiles fine
}
{
    SubVolumeIterator<decltype(bit), decltype(DV)> DIt(bit, DV, 5);

    SubVolumeIterator<decltype(bit), decltype(DV)> DIt_end(bit + 20, DV, 5);

    thrust::fill(DIt,DIt_end , -5.); // this throws the error

    thrust::copy(DV.begin()+1,DV.begin()+21, HV.begin()+1); 

    for (int i = 1; i < 21; ++i)
    {
      std::cout << HV[i] << std::endl;
    }
}


return 0;
}

请不要通过指出使用推力提供的奇特迭代器来达到相同效果的方法来回答。我需要开发一个迭代器,以适应以预定义方式跨过晶格的类。这是产生错误的准系统代码。代码编译 用于在主机中定制的自适应迭代器,以及用于设备矢量的设备矢量的标准迭代器。当我在设备向量上使用自定义迭代器时,编译器在上例中指示的位置抛出错误“对非const的引用的初始值必须是左值”。 我用nvcc main.cu编译它。 nvcc版本9.0,gcc版本7.3.0,推力版本1.9.0

1 个答案:

答案 0 :(得分:1)

我想我找到了解决方案。问题与基本迭代器的成员类型的定义有关。用这个替换上面的代码

#include <thrust/iterator/iterator_adaptor.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
template <typename T>
struct BoxIterator
{

  unsigned int m_loc;
  typedef typename decltype(T().begin())::difference_type difference_type;
  typedef typename decltype(T().begin())::pointer pointer;
  typedef typename decltype(T().begin())::reference reference;
  typedef typename decltype(T().begin())::value_type value_type;
  typedef typename decltype(T().begin())::iterator_category iterator_category;

  __host__ __device__
  BoxIterator() : m_loc(0) {}

  __host__ __device__ void operator++()
  {
    ++m_loc;
  }
  __host__ __device__ void advance(int n)
  {
    m_loc += n;
  }
  __host__ __device__ void operator--()
  {
    advance(-1);
  }

  __host__ __device__ void operator+=(int n)
  {
    advance(n);
  }
  __host__ __device__ void begin()
  {
    m_loc = 0;
  }

  __host__ __device__ bool operator==(const BoxIterator<T> &other) const
  {
    return m_loc == other.m_loc;
  }

  __host__ __device__ bool equal(const BoxIterator<T> &other) const
  {
    return m_loc == other.m_loc;
  }

  __host__ __device__
      difference_type
      distance_to(const BoxIterator<T> &other) const
  {
    return other.m_loc - this->m_loc;
  }
  __host__ __device__
      BoxIterator<T>
      operator+(int n) const
  {
    BoxIterator<T> tmp = *this;
    tmp.m_loc += n;
    return tmp;
  }
  __host__ __device__
      BoxIterator<T>
      operator-(int n) const
  {
    BoxIterator<T> tmp = *this;
    tmp.m_loc -= n;
    return tmp;
  }
  __host__ __device__
      BoxIterator
      operator-(const BoxIterator<T> other) const
  {
    BoxIterator<T> tmp = *this;
    tmp.m_loc -= other.m_loc;
    return tmp;
  }
  __host__ __device__
  BoxIterator(const BoxIterator<T> &other)
  {
    m_loc = other.m_loc;
  }
  __host__ __device__
      BoxIterator &
      operator=(const BoxIterator<T> &other)
  {
    m_loc = other.m_loc;
    return *this;
  }


};

template <typename LatticeIt, typename Container>
class SubVolumeIterator : public thrust::iterator_adaptor<SubVolumeIterator<LatticeIt, Container>,
                                                          LatticeIt>
{
public:
  typedef thrust::iterator_adaptor<SubVolumeIterator<LatticeIt, Container>,
                                   LatticeIt>
      super_t;
  __host__
  SubVolumeIterator(const LatticeIt &It, Container &FAB, int N) : super_t(It),
                                                                  v(FAB.begin()),
                                                                  offset(N) {}
  friend class thrust::iterator_core_access;

private:
  decltype(Container().begin()) v;
  int offset;

  __host__ __device__
      typename super_t::reference
      dereference() const
  {
    return *(v + offset + this->base().m_loc); // this gives an error: initial value of reference to non-const must be an lvalue
                                               // when thrust::copy is used on a device_vector. Compiles fine with a host_vector.
  }

  __host__ __device__
      typename super_t::difference_type
      distance_to(const SubVolumeIterator<LatticeIt, Container> &other) const
  {
    return (other.base().m_loc - this->base().m_loc); //+this->base().m_loc); // this gives an error: initial value of reference to non-const must be an lvalue
                                                      // when thrust::copy is used on a device_vector. Compiles fine with a host_vector.
  }
};

int main()
{
  thrust::host_vector<double> HV(100);
  thrust::device_vector<double> DV(100);
  thrust::device_vector<double> DV1(100);
  {
    BoxIterator<thrust::host_vector<double>> bit;

    SubVolumeIterator<decltype(bit), decltype(HV)> HIt(bit, HV, 1);

    SubVolumeIterator<decltype(bit), decltype(HV)> HIt_end(bit + 20, HV, 1);

    thrust::fill(HIt, HIt_end, 5.); // this compiles fine

    for (int i = 1; i < 21; ++i)
    {
      std::cout << HV[i] << std::endl;
    }
  }

  {
    BoxIterator<thrust::device_vector<double>> bit;
    SubVolumeIterator<decltype(bit), decltype(DV)> DIt(bit, DV, 5);

    SubVolumeIterator<decltype(bit), decltype(DV)> DIt_end(bit + 20, DV, 5);

    thrust::fill(DIt, DIt_end, -5.); // this throws the error

    thrust::copy(DV.begin() + 1, DV.begin() + 21, HV.begin() + 1);

    for (int i = 1; i < 21; ++i)
    {
      std::cout << HV[i] << std::endl;
    }
  }

  return 0;
}

生成可正确编译并运行的代码。主要区别在于在基本BoxIterator中如何定义成员类型。 最初,我将它们定义为

typedef double value_type; 
typedef double& reference;
...

在代码中,我向基本迭代器添加了一个模板参数, 拿容器。所以现在我有

typedef typename decltype(T().begin())::value_type value_type;
...

这解决了编译问题。为什么原始代码无法编译仍然是我的一个谜,但这提供了解决方案。