]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/compute/container/vector.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / compute / container / vector.hpp
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
10
11 #ifndef BOOST_COMPUTE_CONTAINER_VECTOR_HPP
12 #define BOOST_COMPUTE_CONTAINER_VECTOR_HPP
13
14 #include <vector>
15 #include <cstddef>
16 #include <iterator>
17 #include <exception>
18
19 #include <boost/throw_exception.hpp>
20
21 #include <boost/compute/config.hpp>
22
23 #ifndef BOOST_COMPUTE_NO_HDR_INITIALIZER_LIST
24 #include <initializer_list>
25 #endif
26
27 #include <boost/compute/buffer.hpp>
28 #include <boost/compute/device.hpp>
29 #include <boost/compute/system.hpp>
30 #include <boost/compute/context.hpp>
31 #include <boost/compute/command_queue.hpp>
32 #include <boost/compute/algorithm/copy.hpp>
33 #include <boost/compute/algorithm/copy_n.hpp>
34 #include <boost/compute/algorithm/fill_n.hpp>
35 #include <boost/compute/allocator/buffer_allocator.hpp>
36 #include <boost/compute/iterator/buffer_iterator.hpp>
37 #include <boost/compute/type_traits/detail/capture_traits.hpp>
38 #include <boost/compute/detail/buffer_value.hpp>
39 #include <boost/compute/detail/iterator_range_size.hpp>
40
41 namespace boost {
42 namespace compute {
43
44 /// \class vector
45 /// \brief A resizable array of values.
46 ///
47 /// The vector<T> class stores a dynamic array of values. Internally, the data
48 /// is stored in an OpenCL buffer object.
49 ///
50 /// The vector class is the prefered container for storing and accessing data
51 /// on a compute device. In most cases it should be used instead of directly
52 /// dealing with buffer objects. If the undelying buffer is needed, it can be
53 /// accessed with the get_buffer() method.
54 ///
55 /// The internal storage is allocated in a specific OpenCL context which is
56 /// passed as an argument to the constructor when the vector is created.
57 ///
58 /// For example, to create a vector on the device containing space for ten
59 /// \c int values:
60 /// \code
61 /// boost::compute::vector<int> vec(10, context);
62 /// \endcode
63 ///
64 /// Allocation and data transfer can also be performed in a single step:
65 /// \code
66 /// // values on the host
67 /// int data[] = { 1, 2, 3, 4 };
68 ///
69 /// // create a vector of size four and copy the values from data
70 /// boost::compute::vector<int> vec(data, data + 4, queue);
71 /// \endcode
72 ///
73 /// The Boost.Compute \c vector class provides a STL-like API and is modeled
74 /// after the \c std::vector class from the C++ standard library. It can be
75 /// used with any of the STL-like algorithms provided by Boost.Compute
76 /// including \c copy(), \c transform(), and \c sort() (among many others).
77 ///
78 /// For example:
79 /// \code
80 /// // a vector on a compute device
81 /// boost::compute::vector<float> vec = ...
82 ///
83 /// // copy data to the vector from a host std:vector
84 /// boost::compute::copy(host_vec.begin(), host_vec.end(), vec.begin(), queue);
85 ///
86 /// // copy data from the vector to a host std::vector
87 /// boost::compute::copy(vec.begin(), vec.end(), host_vec.begin(), queue);
88 ///
89 /// // sort the values in the vector
90 /// boost::compute::sort(vec.begin(), vec.end(), queue);
91 ///
92 /// // calculate the sum of the values in the vector (also see reduce())
93 /// float sum = boost::compute::accumulate(vec.begin(), vec.end(), 0, queue);
94 ///
95 /// // reverse the values in the vector
96 /// boost::compute::reverse(vec.begin(), vec.end(), queue);
97 ///
98 /// // fill the vector with ones
99 /// boost::compute::fill(vec.begin(), vec.end(), 1, queue);
100 /// \endcode
101 ///
102 /// \see \ref array "array<T, N>", buffer
103 template<class T, class Alloc = buffer_allocator<T> >
104 class vector
105 {
106 public:
107 typedef T value_type;
108 typedef Alloc allocator_type;
109 typedef typename allocator_type::size_type size_type;
110 typedef typename allocator_type::difference_type difference_type;
111 typedef detail::buffer_value<T> reference;
112 typedef const detail::buffer_value<T> const_reference;
113 typedef typename allocator_type::pointer pointer;
114 typedef typename allocator_type::const_pointer const_pointer;
115 typedef buffer_iterator<T> iterator;
116 typedef buffer_iterator<T> const_iterator;
117 typedef std::reverse_iterator<iterator> reverse_iterator;
118 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
119
120 /// Creates an empty vector in \p context.
121 explicit vector(const context &context = system::default_context())
122 : m_size(0),
123 m_allocator(context)
124 {
125 m_data = m_allocator.allocate(_minimum_capacity());
126 }
127
128 /// Creates a vector with space for \p count elements in \p context.
129 ///
130 /// Note that unlike \c std::vector's constructor, this will not initialize
131 /// the values in the container. Either call the vector constructor which
132 /// takes a value to initialize with or use the fill() algorithm to set
133 /// the initial values.
134 ///
135 /// For example:
136 /// \code
137 /// // create a vector on the device with space for ten ints
138 /// boost::compute::vector<int> vec(10, context);
139 /// \endcode
140 explicit vector(size_type count,
141 const context &context = system::default_context())
142 : m_size(count),
143 m_allocator(context)
144 {
145 m_data = m_allocator.allocate((std::max)(count, _minimum_capacity()));
146 }
147
148 /// Creates a vector with space for \p count elements and sets each equal
149 /// to \p value.
150 ///
151 /// For example:
152 /// \code
153 /// // creates a vector with four values set to nine (e.g. [9, 9, 9, 9]).
154 /// boost::compute::vector<int> vec(4, 9, queue);
155 /// \endcode
156 vector(size_type count,
157 const T &value,
158 command_queue &queue = system::default_queue())
159 : m_size(count),
160 m_allocator(queue.get_context())
161 {
162 m_data = m_allocator.allocate((std::max)(count, _minimum_capacity()));
163
164 ::boost::compute::fill_n(begin(), count, value, queue);
165 }
166
167 /// Creates a vector with space for the values in the range [\p first,
168 /// \p last) and copies them into the vector with \p queue.
169 ///
170 /// For example:
171 /// \code
172 /// // values on the host
173 /// int data[] = { 1, 2, 3, 4 };
174 ///
175 /// // create a vector of size four and copy the values from data
176 /// boost::compute::vector<int> vec(data, data + 4, queue);
177 /// \endcode
178 template<class InputIterator>
179 vector(InputIterator first,
180 InputIterator last,
181 command_queue &queue = system::default_queue())
182 : m_size(detail::iterator_range_size(first, last)),
183 m_allocator(queue.get_context())
184 {
185 m_data = m_allocator.allocate((std::max)(m_size, _minimum_capacity()));
186
187 ::boost::compute::copy(first, last, begin(), queue);
188 }
189
190 /// Creates a new vector and copies the values from \p other.
191 vector(const vector &other,
192 command_queue &queue = system::default_queue())
193 : m_size(other.m_size),
194 m_allocator(other.m_allocator)
195 {
196 m_data = m_allocator.allocate((std::max)(m_size, _minimum_capacity()));
197
198 if(!other.empty()){
199 if(other.get_buffer().get_context() != queue.get_context()){
200 command_queue other_queue = other.default_queue();
201 ::boost::compute::copy(other.begin(), other.end(), begin(), other_queue);
202 other_queue.finish();
203 }
204 else {
205 ::boost::compute::copy(other.begin(), other.end(), begin(), queue);
206 queue.finish();
207 }
208 }
209 }
210
211 /// Creates a new vector and copies the values from \p other.
212 template<class OtherAlloc>
213 vector(const vector<T, OtherAlloc> &other,
214 command_queue &queue = system::default_queue())
215 : m_size(other.size()),
216 m_allocator(queue.get_context())
217 {
218 m_data = m_allocator.allocate((std::max)(m_size, _minimum_capacity()));
219
220 if(!other.empty()){
221 ::boost::compute::copy(other.begin(), other.end(), begin(), queue);
222 queue.finish();
223 }
224 }
225
226 /// Creates a new vector and copies the values from \p vector.
227 template<class OtherAlloc>
228 vector(const std::vector<T, OtherAlloc> &vector,
229 command_queue &queue = system::default_queue())
230 : m_size(vector.size()),
231 m_allocator(queue.get_context())
232 {
233 m_data = m_allocator.allocate((std::max)(m_size, _minimum_capacity()));
234
235 ::boost::compute::copy(vector.begin(), vector.end(), begin(), queue);
236 }
237
238 #ifndef BOOST_COMPUTE_NO_HDR_INITIALIZER_LIST
239 vector(std::initializer_list<T> list,
240 command_queue &queue = system::default_queue())
241 : m_size(list.size()),
242 m_allocator(queue.get_context())
243 {
244 m_data = m_allocator.allocate((std::max)(m_size, _minimum_capacity()));
245
246 ::boost::compute::copy(list.begin(), list.end(), begin(), queue);
247 }
248 #endif // BOOST_COMPUTE_NO_HDR_INITIALIZER_LIST
249
250 vector& operator=(const vector &other)
251 {
252 if(this != &other){
253 command_queue queue = default_queue();
254 resize(other.size(), queue);
255 ::boost::compute::copy(other.begin(), other.end(), begin(), queue);
256 queue.finish();
257 }
258
259 return *this;
260 }
261
262 template<class OtherAlloc>
263 vector& operator=(const vector<T, OtherAlloc> &other)
264 {
265 command_queue queue = default_queue();
266 resize(other.size(), queue);
267 ::boost::compute::copy(other.begin(), other.end(), begin(), queue);
268 queue.finish();
269
270 return *this;
271 }
272
273 template<class OtherAlloc>
274 vector& operator=(const std::vector<T, OtherAlloc> &vector)
275 {
276 command_queue queue = default_queue();
277 resize(vector.size(), queue);
278 ::boost::compute::copy(vector.begin(), vector.end(), begin(), queue);
279 queue.finish();
280 return *this;
281 }
282
283 #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES
284 /// Move-constructs a new vector from \p other.
285 vector(vector&& other)
286 : m_data(std::move(other.m_data)),
287 m_size(other.m_size),
288 m_allocator(std::move(other.m_allocator))
289 {
290 other.m_size = 0;
291 }
292
293 /// Move-assigns the data from \p other to \c *this.
294 vector& operator=(vector&& other)
295 {
296 if(capacity() > 0){
297 m_allocator.deallocate(m_data, capacity());
298 }
299
300 m_data = std::move(other.m_data);
301 m_size = other.m_size;
302 m_allocator = std::move(other.m_allocator);
303
304 other.m_size = 0;
305
306 return *this;
307 }
308 #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
309
310 /// Destroys the vector object.
311 ~vector()
312 {
313 if(capacity() > 0){
314 m_allocator.deallocate(m_data, capacity());
315 }
316 }
317
318 iterator begin()
319 {
320 return ::boost::compute::make_buffer_iterator<T>(m_data.get_buffer(), 0);
321 }
322
323 const_iterator begin() const
324 {
325 return ::boost::compute::make_buffer_iterator<T>(m_data.get_buffer(), 0);
326 }
327
328 const_iterator cbegin() const
329 {
330 return begin();
331 }
332
333 iterator end()
334 {
335 return ::boost::compute::make_buffer_iterator<T>(m_data.get_buffer(), m_size);
336 }
337
338 const_iterator end() const
339 {
340 return ::boost::compute::make_buffer_iterator<T>(m_data.get_buffer(), m_size);
341 }
342
343 const_iterator cend() const
344 {
345 return end();
346 }
347
348 reverse_iterator rbegin()
349 {
350 return reverse_iterator(end() - 1);
351 }
352
353 const_reverse_iterator rbegin() const
354 {
355 return reverse_iterator(end() - 1);
356 }
357
358 const_reverse_iterator crbegin() const
359 {
360 return rbegin();
361 }
362
363 reverse_iterator rend()
364 {
365 return reverse_iterator(begin() - 1);
366 }
367
368 const_reverse_iterator rend() const
369 {
370 return reverse_iterator(begin() - 1);
371 }
372
373 const_reverse_iterator crend() const
374 {
375 return rend();
376 }
377
378 /// Returns the number of elements in the vector.
379 size_type size() const
380 {
381 return m_size;
382 }
383
384 size_type max_size() const
385 {
386 return m_allocator.max_size();
387 }
388
389 /// Resizes the vector to \p size.
390 void resize(size_type size, command_queue &queue)
391 {
392 if(size <= capacity()){
393 m_size = size;
394 }
395 else {
396 // allocate new buffer
397 pointer new_data =
398 m_allocator.allocate(
399 static_cast<size_type>(
400 static_cast<float>(size) * _growth_factor()
401 )
402 );
403
404 if(capacity() > 0)
405 {
406 // copy old values to the new buffer
407 ::boost::compute::copy(m_data, m_data + m_size, new_data, queue);
408
409 // free old memory
410 m_allocator.deallocate(m_data, capacity());
411 }
412
413 // set new data and size
414 m_data = new_data;
415 m_size = size;
416 }
417 }
418
419 /// \overload
420 void resize(size_type size)
421 {
422 command_queue queue = default_queue();
423 resize(size, queue);
424 queue.finish();
425 }
426
427 /// Returns \c true if the vector is empty.
428 bool empty() const
429 {
430 return m_size == 0;
431 }
432
433 /// Returns the capacity of the vector.
434 size_type capacity() const
435 {
436 if(m_data == pointer()) // null pointer check
437 {
438 return 0;
439 }
440 return m_data.get_buffer().size() / sizeof(T);
441 }
442
443 void reserve(size_type size, command_queue &queue)
444 {
445 if(size > max_size()){
446 throw std::length_error("vector::reserve");
447 }
448 if(capacity() < size){
449 // allocate new buffer
450 pointer new_data =
451 m_allocator.allocate(
452 static_cast<size_type>(
453 static_cast<float>(size) * _growth_factor()
454 )
455 );
456
457 if(capacity() > 0)
458 {
459 // copy old values to the new buffer
460 ::boost::compute::copy(m_data, m_data + m_size, new_data, queue);
461
462 // free old memory
463 m_allocator.deallocate(m_data, capacity());
464 }
465
466 // set new data
467 m_data = new_data;
468 }
469 }
470
471 void reserve(size_type size)
472 {
473 command_queue queue = default_queue();
474 reserve(size, queue);
475 queue.finish();
476 }
477
478 void shrink_to_fit(command_queue &queue)
479 {
480 pointer old_data = m_data;
481 m_data = pointer(); // null pointer
482 if(m_size > 0)
483 {
484 // allocate new buffer
485 m_data = m_allocator.allocate(m_size);
486
487 // copy old values to the new buffer
488 ::boost::compute::copy(old_data, old_data + m_size, m_data, queue);
489 }
490
491 if(capacity() > 0)
492 {
493 // free old memory
494 m_allocator.deallocate(old_data, capacity());
495 }
496 }
497
498 void shrink_to_fit()
499 {
500 command_queue queue = default_queue();
501 shrink_to_fit(queue);
502 queue.finish();
503 }
504
505 reference operator[](size_type index)
506 {
507 return *(begin() + static_cast<difference_type>(index));
508 }
509
510 const_reference operator[](size_type index) const
511 {
512 return *(begin() + static_cast<difference_type>(index));
513 }
514
515 reference at(size_type index)
516 {
517 if(index >= size()){
518 BOOST_THROW_EXCEPTION(std::out_of_range("index out of range"));
519 }
520
521 return operator[](index);
522 }
523
524 const_reference at(size_type index) const
525 {
526 if(index >= size()){
527 BOOST_THROW_EXCEPTION(std::out_of_range("index out of range"));
528 }
529
530 return operator[](index);
531 }
532
533 reference front()
534 {
535 return *begin();
536 }
537
538 const_reference front() const
539 {
540 return *begin();
541 }
542
543 reference back()
544 {
545 return *(end() - static_cast<difference_type>(1));
546 }
547
548 const_reference back() const
549 {
550 return *(end() - static_cast<difference_type>(1));
551 }
552
553 template<class InputIterator>
554 void assign(InputIterator first,
555 InputIterator last,
556 command_queue &queue)
557 {
558 // resize vector for new contents
559 resize(detail::iterator_range_size(first, last), queue);
560
561 // copy values into the vector
562 ::boost::compute::copy(first, last, begin(), queue);
563 }
564
565 template<class InputIterator>
566 void assign(InputIterator first, InputIterator last)
567 {
568 command_queue queue = default_queue();
569 assign(first, last, queue);
570 queue.finish();
571 }
572
573 void assign(size_type n, const T &value, command_queue &queue)
574 {
575 // resize vector for new contents
576 resize(n, queue);
577
578 // fill vector with value
579 ::boost::compute::fill_n(begin(), n, value, queue);
580 }
581
582 void assign(size_type n, const T &value)
583 {
584 command_queue queue = default_queue();
585 assign(n, value, queue);
586 queue.finish();
587 }
588
589 /// Inserts \p value at the end of the vector (resizing if neccessary).
590 ///
591 /// Note that calling \c push_back() to insert data values one at a time
592 /// is inefficient as there is a non-trivial overhead in performing a data
593 /// transfer to the device. It is usually better to store a set of values
594 /// on the host (for example, in a \c std::vector) and then transfer them
595 /// in bulk using the \c insert() method or the copy() algorithm.
596 void push_back(const T &value, command_queue &queue)
597 {
598 insert(end(), value, queue);
599 }
600
601 /// \overload
602 void push_back(const T &value)
603 {
604 command_queue queue = default_queue();
605 push_back(value, queue);
606 queue.finish();
607 }
608
609 void pop_back(command_queue &queue)
610 {
611 resize(size() - 1, queue);
612 }
613
614 void pop_back()
615 {
616 command_queue queue = default_queue();
617 pop_back(queue);
618 queue.finish();
619 }
620
621 iterator insert(iterator position, const T &value, command_queue &queue)
622 {
623 if(position == end()){
624 resize(m_size + 1, queue);
625 position = begin() + position.get_index();
626 ::boost::compute::copy_n(&value, 1, position, queue);
627 }
628 else {
629 ::boost::compute::vector<T, Alloc> tmp(position, end(), queue);
630 resize(m_size + 1, queue);
631 position = begin() + position.get_index();
632 ::boost::compute::copy_n(&value, 1, position, queue);
633 ::boost::compute::copy(tmp.begin(), tmp.end(), position + 1, queue);
634 }
635
636 return position + 1;
637 }
638
639 iterator insert(iterator position, const T &value)
640 {
641 command_queue queue = default_queue();
642 iterator iter = insert(position, value, queue);
643 queue.finish();
644 return iter;
645 }
646
647 void insert(iterator position,
648 size_type count,
649 const T &value,
650 command_queue &queue)
651 {
652 ::boost::compute::vector<T, Alloc> tmp(position, end(), queue);
653 resize(size() + count, queue);
654
655 position = begin() + position.get_index();
656
657 ::boost::compute::fill_n(position, count, value, queue);
658 ::boost::compute::copy(
659 tmp.begin(),
660 tmp.end(),
661 position + static_cast<difference_type>(count),
662 queue
663 );
664 }
665
666 void insert(iterator position, size_type count, const T &value)
667 {
668 command_queue queue = default_queue();
669 insert(position, count, value, queue);
670 queue.finish();
671 }
672
673 /// Inserts the values in the range [\p first, \p last) into the vector at
674 /// \p position using \p queue.
675 template<class InputIterator>
676 void insert(iterator position,
677 InputIterator first,
678 InputIterator last,
679 command_queue &queue)
680 {
681 ::boost::compute::vector<T, Alloc> tmp(position, end(), queue);
682
683 size_type count = detail::iterator_range_size(first, last);
684 resize(size() + count, queue);
685
686 position = begin() + position.get_index();
687
688 ::boost::compute::copy(first, last, position, queue);
689 ::boost::compute::copy(
690 tmp.begin(),
691 tmp.end(),
692 position + static_cast<difference_type>(count),
693 queue
694 );
695 }
696
697 /// \overload
698 template<class InputIterator>
699 void insert(iterator position, InputIterator first, InputIterator last)
700 {
701 command_queue queue = default_queue();
702 insert(position, first, last, queue);
703 queue.finish();
704 }
705
706 iterator erase(iterator position, command_queue &queue)
707 {
708 return erase(position, position + 1, queue);
709 }
710
711 iterator erase(iterator position)
712 {
713 command_queue queue = default_queue();
714 iterator iter = erase(position, queue);
715 queue.finish();
716 return iter;
717 }
718
719 iterator erase(iterator first, iterator last, command_queue &queue)
720 {
721 if(last != end()){
722 ::boost::compute::vector<T, Alloc> tmp(last, end(), queue);
723 ::boost::compute::copy(tmp.begin(), tmp.end(), first, queue);
724 }
725
726 difference_type count = std::distance(first, last);
727 resize(size() - static_cast<size_type>(count), queue);
728
729 return begin() + first.get_index() + count;
730 }
731
732 iterator erase(iterator first, iterator last)
733 {
734 command_queue queue = default_queue();
735 iterator iter = erase(first, last, queue);
736 queue.finish();
737 return iter;
738 }
739
740 /// Swaps the contents of \c *this with \p other.
741 void swap(vector &other)
742 {
743 std::swap(m_data, other.m_data);
744 std::swap(m_size, other.m_size);
745 std::swap(m_allocator, other.m_allocator);
746 }
747
748 /// Removes all elements from the vector.
749 void clear()
750 {
751 m_size = 0;
752 }
753
754 allocator_type get_allocator() const
755 {
756 return m_allocator;
757 }
758
759 /// Returns the underlying buffer.
760 const buffer& get_buffer() const
761 {
762 return m_data.get_buffer();
763 }
764
765 /// \internal_
766 ///
767 /// Returns a command queue usable to issue commands for the vector's
768 /// memory buffer. This is used when a member function is called without
769 /// specifying an existing command queue to use.
770 command_queue default_queue() const
771 {
772 const context &context = m_allocator.get_context();
773 command_queue queue(context, context.get_device());
774 return queue;
775 }
776
777 private:
778 /// \internal_
779 BOOST_CONSTEXPR size_type _minimum_capacity() const { return 4; }
780
781 /// \internal_
782 BOOST_CONSTEXPR float _growth_factor() const { return 1.5; }
783
784 private:
785 pointer m_data;
786 size_type m_size;
787 allocator_type m_allocator;
788 };
789
790 namespace detail {
791
792 // set_kernel_arg specialization for vector<T>
793 template<class T, class Alloc>
794 struct set_kernel_arg<vector<T, Alloc> >
795 {
796 void operator()(kernel &kernel_, size_t index, const vector<T, Alloc> &vector)
797 {
798 kernel_.set_arg(index, vector.get_buffer());
799 }
800 };
801
802 // for capturing vector<T> with BOOST_COMPUTE_CLOSURE()
803 template<class T, class Alloc>
804 struct capture_traits<vector<T, Alloc> >
805 {
806 static std::string type_name()
807 {
808 return std::string("__global ") + ::boost::compute::type_name<T>() + "*";
809 }
810 };
811
812 // meta_kernel streaming operator for vector<T>
813 template<class T, class Alloc>
814 meta_kernel& operator<<(meta_kernel &k, const vector<T, Alloc> &vector)
815 {
816 return k << k.get_buffer_identifier<T>(vector.get_buffer());
817 }
818
819 } // end detail namespace
820 } // end compute namespace
821 } // end boost namespace
822
823 #endif // BOOST_COMPUTE_CONTAINER_VECTOR_HPP