vector.hpp
Go to the documentation of this file.
1 /*!
2  * \brief Implements the dense vector class for the gpu
3  *
4  * \author O. Krause
5  * \date 2016
6  *
7  *
8  * \par Copyright 1995-2015 Shark Development Team
9  *
10  * <BR><HR>
11  * This file is part of Shark.
12  * <http://image.diku.dk/shark/>
13  *
14  * Shark is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU Lesser General Public License as published
16  * by the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * Shark is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
26  *
27  */
28 #ifndef REMORA_GPU_VECTOR_HPP
29 #define REMORA_GPU_VECTOR_HPP
30 
31 #include "../detail/traits.hpp"
32 #include "../detail/vector_proxy_classes.hpp"
33 #include "../assignment.hpp"
34 #include <boost/compute/container/vector.hpp>
35 #include <boost/compute/algorithm/fill.hpp>
36 
37 namespace remora{
38 
39 namespace detail{
40 template<class Arg, class T>
41 struct induced_vector_element{
42  typedef T result_type;
43  Arg arg;
44  std::size_t stride;
45  boost::compute::buffer const& buffer;
46 };
47 
48 template<class Arg,class T>
49 boost::compute::detail::meta_kernel& operator<< (
50  boost::compute::detail::meta_kernel& k,
51  induced_vector_element<Arg, T> const& e
52 ){
53  return k << k.get_buffer_identifier<T>(e.buffer, boost::compute::memory_object::global_memory)
54  <<'['<<e.arg <<'*'<<e.stride<<']';
55 }
56 }
57 
58 /// \brief A dense vector of values of type \c T sored on the GPU
59 ///
60 /// For a \f$n\f$-dimensional vector \f$v\f$ and \f$0\leq i < n\f$ every element \f$v_i\f$ is mapped
61 /// to the \f$i\f$-th element of the container.
62 ///
63 /// \tparam T type of the objects stored in the vector (like int, double, complex,...)
64 template<class T>
65 class vector<T, gpu_tag>: public vector_container<vector<T, gpu_tag>, gpu_tag > {
66 public:
67  typedef T value_type;
68  typedef value_type const_reference;
69  typedef value_type reference;
70  typedef std::size_t size_type;
71 
72  typedef vector_reference<vector const> const_closure_type;
73  typedef vector_reference<vector> closure_type;
74  typedef gpu::dense_vector_storage<T,continuous_dense_tag> storage_type;
75  typedef gpu::dense_vector_storage<T,continuous_dense_tag> const_storage_type;
76  typedef elementwise<dense_tag> evaluation_category;
77 
78  // Construction and destruction
79 
80  /// \brief Constructor of a vector with a default queue
81  ///
82  ///note that for all operations for which vector is on the left hand side,
83  ///the kernels are enqueued on the supplied queue in case of a multi-queue setup.
84  vector(boost::compute::command_queue& queue = boost::compute::system::default_queue())
85  :m_storage(queue.get_context()), m_queue(&queue){}
86 
87  /// \brief Constructor of a vector with a predefined size
88  /// By default, its elements are uninitialized.
89  /// \param size initial size of the vector
90  /// \param queue the opencl queue to use by this vector
91  explicit vector(size_type size, boost::compute::command_queue& queue = boost::compute::system::default_queue())
92  : m_storage(size,queue.get_context()), m_queue(&queue){}
93 
94  /// \brief Constructor of a vector with a predefined size and a unique initial value
95  /// \param size of the vector
96  /// \param init value to assign to each element of the vector
97  /// \param queue the opencl queue to use by this vector
98  vector(size_type size, value_type const& init, boost::compute::command_queue& queue = boost::compute::system::default_queue())
99  : m_storage(size, init, queue), m_queue(&queue){}
100 
101  /// \brief Move-constructor of a vector
102  /// \param v is the vector to be moved
103  vector(vector && v)
104  : m_storage(std::move(v.m_storage))
105  , m_queue(&v.queue()){}
106 
107  /// \brief Copy-constructor of a vector
108  /// \param v is the vector to be duplicated
109  vector(vector const& v) = default;
110 
111  /// \brief Copy-constructor of a vector from a vector_expression
112  /// \param e the vector_expression whose values will be duplicated into the vector
113  template<class E>
114  vector(vector_expression<E, gpu_tag> const& e)
115  : m_storage(e().size(), e().queue().get_context())
116  , m_queue(&e().queue()){
117  assign(*this, e);
118  }
119 
120  /// \brief Copy-constructor of a vector from a vector_expression on a given queue
121  /// \param e the vector_expression whose values will be duplicated into the vector
122  /// \param queue the queue which should perform the task
123  template<class E>
124  vector(vector_expression<E, gpu_tag> const& e, boost::compute::command_queue& queue)
125  : m_storage(e().size(), queue.get_context())
126  , m_queue(&queue){
127  assign(*this, e);
128  }
129 
130  // Element access
131  template <class IndexExpr>
132  detail::induced_vector_element<IndexExpr,T> operator()(IndexExpr const& i) const {
133  return {i,1,m_storage.get_buffer()};
134  }
135 
136  // -------------------
137  // Assignment operators
138  // -------------------
139 
140  /// \brief Assign a full vector (\e RHS-vector) to the current vector (\e LHS-vector)
141  /// Assign a full vector (\e RHS-vector) to the current vector (\e LHS-vector). This method does not create any temporary.
142  /// \param v is the source vector container
143  /// \return a reference to a vector (i.e. the destination vector)
144  vector& operator = (vector const& v){
145  resize(v.size());
146  assign(*this, v);
147  }
148 
149  /// \brief Move-Assign a full vector (\e RHS-vector) to the current vector (\e LHS-vector)
150  /// \param v is the source vector container
151  /// \return a reference to a vector (i.e. the destination vector)
152  vector& operator = (vector && v){
153  m_storage = std::move(v.m_storage);
154  m_queue = v.m_queue;
155  return *this;
156  }
157 
158  /// \brief Assign a full vector (\e RHS-vector) to the current vector (\e LHS-vector)
159  /// Assign a full vector (\e RHS-vector) to the current vector (\e LHS-vector). This method does not create any temporary.
160  /// \param v is the source vector container
161  /// \return a reference to a vector (i.e. the destination vector)
162  template<class C> // Container assignment without temporary
163  vector& operator = (vector_container<C, gpu_tag> const& v) {
164  resize(v().size());
165  return assign(*this, v);
166  }
167 
168  /// \brief Assign the result of a vector_expression to the vector
169  ///
170  /// \param e is a const reference to the vector_expression
171  /// \return a reference to the resulting vector
172  template<class E>
173  vector& operator = (vector_expression<E, gpu_tag> const& e) {
174  vector temporary(e,queue());
175  swap(*this,temporary);
176  return *this;
177  }
178 
179  // ---------
180  // Storage interface
181  // ---------
182 
183  /// \brief Return the size of the vector.
184  size_type size() const {
185  return m_storage.size();
186  }
187 
188  boost::compute::command_queue& queue() const{
189  return *m_queue;
190  }
191  ///\brief Returns the underlying storage structure for low level access
192  const_storage_type raw_storage() const{
193  return {m_storage.get_buffer(),0,1};
194  }
195 
196  ///\brief Returns the underlying storage structure for low level access
197  storage_type raw_storage(){
198  return {m_storage.get_buffer(),0,1};
199  }
200 
201  /// \brief Resize the vector
202  ///
203  /// This might erase all data stored in the vector
204  ///
205  /// \param size new size of the vector
206  void resize(size_type size) {
207  if(size < m_storage.size())
208  m_storage.resize(size);
209  else
210  m_storage = boost::compute::vector<T>(size, queue().get_context());
211  }
212 
213  /// \brief Resize the vector
214  ///
215  /// This will erase all data stored in the vector and reinitialize it with the supplied value of init
216  ///
217  /// \param size new size of the vector
218  /// \param init the value of all elements
219  void resize(size_type size, value_type init) {
220  resize(size);
221  boost::compute::fill(m_storage.begin(),m_storage.end(), init);
222  }
223 
224  void clear(){
225  boost::compute::fill(m_storage.begin(),m_storage.end(), value_type/*zero*/());
226  }
227 
228  bool empty()const{
229  return m_storage.empty();
230  }
231 
232  /// \brief Swap the content of two vectors
233  /// \param v1 is the first vector. It takes values from v2
234  /// \param v2 is the second vector It takes values from v1
235  friend void swap(vector& v1, vector& v2) {
236  using std::swap;
237  swap(v1.m_storage,v2.m_storage);
238  std::swap(v2.m_queue,v2.m_queue);
239  }
240 
241  // Iterator types
242  typedef typename boost::compute::vector<T>::iterator iterator;
243  typedef typename boost::compute::vector<T>::const_iterator const_iterator;
244 
245  /// \brief return an iterator on the first element of the vector
246  const_iterator cbegin() const {
247  return m_storage.begin();
248  }
249 
250  /// \brief return an iterator after the last element of the vector
251  const_iterator cend() const {
252  return m_storage.end();
253  }
254 
255  /// \brief return an iterator on the first element of the vector
256  const_iterator begin() const {
257  return cbegin();
258  }
259 
260  /// \brief return an iterator after the last element of the vector
261  const_iterator end() const {
262  return cend();
263  }
264 
265  /// \brief Return an iterator on the first element of the vector
266  iterator begin(){
267  return m_storage.begin();
268  }
269 
270  /// \brief Return an iterator at the end of the vector
271  iterator end(){
272  return m_storage.end();
273  }
274 
275 private:
276  boost::compute::vector<T> m_storage;
277  boost::compute::command_queue* m_queue;
278 };
279 }
280 
281 #endif