xgboost
span.h
Go to the documentation of this file.
1 
29 #ifndef XGBOOST_SPAN_H_
30 #define XGBOOST_SPAN_H_
31 
32 #include <xgboost/base.h>
33 #include <xgboost/logging.h>
34 
35 #include <cinttypes> // size_t
36 #include <limits> // numeric_limits
37 #include <iterator>
38 #include <type_traits>
39 #include <cstdio>
40 
41 #if defined(__CUDACC__)
42 #include <cuda_runtime.h>
43 #endif // defined(__CUDACC__)
44 
62 #if defined(_MSC_VER) && _MSC_VER < 1910
63 
64 #define __span_noexcept
65 
66 #pragma push_macro("constexpr")
67 #define constexpr /*constexpr*/
68 
69 #else
70 
71 #define __span_noexcept noexcept
72 
73 #endif // defined(_MSC_VER) && _MSC_VER < 1910
74 
75 namespace xgboost {
76 namespace common {
77 
78 #if defined(__CUDA_ARCH__)
79 // Usual logging facility is not available inside device code.
80 
81 #if defined(_MSC_VER)
82 
83 // Windows CUDA doesn't have __assert_fail.
84 #define CUDA_KERNEL_CHECK(cond) \
85  do { \
86  if (XGBOOST_EXPECT(!(cond), false)) { \
87  asm("trap;"); \
88  } \
89  } while (0)
90 
91 #else // defined(_MSC_VER)
92 
93 #define __ASSERT_STR_HELPER(x) #x
94 
95 #define CUDA_KERNEL_CHECK(cond) \
96  (XGBOOST_EXPECT((cond), true) \
97  ? static_cast<void>(0) \
98  : __assert_fail(__ASSERT_STR_HELPER((cond)), __FILE__, __LINE__, __PRETTY_FUNCTION__))
99 
100 #endif // defined(_MSC_VER)
101 
102 #define KERNEL_CHECK CUDA_KERNEL_CHECK
103 
104 #define SPAN_CHECK KERNEL_CHECK
105 
106 #else // ------------------------------ not CUDA ----------------------------
107 
108 #if defined(XGBOOST_STRICT_R_MODE) && XGBOOST_STRICT_R_MODE == 1
109 
110 #define KERNEL_CHECK(cond)
111 
112 #define SPAN_CHECK(cond) KERNEL_CHECK(cond)
113 
114 #else
115 
116 #define KERNEL_CHECK(cond) (XGBOOST_EXPECT((cond), true) ? static_cast<void>(0) : std::terminate())
117 
118 #define SPAN_CHECK(cond) KERNEL_CHECK(cond)
119 
120 #endif // defined(XGBOOST_STRICT_R_MODE)
121 
122 #endif // __CUDA_ARCH__
123 
124 #define SPAN_LT(lhs, rhs) SPAN_CHECK((lhs) < (rhs))
125 
126 namespace detail {
133 using ptrdiff_t = typename std::conditional< // NOLINT
134  std::is_same<std::ptrdiff_t, std::int64_t>::value,
135  std::ptrdiff_t, std::int64_t>::type;
136 } // namespace detail
137 
138 #if defined(_MSC_VER) && _MSC_VER < 1910
139 constexpr const std::size_t
140 dynamic_extent = std::numeric_limits<std::size_t>::max(); // NOLINT
141 #else
142 constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max(); // NOLINT
143 #endif // defined(_MSC_VER) && _MSC_VER < 1910
144 
145 enum class byte : unsigned char {}; // NOLINT
146 
147 template <class ElementType, std::size_t Extent>
148 class Span;
149 
150 namespace detail {
151 
152 template <typename SpanType, bool IsConst>
154  using ElementType = typename SpanType::element_type;
155 
156  public:
157  using iterator_category = std::random_access_iterator_tag; // NOLINT
158  using value_type = typename SpanType::value_type; // NOLINT
160 
161  using reference = typename std::conditional< // NOLINT
162  IsConst, const ElementType, ElementType>::type&;
163  using pointer = typename std::add_pointer<reference>::type; // NOLINT
164 
165  constexpr SpanIterator() = default;
166 
168  const SpanType* _span,
169  typename SpanType::index_type _idx) __span_noexcept :
170  span_(_span), index_(_idx) {}
171 
173  template <bool B, typename std::enable_if<!B && IsConst>::type* = nullptr>
174  XGBOOST_DEVICE constexpr SpanIterator( // NOLINT
176  : SpanIterator(other_.span_, other_.index_) {}
177 
179  SPAN_CHECK(index_ < span_->size());
180  return *(span_->data() + index_);
181  }
183  return *(*this + n);
184  }
185 
187  SPAN_CHECK(index_ != span_->size());
188  return span_->data() + index_;
189  }
190 
192  SPAN_CHECK(index_ != span_->size());
193  index_++;
194  return *this;
195  }
196 
198  auto ret = *this;
199  ++(*this);
200  return ret;
201  }
202 
204  SPAN_CHECK(index_ != 0 && index_ <= span_->size());
205  index_--;
206  return *this;
207  }
208 
210  auto ret = *this;
211  --(*this);
212  return ret;
213  }
214 
216  auto ret = *this;
217  return ret += n;
218  }
219 
221  SPAN_CHECK((index_ + n) <= span_->size());
222  index_ += n;
223  return *this;
224  }
225 
227  SPAN_CHECK(span_ == rhs.span_);
228  return index_ - rhs.index_;
229  }
230 
232  auto ret = *this;
233  return ret -= n;
234  }
235 
237  return *this += -n;
238  }
239 
240  // friends
241  XGBOOST_DEVICE constexpr friend bool operator==(
243  return _lhs.span_ == _rhs.span_ && _lhs.index_ == _rhs.index_;
244  }
245 
246  XGBOOST_DEVICE constexpr friend bool operator!=(
248  return !(_lhs == _rhs);
249  }
250 
251  XGBOOST_DEVICE constexpr friend bool operator<(
253  return _lhs.index_ < _rhs.index_;
254  }
255 
256  XGBOOST_DEVICE constexpr friend bool operator<=(
258  return !(_rhs < _lhs);
259  }
260 
261  XGBOOST_DEVICE constexpr friend bool operator>(
263  return _rhs < _lhs;
264  }
265 
266  XGBOOST_DEVICE constexpr friend bool operator>=(
268  return !(_rhs > _lhs);
269  }
270 
271  protected:
272  const SpanType *span_ { nullptr };
273  typename SpanType::index_type index_ { 0 };
274 };
275 
276 
277 // It's tempting to use constexpr instead of structs to do the following meta
278 // programming. But remember that we are supporting MSVC 2013 here.
279 
287 template <std::size_t Extent, std::size_t Offset, std::size_t Count>
288 struct ExtentValue : public std::integral_constant<
289  std::size_t, Count != dynamic_extent ?
290  Count : (Extent != dynamic_extent ? Extent - Offset : Extent)> {};
291 
296 template <typename T, std::size_t Extent>
297 struct ExtentAsBytesValue : public std::integral_constant<
298  std::size_t,
299  Extent == dynamic_extent ?
300  Extent : sizeof(T) * Extent> {};
301 
302 template <std::size_t From, std::size_t To>
303 struct IsAllowedExtentConversion : public std::integral_constant<
304  bool, From == To || From == dynamic_extent || To == dynamic_extent> {};
305 
306 template <class From, class To>
307 struct IsAllowedElementTypeConversion : public std::integral_constant<
308  bool, std::is_convertible<From(*)[], To(*)[]>::value> {};
309 
310 template <class T>
311 struct IsSpanOracle : std::false_type {};
312 
313 template <class T, std::size_t Extent>
314 struct IsSpanOracle<Span<T, Extent>> : std::true_type {};
315 
316 template <class T>
317 struct IsSpan : public IsSpanOracle<typename std::remove_cv<T>::type> {};
318 
319 // Re-implement std algorithms here to adopt CUDA.
320 template <typename T>
321 struct Less {
322  XGBOOST_DEVICE constexpr bool operator()(const T& _x, const T& _y) const {
323  return _x < _y;
324  }
325 };
326 
327 template <typename T>
328 struct Greater {
329  XGBOOST_DEVICE constexpr bool operator()(const T& _x, const T& _y) const {
330  return _x > _y;
331  }
332 };
333 
334 template <class InputIt1, class InputIt2,
335  class Compare =
337 XGBOOST_DEVICE bool LexicographicalCompare(InputIt1 first1, InputIt1 last1,
338  InputIt2 first2, InputIt2 last2) {
339  Compare comp;
340  for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
341  if (comp(*first1, *first2)) {
342  return true;
343  }
344  if (comp(*first2, *first1)) {
345  return false;
346  }
347  }
348  return first1 == last1 && first2 != last2;
349 }
350 
351 } // namespace detail
352 
353 
421 template <typename T,
422  std::size_t Extent = dynamic_extent>
423 class Span {
424  public:
425  using element_type = T; // NOLINT
426  using value_type = typename std::remove_cv<T>::type; // NOLINT
427  using index_type = std::size_t; // NOLINT
429  using pointer = T*; // NOLINT
430  using reference = T&; // NOLINT
431 
433  using const_iterator = const detail::SpanIterator<Span<T, Extent>, true>; // NOLINT
434  using reverse_iterator = std::reverse_iterator<iterator>; // NOLINT
435  using const_reverse_iterator = const std::reverse_iterator<const_iterator>; // NOLINT
436 
437  // constructors
438  constexpr Span() __span_noexcept = default;
439 
441  size_(_count), data_(_ptr) {
442  SPAN_CHECK(!(Extent != dynamic_extent && _count != Extent));
443  SPAN_CHECK(_ptr || _count == 0);
444  }
445 
447  size_(_last - _first), data_(_first) {
448  SPAN_CHECK(data_ || size_ == 0);
449  }
450 
451  template <std::size_t N>
452  XGBOOST_DEVICE constexpr Span(element_type (&arr)[N]) // NOLINT
453  __span_noexcept : size_(N), data_(&arr[0]) {}
454 
455  template <class Container,
456  class = typename std::enable_if<
457  !std::is_const<element_type>::value &&
459  std::is_convertible<typename Container::pointer, pointer>::value &&
460  std::is_convertible<typename Container::pointer,
461  decltype(std::declval<Container>().data())>::value>::type>
462  Span(Container& _cont) : // NOLINT
463  size_(_cont.size()), data_(_cont.data()) {
464  static_assert(!detail::IsSpan<Container>::value, "Wrong constructor of Span is called.");
465  }
466 
467  template <class Container,
468  class = typename std::enable_if<
469  std::is_const<element_type>::value &&
471  std::is_convertible<typename Container::pointer, pointer>::value &&
472  std::is_convertible<typename Container::pointer,
473  decltype(std::declval<Container>().data())>::value>::type>
474  Span(const Container& _cont) : size_(_cont.size()), // NOLINT
475  data_(_cont.data()) {
476  static_assert(!detail::IsSpan<Container>::value, "Wrong constructor of Span is called.");
477  }
478 
479  template <class U, std::size_t OtherExtent,
480  class = typename std::enable_if<
483  XGBOOST_DEVICE constexpr Span(const Span<U, OtherExtent>& _other) // NOLINT
484  __span_noexcept : size_(_other.size()), data_(_other.data()) {}
485 
486  XGBOOST_DEVICE constexpr Span(const Span& _other)
487  __span_noexcept : size_(_other.size()), data_(_other.data()) {}
488 
490  size_ = _other.size();
491  data_ = _other.data();
492  return *this;
493  }
494 
496 
497  XGBOOST_DEVICE constexpr iterator begin() const __span_noexcept { // NOLINT
498  return {this, 0};
499  }
500 
501  XGBOOST_DEVICE constexpr iterator end() const __span_noexcept { // NOLINT
502  return {this, size()};
503  }
504 
505  XGBOOST_DEVICE constexpr const_iterator cbegin() const __span_noexcept { // NOLINT
506  return {this, 0};
507  }
508 
509  XGBOOST_DEVICE constexpr const_iterator cend() const __span_noexcept { // NOLINT
510  return {this, size()};
511  }
512 
513  constexpr reverse_iterator rbegin() const __span_noexcept { // NOLINT
514  return reverse_iterator{end()};
515  }
516 
517  constexpr reverse_iterator rend() const __span_noexcept { // NOLINT
518  return reverse_iterator{begin()};
519  }
520 
522  return const_reverse_iterator{cend()};
523  }
524 
526  return const_reverse_iterator{cbegin()};
527  }
528 
529  // element access
530 
531  XGBOOST_DEVICE reference front() const { // NOLINT
532  return (*this)[0];
533  }
534 
535  XGBOOST_DEVICE reference back() const { // NOLINT
536  return (*this)[size() - 1];
537  }
538 
540  SPAN_LT(_idx, size());
541  return data()[_idx];
542  }
543 
545  return this->operator[](_idx);
546  }
547 
548  XGBOOST_DEVICE constexpr pointer data() const __span_noexcept { // NOLINT
549  return data_;
550  }
551 
552  // Observers
553  XGBOOST_DEVICE constexpr index_type size() const __span_noexcept { // NOLINT
554  return size_;
555  }
556  XGBOOST_DEVICE constexpr index_type size_bytes() const __span_noexcept { // NOLINT
557  return size() * sizeof(T);
558  }
559 
560  XGBOOST_DEVICE constexpr bool empty() const __span_noexcept { // NOLINT
561  return size() == 0;
562  }
563 
564  // Subviews
565  template <std::size_t Count>
567  SPAN_CHECK(Count <= size());
568  return {data(), Count};
569  }
570 
572  std::size_t _count) const {
573  SPAN_CHECK(_count <= size());
574  return {data(), _count};
575  }
576 
577  template <std::size_t Count>
579  SPAN_CHECK(Count <= size());
580  return {data() + size() - Count, Count};
581  }
582 
584  std::size_t _count) const {
585  SPAN_CHECK(_count <= size());
586  return subspan(size() - _count, _count);
587  }
588 
593  template <std::size_t Offset,
594  std::size_t Count = dynamic_extent>
595  XGBOOST_DEVICE auto subspan() const -> // NOLINT
597  detail::ExtentValue<Extent, Offset, Count>::value> {
598  SPAN_CHECK((Count == dynamic_extent) ?
599  (Offset <= size()) : (Offset + Count <= size()));
600  return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
601  }
602 
604  index_type _offset,
605  index_type _count = dynamic_extent) const {
606  SPAN_CHECK((_count == dynamic_extent) ?
607  (_offset <= size()) : (_offset + _count <= size()));
608  return {data() + _offset, _count ==
609  dynamic_extent ? size() - _offset : _count};
610  }
611 
612  private:
613  index_type size_ { 0 };
614  pointer data_ { nullptr };
615 };
616 
617 template <class T, std::size_t X, class U, std::size_t Y>
619  if (l.size() != r.size()) {
620  return false;
621  }
622  for (auto l_beg = l.cbegin(), r_beg = r.cbegin(); l_beg != l.cend();
623  ++l_beg, ++r_beg) {
624  if (*l_beg != *r_beg) {
625  return false;
626  }
627  }
628  return true;
629 }
630 
631 template <class T, std::size_t X, class U, std::size_t Y>
633  return !(l == r);
634 }
635 
636 template <class T, std::size_t X, class U, std::size_t Y>
638  return detail::LexicographicalCompare(l.begin(), l.end(),
639  r.begin(), r.end());
640 }
641 
642 template <class T, std::size_t X, class U, std::size_t Y>
644  return !(l > r);
645 }
646 
647 template <class T, std::size_t X, class U, std::size_t Y>
650  typename Span<T, X>::iterator, typename Span<U, Y>::iterator,
652  r.begin(), r.end());
653 }
654 
655 template <class T, std::size_t X, class U, std::size_t Y>
657  return !(l < r);
658 }
659 
660 template <class T, std::size_t E>
663  return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
664 }
665 
666 template <class T, std::size_t E>
669  return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
670 }
671 } // namespace common
672 } // namespace xgboost
673 
674 #if defined(_MSC_VER) &&_MSC_VER < 1910
675 #undef constexpr
676 #pragma pop_macro("constexpr")
677 #undef __span_noexcept
678 #endif // _MSC_VER < 1910
679 
680 #endif // XGBOOST_SPAN_H_
defines configuration macros of xgboost.
#define XGBOOST_DEVICE
Tag function as usable by device.
Definition: base.h:84
span class implementation, based on ISO++20 span<T>. The interface should be the same.
Definition: span.h:423
T & reference
Definition: span.h:430
XGBOOST_DEVICE Span(pointer _first, pointer _last)
Definition: span.h:446
T * pointer
Definition: span.h:429
detail::ptrdiff_t difference_type
Definition: span.h:428
XGBOOST_DEVICE reference operator()(index_type _idx) const
Definition: span.h:544
XGBOOST_DEVICE reference front() const
Definition: span.h:531
constexpr XGBOOST_DEVICE const_iterator cbegin() const __span_noexcept
Definition: span.h:505
constexpr XGBOOST_DEVICE iterator begin() const __span_noexcept
Definition: span.h:497
constexpr XGBOOST_DEVICE pointer data() const __span_noexcept
Definition: span.h:548
std::size_t index_type
Definition: span.h:427
XGBOOST_DEVICE reference back() const
Definition: span.h:535
XGBOOST_DEVICE auto subspan() const -> Span< element_type, detail::ExtentValue< Extent, Offset, Count >::value >
Definition: span.h:595
constexpr XGBOOST_DEVICE index_type size() const __span_noexcept
Definition: span.h:553
XGBOOST_DEVICE Span< element_type, Count > last() const
Definition: span.h:578
Span(Container &_cont)
Definition: span.h:462
XGBOOST_DEVICE Span< element_type, Count > first() const
Definition: span.h:566
constexpr XGBOOST_DEVICE Span(const Span &_other) __span_noexcept
Definition: span.h:486
constexpr XGBOOST_DEVICE bool empty() const __span_noexcept
Definition: span.h:560
constexpr XGBOOST_DEVICE index_type size_bytes() const __span_noexcept
Definition: span.h:556
constexpr XGBOOST_DEVICE Span(element_type(&arr)[N]) __span_noexcept
Definition: span.h:452
constexpr reverse_iterator rbegin() const __span_noexcept
Definition: span.h:513
T element_type
Definition: span.h:425
const std::reverse_iterator< const_iterator > const_reverse_iterator
Definition: span.h:435
XGBOOST_DEVICE Span< element_type, dynamic_extent > first(std::size_t _count) const
Definition: span.h:571
constexpr XGBOOST_DEVICE const_reverse_iterator crbegin() const __span_noexcept
Definition: span.h:521
constexpr XGBOOST_DEVICE const_iterator cend() const __span_noexcept
Definition: span.h:509
XGBOOST_DEVICE ~Span() __span_noexcept
Definition: span.h:495
constexpr XGBOOST_DEVICE const_reverse_iterator crend() const __span_noexcept
Definition: span.h:525
constexpr Span() __span_noexcept=default
constexpr XGBOOST_DEVICE iterator end() const __span_noexcept
Definition: span.h:501
XGBOOST_DEVICE reference operator[](index_type _idx) const
Definition: span.h:539
constexpr reverse_iterator rend() const __span_noexcept
Definition: span.h:517
XGBOOST_DEVICE Span & operator=(const Span &_other) __span_noexcept
Definition: span.h:489
std::reverse_iterator< iterator > reverse_iterator
Definition: span.h:434
XGBOOST_DEVICE Span< element_type, dynamic_extent > subspan(index_type _offset, index_type _count=dynamic_extent) const
Definition: span.h:603
Span(const Container &_cont)
Definition: span.h:474
constexpr XGBOOST_DEVICE Span(const Span< U, OtherExtent > &_other) __span_noexcept
Definition: span.h:483
typename std::remove_cv< T >::type value_type
Definition: span.h:426
XGBOOST_DEVICE Span< element_type, dynamic_extent > last(std::size_t _count) const
Definition: span.h:583
XGBOOST_DEVICE pointer operator->() const
Definition: span.h:186
XGBOOST_DEVICE SpanIterator & operator--()
Definition: span.h:203
std::random_access_iterator_tag iterator_category
Definition: span.h:157
XGBOOST_DEVICE reference operator*() const
Definition: span.h:178
constexpr XGBOOST_DEVICE friend bool operator<(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:251
typename SpanType::value_type value_type
Definition: span.h:158
constexpr XGBOOST_DEVICE SpanIterator(const SpanType *_span, typename SpanType::index_type _idx) __span_noexcept
Definition: span.h:167
constexpr XGBOOST_DEVICE friend bool operator>(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:261
constexpr XGBOOST_DEVICE SpanIterator(const SpanIterator< SpanType, B > &other_) __span_noexcept
Definition: span.h:174
XGBOOST_DEVICE SpanIterator operator++(int)
Definition: span.h:197
constexpr XGBOOST_DEVICE friend bool operator==(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:241
XGBOOST_DEVICE SpanIterator & operator+=(difference_type n)
Definition: span.h:220
XGBOOST_DEVICE SpanIterator operator-(difference_type n) const
Definition: span.h:231
XGBOOST_DEVICE SpanIterator & operator-=(difference_type n)
Definition: span.h:236
detail::ptrdiff_t difference_type
Definition: span.h:159
typename std::conditional< IsConst, const ElementType, ElementType >::type & reference
Definition: span.h:162
XGBOOST_DEVICE reference operator[](difference_type n) const
Definition: span.h:182
typename std::add_pointer< reference >::type pointer
Definition: span.h:163
XGBOOST_DEVICE difference_type operator-(SpanIterator rhs) const
Definition: span.h:226
const SpanType * span_
Definition: span.h:272
constexpr XGBOOST_DEVICE friend bool operator>=(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:266
XGBOOST_DEVICE SpanIterator operator+(difference_type n) const
Definition: span.h:215
constexpr XGBOOST_DEVICE friend bool operator!=(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:246
constexpr XGBOOST_DEVICE friend bool operator<=(SpanIterator _lhs, SpanIterator _rhs) __span_noexcept
Definition: span.h:256
XGBOOST_DEVICE SpanIterator operator--(int)
Definition: span.h:209
SpanType::index_type index_
Definition: span.h:273
XGBOOST_DEVICE SpanIterator & operator++()
Definition: span.h:191
XGBOOST_DEVICE bool LexicographicalCompare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
Definition: span.h:337
typename std::conditional< std::is_same< std::ptrdiff_t, std::int64_t >::value, std::ptrdiff_t, std::int64_t >::type ptrdiff_t
Definition: span.h:135
constexpr XGBOOST_DEVICE bool operator>=(Span< T, X > l, Span< U, Y > r)
Definition: span.h:656
constexpr XGBOOST_DEVICE bool operator!=(Span< T, X > l, Span< U, Y > r)
Definition: span.h:632
constexpr std::size_t dynamic_extent
Definition: span.h:142
byte
Definition: span.h:145
XGBOOST_DEVICE auto as_writable_bytes(Span< T, E > s) __span_noexcept -> Span< byte, detail::ExtentAsBytesValue< T, E >::value >
Definition: span.h:667
XGBOOST_DEVICE bool operator==(Span< T, X > l, Span< U, Y > r)
Definition: span.h:618
XGBOOST_DEVICE auto as_bytes(Span< T, E > s) __span_noexcept -> Span< const byte, detail::ExtentAsBytesValue< T, E >::value >
Definition: span.h:661
constexpr XGBOOST_DEVICE bool operator>(Span< T, X > l, Span< U, Y > r)
Definition: span.h:648
constexpr XGBOOST_DEVICE bool operator<=(Span< T, X > l, Span< U, Y > r)
Definition: span.h:643
constexpr XGBOOST_DEVICE bool operator<(Span< T, X > l, Span< U, Y > r)
Definition: span.h:637
constexpr size_t Offset(S(&strides)[D], size_t n, Head head)
Definition: linalg.h:48
namespace of xgboost
Definition: base.h:110
#define SPAN_LT(lhs, rhs)
Definition: span.h:124
#define __span_noexcept
span class based on ISO++20 span
Definition: span.h:71
#define SPAN_CHECK(cond)
Definition: span.h:118
Definition: span.h:328
constexpr XGBOOST_DEVICE bool operator()(const T &_x, const T &_y) const
Definition: span.h:329
Definition: span.h:317
Definition: span.h:321
constexpr XGBOOST_DEVICE bool operator()(const T &_x, const T &_y) const
Definition: span.h:322