Span class. (#3548)
* Add basic Span class based on ISO++20. * Use Span<Entry const> instead of Inst in SparsePage. * Add DeviceSpan in HostDeviceVector, use it in regression obj.
This commit is contained in:
parent
2b7a1c5780
commit
2c502784ff
@ -15,6 +15,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "./base.h"
|
#include "./base.h"
|
||||||
|
#include "../../src/common/span.h"
|
||||||
|
|
||||||
namespace xgboost {
|
namespace xgboost {
|
||||||
// forward declare learner.
|
// forward declare learner.
|
||||||
@ -133,7 +134,7 @@ struct Entry {
|
|||||||
/*!
|
/*!
|
||||||
* \brief constructor with index and value
|
* \brief constructor with index and value
|
||||||
* \param index The feature or row index.
|
* \param index The feature or row index.
|
||||||
* \param fvalue THe feature value.
|
* \param fvalue The feature value.
|
||||||
*/
|
*/
|
||||||
Entry(bst_uint index, bst_float fvalue) : index(index), fvalue(fvalue) {}
|
Entry(bst_uint index, bst_float fvalue) : index(index), fvalue(fvalue) {}
|
||||||
/*! \brief reversely compare feature values */
|
/*! \brief reversely compare feature values */
|
||||||
@ -155,24 +156,14 @@ class SparsePage {
|
|||||||
std::vector<Entry> data;
|
std::vector<Entry> data;
|
||||||
|
|
||||||
size_t base_rowid;
|
size_t base_rowid;
|
||||||
|
|
||||||
/*! \brief an instance of sparse vector in the batch */
|
/*! \brief an instance of sparse vector in the batch */
|
||||||
struct Inst {
|
using Inst = common::Span<Entry const>;
|
||||||
/*! \brief pointer to the elements*/
|
|
||||||
const Entry *data{nullptr};
|
|
||||||
/*! \brief length of the instance */
|
|
||||||
bst_uint length{0};
|
|
||||||
/*! \brief constructor */
|
|
||||||
Inst() = default;
|
|
||||||
Inst(const Entry *data, bst_uint length) : data(data), length(length) {}
|
|
||||||
/*! \brief get i-th pair in the sparse vector*/
|
|
||||||
inline const Entry& operator[](size_t i) const {
|
|
||||||
return data[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! \brief get i-th row from the batch */
|
/*! \brief get i-th row from the batch */
|
||||||
inline Inst operator[](size_t i) const {
|
inline Inst operator[](size_t i) const {
|
||||||
return {data.data() + offset[i], static_cast<bst_uint>(offset[i + 1] - offset[i])};
|
return {data.data() + offset[i],
|
||||||
|
static_cast<Inst::index_type>(offset[i + 1] - offset[i])};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief constructor */
|
/*! \brief constructor */
|
||||||
@ -234,12 +225,12 @@ class SparsePage {
|
|||||||
* \param inst an instance row
|
* \param inst an instance row
|
||||||
*/
|
*/
|
||||||
inline void Push(const Inst &inst) {
|
inline void Push(const Inst &inst) {
|
||||||
offset.push_back(offset.back() + inst.length);
|
offset.push_back(offset.back() + inst.size());
|
||||||
size_t begin = data.size();
|
size_t begin = data.size();
|
||||||
data.resize(begin + inst.length);
|
data.resize(begin + inst.size());
|
||||||
if (inst.length != 0) {
|
if (inst.size() != 0) {
|
||||||
std::memcpy(dmlc::BeginPtr(data) + begin, inst.data,
|
std::memcpy(dmlc::BeginPtr(data) + begin, inst.data(),
|
||||||
sizeof(Entry) * inst.length);
|
sizeof(Entry) * inst.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -574,14 +574,14 @@ inline void RegTree::FVec::Init(size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void RegTree::FVec::Fill(const SparsePage::Inst& inst) {
|
inline void RegTree::FVec::Fill(const SparsePage::Inst& inst) {
|
||||||
for (bst_uint i = 0; i < inst.length; ++i) {
|
for (bst_uint i = 0; i < inst.size(); ++i) {
|
||||||
if (inst[i].index >= data_.size()) continue;
|
if (inst[i].index >= data_.size()) continue;
|
||||||
data_[inst[i].index].fvalue = inst[i].fvalue;
|
data_[inst[i].index].fvalue = inst[i].fvalue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void RegTree::FVec::Drop(const SparsePage::Inst& inst) {
|
inline void RegTree::FVec::Drop(const SparsePage::Inst& inst) {
|
||||||
for (bst_uint i = 0; i < inst.length; ++i) {
|
for (bst_uint i = 0; i < inst.size(); ++i) {
|
||||||
if (inst[i].index >= data_.size()) continue;
|
if (inst[i].index >= data_.size()) continue;
|
||||||
data_[inst[i].index].flag = -1;
|
data_[inst[i].index].flag = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -687,10 +687,10 @@ XGB_DLL int XGDMatrixSliceDMatrix(DMatrixHandle handle,
|
|||||||
const int ridx = idxset[i];
|
const int ridx = idxset[i];
|
||||||
auto inst = batch[ridx];
|
auto inst = batch[ridx];
|
||||||
CHECK_LT(static_cast<xgboost::bst_ulong>(ridx), batch.Size());
|
CHECK_LT(static_cast<xgboost::bst_ulong>(ridx), batch.Size());
|
||||||
ret.page_.data.insert(ret.page_.data.end(), inst.data,
|
ret.page_.data.insert(ret.page_.data.end(), inst.data(),
|
||||||
inst.data + inst.length);
|
inst.data() + inst.size());
|
||||||
ret.page_.offset.push_back(ret.page_.offset.back() + inst.length);
|
ret.page_.offset.push_back(ret.page_.offset.back() + inst.size());
|
||||||
ret.info.num_nonzero_ += inst.length;
|
ret.info.num_nonzero_ += inst.size();
|
||||||
|
|
||||||
if (src.info.labels_.size() != 0) {
|
if (src.info.labels_.size() != 0) {
|
||||||
ret.info.labels_.push_back(src.info.labels_[ridx]);
|
ret.info.labels_.push_back(src.info.labels_[ridx]);
|
||||||
|
|||||||
@ -48,9 +48,9 @@ void HistCutMatrix::Init(DMatrix* p_fmat, uint32_t max_num_bins) {
|
|||||||
for (size_t i = 0; i < batch.Size(); ++i) { // NOLINT(*)
|
for (size_t i = 0; i < batch.Size(); ++i) { // NOLINT(*)
|
||||||
size_t ridx = batch.base_rowid + i;
|
size_t ridx = batch.base_rowid + i;
|
||||||
SparsePage::Inst inst = batch[i];
|
SparsePage::Inst inst = batch[i];
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
for (auto& ins : inst) {
|
||||||
if (inst[j].index >= begin && inst[j].index < end) {
|
if (ins.index >= begin && ins.index < end) {
|
||||||
sketchs[inst[j].index].Push(inst[j].fvalue, info.GetWeight(ridx));
|
sketchs[ins.index].Push(ins.fvalue, info.GetWeight(ridx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ void GHistIndexMatrix::Init(DMatrix* p_fmat, int max_num_bins) {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
const size_t rbegin = row_ptr.size() - 1;
|
const size_t rbegin = row_ptr.size() - 1;
|
||||||
for (size_t i = 0; i < batch.Size(); ++i) {
|
for (size_t i = 0; i < batch.Size(); ++i) {
|
||||||
row_ptr.push_back(batch[i].length + row_ptr.back());
|
row_ptr.push_back(batch[i].size() + row_ptr.back());
|
||||||
}
|
}
|
||||||
index.resize(row_ptr.back());
|
index.resize(row_ptr.back());
|
||||||
|
|
||||||
@ -154,9 +154,11 @@ void GHistIndexMatrix::Init(DMatrix* p_fmat, int max_num_bins) {
|
|||||||
size_t ibegin = row_ptr[rbegin + i];
|
size_t ibegin = row_ptr[rbegin + i];
|
||||||
size_t iend = row_ptr[rbegin + i + 1];
|
size_t iend = row_ptr[rbegin + i + 1];
|
||||||
SparsePage::Inst inst = batch[i];
|
SparsePage::Inst inst = batch[i];
|
||||||
CHECK_EQ(ibegin + inst.length, iend);
|
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
CHECK_EQ(ibegin + inst.size(), iend);
|
||||||
|
for (bst_uint j = 0; j < inst.size(); ++j) {
|
||||||
uint32_t idx = cut.GetBinIdx(inst[j]);
|
uint32_t idx = cut.GetBinIdx(inst[j]);
|
||||||
|
|
||||||
index[ibegin + j] = idx;
|
index[ibegin + j] = idx;
|
||||||
++hit_count_tloc_[tid * nbins + idx];
|
++hit_count_tloc_[tid * nbins + idx];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,11 @@ GPUSet HostDeviceVector<T>::Devices() const { return GPUSet::Empty(); }
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T* HostDeviceVector<T>::DevicePointer(int device) { return nullptr; }
|
T* HostDeviceVector<T>::DevicePointer(int device) { return nullptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
common::Span<T> HostDeviceVector<T>::DeviceSpan(int device) {
|
||||||
|
return common::Span<T>();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::vector<T>& HostDeviceVector<T>::HostVector() { return impl_->data_h_; }
|
std::vector<T>& HostDeviceVector<T>::HostVector() { return impl_->data_h_; }
|
||||||
|
|
||||||
|
|||||||
@ -156,6 +156,13 @@ struct HostDeviceVectorImpl {
|
|||||||
return shards_[devices_.Index(device)].data_.data().get();
|
return shards_[devices_.Index(device)].data_.data().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common::Span<T> DeviceSpan(int device) {
|
||||||
|
CHECK(devices_.Contains(device));
|
||||||
|
LazySyncDevice(device);
|
||||||
|
return { shards_[devices_.Index(device)].data_.data().get(),
|
||||||
|
static_cast<typename common::Span<T>::index_type>(Size()) };
|
||||||
|
}
|
||||||
|
|
||||||
size_t DeviceSize(int device) {
|
size_t DeviceSize(int device) {
|
||||||
CHECK(devices_.Contains(device));
|
CHECK(devices_.Contains(device));
|
||||||
LazySyncDevice(device);
|
LazySyncDevice(device);
|
||||||
@ -323,6 +330,11 @@ GPUSet HostDeviceVector<T>::Devices() const { return impl_->Devices(); }
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T* HostDeviceVector<T>::DevicePointer(int device) { return impl_->DevicePointer(device); }
|
T* HostDeviceVector<T>::DevicePointer(int device) { return impl_->DevicePointer(device); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
common::Span<T> HostDeviceVector<T>::DeviceSpan(int device) {
|
||||||
|
return impl_->DeviceSpan(device);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
size_t HostDeviceVector<T>::DeviceStart(int device) { return impl_->DeviceStart(device); }
|
size_t HostDeviceVector<T>::DeviceStart(int device) { return impl_->DeviceStart(device); }
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,8 @@
|
|||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "span.h"
|
||||||
|
|
||||||
// only include thrust-related files if host_device_vector.h
|
// only include thrust-related files if host_device_vector.h
|
||||||
// is included from a .cu file
|
// is included from a .cu file
|
||||||
#ifdef __CUDACC__
|
#ifdef __CUDACC__
|
||||||
@ -117,6 +119,7 @@ class HostDeviceVector {
|
|||||||
size_t Size() const;
|
size_t Size() const;
|
||||||
GPUSet Devices() const;
|
GPUSet Devices() const;
|
||||||
T* DevicePointer(int device);
|
T* DevicePointer(int device);
|
||||||
|
common::Span<T> DeviceSpan(int device);
|
||||||
|
|
||||||
T* HostPointer() { return HostVector().data(); }
|
T* HostPointer() { return HostVector().data(); }
|
||||||
size_t DeviceStart(int device);
|
size_t DeviceStart(int device);
|
||||||
|
|||||||
633
src/common/span.h
Normal file
633
src/common/span.h
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2018 XGBoost contributors
|
||||||
|
* \brief span class based on ISO++20 span
|
||||||
|
*
|
||||||
|
* About NOLINTs in this file:
|
||||||
|
*
|
||||||
|
* If we want Span to work with std interface, like range for loop, the
|
||||||
|
* naming must be consistant with std, not XGBoost. Also, the interface also
|
||||||
|
* conflicts with XGBoost coding style, specifically, the use of `explicit'
|
||||||
|
* keyword.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Some of the code is copied from Guidelines Support Library, here is the
|
||||||
|
* license:
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is licensed under the MIT License (MIT).
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XGBOOST_COMMON_SPAN_H_
|
||||||
|
#define XGBOOST_COMMON_SPAN_H_
|
||||||
|
|
||||||
|
#include <xgboost/logging.h> // CHECK
|
||||||
|
|
||||||
|
#include <cinttypes> // int64_t
|
||||||
|
#include <type_traits> // remove_cv_t
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The version number 1910 is picked up from GSL.
|
||||||
|
*
|
||||||
|
* We might want to use MOODYCAMEL_NOEXCEPT from dmlc/concurrentqueue.h. But
|
||||||
|
* there are a lot more definitions in that file would cause warnings/troubles
|
||||||
|
* in MSVC 2013. Currently we try to keep the closure of Span as minimal as
|
||||||
|
* possible.
|
||||||
|
*
|
||||||
|
* There are other workarounds for MSVC, like _Unwrapped, _Verify_range ...
|
||||||
|
* Some of these are hiden magics of MSVC and I tried to avoid them. Should any
|
||||||
|
* of them become needed, please consult the source code of GSL, and possibily
|
||||||
|
* some explanations from this thread:
|
||||||
|
*
|
||||||
|
* https://github.com/Microsoft/GSL/pull/664
|
||||||
|
*
|
||||||
|
* FIXME: Group these MSVC workarounds into a manageable place.
|
||||||
|
*/
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||||
|
|
||||||
|
#define __span_noexcept
|
||||||
|
|
||||||
|
#pragma push_macro("constexpr")
|
||||||
|
#define constexpr /*constexpr*/
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define __span_noexcept noexcept
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
// Usual logging facility is not available inside device code.
|
||||||
|
// FIXME: Make dmlc check more generic.
|
||||||
|
#define KERNEL_CHECK(cond) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
printf("\nKernel error:\n" \
|
||||||
|
"In: %s, \tline: %d\n" \
|
||||||
|
"\t%s\n\tExpecting: %s\n", \
|
||||||
|
__FILE__, __LINE__, __PRETTY_FUNCTION__, # cond); \
|
||||||
|
asm("trap;"); \
|
||||||
|
} \
|
||||||
|
} while (0); \
|
||||||
|
|
||||||
|
#ifdef __CUDA_ARCH__
|
||||||
|
#define SPAN_CHECK KERNEL_CHECK
|
||||||
|
#else
|
||||||
|
#define SPAN_CHECK CHECK // check from dmlc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
/*!
|
||||||
|
* By default, XGBoost uses uint32_t for indexing data. int64_t covers all
|
||||||
|
* values uint32_t can represent. Also, On x86-64 Linux, GCC uses long int to
|
||||||
|
* represent ptrdiff_t, which is just int64_t. So we make it determinstic
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
using ptrdiff_t = int64_t; // NOLINT
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||||
|
constexpr const detail::ptrdiff_t dynamic_extent = -1; // NOLINT
|
||||||
|
#else
|
||||||
|
constexpr detail::ptrdiff_t dynamic_extent = -1; // NOLINT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class byte : unsigned char {}; // NOLINT
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class ElementType, detail::ptrdiff_t Extent = dynamic_extent>
|
||||||
|
class Span;
|
||||||
|
|
||||||
|
template <typename SpanType, bool IsConst>
|
||||||
|
class SpanIterator {
|
||||||
|
using ElementType = typename SpanType::element_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::random_access_iterator_tag; // NOLINT
|
||||||
|
using value_type = typename std::remove_cv<ElementType>::type; // NOLINT
|
||||||
|
using difference_type = typename SpanType::index_type; // NOLINT
|
||||||
|
|
||||||
|
using reference = typename std::conditional< // NOLINT
|
||||||
|
IsConst, const ElementType, ElementType>::type&;
|
||||||
|
using pointer = typename std::add_pointer<reference>::type&; // NOLINT
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr SpanIterator() : span_{nullptr}, index_{0} {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr SpanIterator(
|
||||||
|
const SpanType* _span,
|
||||||
|
typename SpanType::index_type _idx) __span_noexcept :
|
||||||
|
span_(_span), index_(_idx) {}
|
||||||
|
|
||||||
|
friend SpanIterator<SpanType, true>;
|
||||||
|
template <bool B, typename std::enable_if<!B && IsConst>::type* = nullptr>
|
||||||
|
XGBOOST_DEVICE constexpr SpanIterator( // NOLINT
|
||||||
|
const SpanIterator<SpanType, B>& other_) __span_noexcept
|
||||||
|
: SpanIterator(other_.span_, other_.index_) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE reference operator*() const {
|
||||||
|
SPAN_CHECK(index_ < span_->size());
|
||||||
|
return *(span_->data() + index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE pointer operator->() const {
|
||||||
|
SPAN_CHECK(index_ != span_->size());
|
||||||
|
return span_->data() + index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator& operator++() {
|
||||||
|
SPAN_CHECK(0 <= index_ && index_ != span_->size());
|
||||||
|
index_++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator operator++(int) {
|
||||||
|
auto ret = *this;
|
||||||
|
++(*this);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator& operator--() {
|
||||||
|
SPAN_CHECK(index_ != 0 && index_ <= span_->size());
|
||||||
|
index_--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator operator--(int) {
|
||||||
|
auto ret = *this;
|
||||||
|
--(*this);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator operator+(difference_type n) const {
|
||||||
|
auto ret = *this;
|
||||||
|
return ret += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator& operator+=(difference_type n) {
|
||||||
|
SPAN_CHECK((index_ + n) >= 0 && (index_ + n) <= span_->size());
|
||||||
|
index_ += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE difference_type operator-(SpanIterator rhs) const {
|
||||||
|
SPAN_CHECK(span_ == rhs.span_);
|
||||||
|
return index_ - rhs.index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator operator-(difference_type n) const {
|
||||||
|
auto ret = *this;
|
||||||
|
return ret -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE SpanIterator& operator-=(difference_type n) {
|
||||||
|
return *this += -n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// friends
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator==(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return _lhs.span_ == _rhs.span_ && _lhs.index_ == _rhs.index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator!=(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return !(_lhs == _rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator<(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return _lhs.index_ < _rhs.index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator<=(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return !(_rhs < _lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator>(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return _rhs < _lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr friend bool operator>=(
|
||||||
|
SpanIterator _lhs, SpanIterator _rhs) __span_noexcept {
|
||||||
|
return !(_rhs > _lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const SpanType *span_;
|
||||||
|
detail::ptrdiff_t index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// It's tempting to use constexpr instead of structs to do the following meta
|
||||||
|
// programming. But remember that we are supporting MSVC 2013 here.
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The extent E of the span returned by subspan is determined as follows:
|
||||||
|
*
|
||||||
|
* - If Count is not std::dynamic_extent, Count;
|
||||||
|
* - Otherwise, if Extent is not std::dynamic_extent, Extent - Offset;
|
||||||
|
* - Otherwise, std::dynamic_extent.
|
||||||
|
*/
|
||||||
|
template <detail::ptrdiff_t Extent,
|
||||||
|
detail::ptrdiff_t Offset,
|
||||||
|
detail::ptrdiff_t Count>
|
||||||
|
struct ExtentValue : public std::integral_constant<
|
||||||
|
detail::ptrdiff_t, Count != dynamic_extent ?
|
||||||
|
Count : (Extent != dynamic_extent ? Extent - Offset : Extent)> {};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* If N is dynamic_extent, the extent of the returned span E is also
|
||||||
|
* dynamic_extent; otherwise it is detail::ptrdiff_t(sizeof(T)) * N.
|
||||||
|
*/
|
||||||
|
template <typename T, detail::ptrdiff_t Extent>
|
||||||
|
struct ExtentAsBytesValue : public std::integral_constant<
|
||||||
|
detail::ptrdiff_t,
|
||||||
|
Extent == dynamic_extent ?
|
||||||
|
Extent : static_cast<detail::ptrdiff_t>(sizeof(T) * Extent)> {};
|
||||||
|
|
||||||
|
template <detail::ptrdiff_t From, detail::ptrdiff_t To>
|
||||||
|
struct IsAllowedExtentConversion : public std::integral_constant<
|
||||||
|
bool, From == To || From == dynamic_extent || To == dynamic_extent> {};
|
||||||
|
|
||||||
|
template <class From, class To>
|
||||||
|
struct IsAllowedElementTypeConversion : public std::integral_constant<
|
||||||
|
bool, std::is_convertible<From(*)[], To(*)[]>::value> {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct IsSpanOracle : std::false_type {};
|
||||||
|
|
||||||
|
template <class T, std::ptrdiff_t Extent>
|
||||||
|
struct IsSpanOracle<Span<T, Extent>> : std::true_type {};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct IsSpan : public IsSpanOracle<typename std::remove_cv<T>::type> {};
|
||||||
|
|
||||||
|
// Re-implement std algorithms here to adopt CUDA.
|
||||||
|
template <typename T>
|
||||||
|
struct Less {
|
||||||
|
XGBOOST_DEVICE constexpr bool operator()(const T& _x, const T& _y) const {
|
||||||
|
return _x < _y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Greater {
|
||||||
|
XGBOOST_DEVICE constexpr bool operator()(const T& _x, const T& _y) const {
|
||||||
|
return _x > _y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class InputIt1, class InputIt2,
|
||||||
|
class Compare =
|
||||||
|
detail::Less<decltype(std::declval<InputIt1>().operator*())>>
|
||||||
|
XGBOOST_DEVICE bool LexicographicalCompare(InputIt1 first1, InputIt1 last1,
|
||||||
|
InputIt2 first2, InputIt2 last2) {
|
||||||
|
Compare comp;
|
||||||
|
for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
|
||||||
|
if (comp(*first1, *first2)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (comp(*first2, *first1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return first1 == last1 && first2 != last2;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief span class implementation, based on ISO++20 span<T>. The interface
|
||||||
|
* should be the same.
|
||||||
|
*
|
||||||
|
* What's different from span<T> in Guidelines Support Library (GSL)
|
||||||
|
*
|
||||||
|
* Interface might be slightly different, we stick with ISO.
|
||||||
|
*
|
||||||
|
* GSL uses C++14/17 features, which are not available here.
|
||||||
|
* GSL uses constexpr extensively, which is not possibile with limitation
|
||||||
|
* of C++11.
|
||||||
|
* GSL doesn't concern about CUDA.
|
||||||
|
*
|
||||||
|
* GSL is more thoroughly implemented and tested.
|
||||||
|
* GSL is more optimized, especially for static extent.
|
||||||
|
*
|
||||||
|
* GSL uses __buildin_unreachable() when error, Span<T> uses dmlc LOG and
|
||||||
|
* customized CUDA logging.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* What's different from span<T> in ISO++20 (ISO)
|
||||||
|
*
|
||||||
|
* ISO uses functions/structs from std library, which might be not available
|
||||||
|
* in CUDA.
|
||||||
|
* Initializing from std::array is not supported.
|
||||||
|
*
|
||||||
|
* ISO uses constexpr extensively, which is not possibile with limitation
|
||||||
|
* of C++11.
|
||||||
|
* ISO uses C++14/17 features, which is not available here.
|
||||||
|
* ISO doesn't concern about CUDA.
|
||||||
|
*
|
||||||
|
* ISO uses std::terminate(), Span<T> uses dmlc LOG and customized CUDA
|
||||||
|
* logging.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* With thrust:
|
||||||
|
* It's not adviced to initialize Span with host_vector directly, since
|
||||||
|
* host_vector::data() is a host function.
|
||||||
|
* It's not possible to initialize Span with device_vector directly, since
|
||||||
|
* device_vector::data() returns a wrapped pointer.
|
||||||
|
* It's unclear that what kind of thrust algorithm can be used without
|
||||||
|
* memory error. See the test case "GPUSpan.WithTrust"
|
||||||
|
*
|
||||||
|
* Pass iterator to kernel:
|
||||||
|
* Not possible. Use subspan instead.
|
||||||
|
*
|
||||||
|
* The underlying Span in SpanIterator is a pointer, but CUDA pass kernel
|
||||||
|
* parameter by value. If we were to hold a Span value instead of a
|
||||||
|
* pointer, the following snippet will crash, violating the safety
|
||||||
|
* purpose of Span:
|
||||||
|
*
|
||||||
|
* \code{.cpp}
|
||||||
|
* Span<float> span {arr_a};
|
||||||
|
* auto beg = span.begin();
|
||||||
|
*
|
||||||
|
* Span<float> span_b = arr_b;
|
||||||
|
* span = span_b;
|
||||||
|
*
|
||||||
|
* delete arr_a;
|
||||||
|
* beg++; // crash
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* While hoding a pointer or reference should avoid the problem, its a
|
||||||
|
* compromise. Since we have subspan, it's acceptable not to support
|
||||||
|
* passing iterator.
|
||||||
|
*/
|
||||||
|
template <typename T,
|
||||||
|
detail::ptrdiff_t Extent = dynamic_extent>
|
||||||
|
class Span {
|
||||||
|
public:
|
||||||
|
using element_type = T; // NOLINT
|
||||||
|
using value_type = typename std::remove_cv<T>::type; // NOLINT
|
||||||
|
using index_type = detail::ptrdiff_t; // NOLINT
|
||||||
|
using difference_type = detail::ptrdiff_t; // NOLINT
|
||||||
|
using pointer = T*; // NOLINT
|
||||||
|
using reference = T&; // NOLINT
|
||||||
|
|
||||||
|
using iterator = detail::SpanIterator<Span<T, Extent>, false>; // NOLINT
|
||||||
|
using const_iterator = const detail::SpanIterator<Span<T, Extent>, true>; // NOLINT
|
||||||
|
using reverse_iterator = detail::SpanIterator<Span<T, Extent>, false>; // NOLINT
|
||||||
|
using const_reverse_iterator = const detail::SpanIterator<Span<T, Extent>, true>; // NOLINT
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr Span() __span_noexcept : size_(0), data_(nullptr) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span(pointer _ptr, index_type _count) :
|
||||||
|
size_(_count), data_(_ptr) {
|
||||||
|
SPAN_CHECK(_count >= 0);
|
||||||
|
SPAN_CHECK(_ptr || _count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span(pointer _first, pointer _last) :
|
||||||
|
size_(_last - _first), data_(_first) {
|
||||||
|
SPAN_CHECK(size_ >= 0);
|
||||||
|
SPAN_CHECK(data_ || size_ == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
XGBOOST_DEVICE constexpr Span(element_type (&arr)[N]) // NOLINT
|
||||||
|
__span_noexcept : size_(N), data_(&arr[0]) {}
|
||||||
|
|
||||||
|
template <class Container,
|
||||||
|
class = typename std::enable_if<
|
||||||
|
!std::is_const<element_type>::value && !detail::IsSpan<Container>::value &&
|
||||||
|
std::is_convertible<typename Container::pointer,
|
||||||
|
pointer>::value &&
|
||||||
|
std::is_convertible<
|
||||||
|
typename Container::pointer,
|
||||||
|
decltype(std::declval<Container>().data())>::value>>
|
||||||
|
XGBOOST_DEVICE Span(Container& _cont) : // NOLINT
|
||||||
|
size_(_cont.size()), data_(_cont.data()) {}
|
||||||
|
|
||||||
|
template <class Container,
|
||||||
|
class = typename std::enable_if<
|
||||||
|
std::is_const<element_type>::value && !detail::IsSpan<Container>::value &&
|
||||||
|
std::is_convertible<typename Container::pointer, pointer>::value &&
|
||||||
|
std::is_convertible<
|
||||||
|
typename Container::pointer,
|
||||||
|
decltype(std::declval<Container>().data())>::value>>
|
||||||
|
XGBOOST_DEVICE Span(const Container& _cont) : size_(_cont.size()), // NOLINT
|
||||||
|
data_(_cont.data()) {}
|
||||||
|
|
||||||
|
template <class U, ptrdiff_t OtherExtent,
|
||||||
|
class = typename std::enable_if<
|
||||||
|
detail::IsAllowedElementTypeConversion<U, T>::value &&
|
||||||
|
detail::IsAllowedExtentConversion<OtherExtent, Extent>::value>>
|
||||||
|
XGBOOST_DEVICE constexpr Span(const Span<U, OtherExtent>& _other) // NOLINT
|
||||||
|
__span_noexcept : size_(_other.size()), data_(_other.data()) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr Span(const Span& _other)
|
||||||
|
__span_noexcept : size_(_other.size()), data_(_other.data()) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span& operator=(const Span& _other) __span_noexcept {
|
||||||
|
size_ = _other.size();
|
||||||
|
data_ = _other.data();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE ~Span() __span_noexcept {}; // NOLINT
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr iterator begin() const __span_noexcept { // NOLINT
|
||||||
|
return {this, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr iterator end() const __span_noexcept { // NOLINT
|
||||||
|
return {this, size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr const_iterator cbegin() const __span_noexcept { // NOLINT
|
||||||
|
return {this, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr const_iterator cend() const __span_noexcept { // NOLINT
|
||||||
|
return {this, size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr reverse_iterator rbegin() const __span_noexcept { // NOLINT
|
||||||
|
return reverse_iterator{end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr reverse_iterator rend() const __span_noexcept { // NOLINT
|
||||||
|
return reverse_iterator{begin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr const_reverse_iterator crbegin() const __span_noexcept { // NOLINT
|
||||||
|
return const_reverse_iterator{cend()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr const_reverse_iterator crend() const __span_noexcept { // NOLINT
|
||||||
|
return const_reverse_iterator{cbegin()};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE reference operator[](index_type _idx) const {
|
||||||
|
SPAN_CHECK(_idx >= 0 && _idx < size());
|
||||||
|
return data()[_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr reference operator()(index_type _idx) const {
|
||||||
|
return this->operator[](_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr pointer data() const __span_noexcept { // NOLINT
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observers
|
||||||
|
XGBOOST_DEVICE constexpr index_type size() const __span_noexcept { // NOLINT
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE constexpr index_type size_bytes() const __span_noexcept { // NOLINT
|
||||||
|
return size() * sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE constexpr bool empty() const __span_noexcept { // NOLINT
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subviews
|
||||||
|
template <detail::ptrdiff_t Count >
|
||||||
|
XGBOOST_DEVICE Span<element_type, Count> first() const { // NOLINT
|
||||||
|
SPAN_CHECK(Count >= 0 && Count <= size());
|
||||||
|
return {data(), Count};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span<element_type, dynamic_extent> first( // NOLINT
|
||||||
|
detail::ptrdiff_t _count) const {
|
||||||
|
SPAN_CHECK(_count >= 0 && _count <= size());
|
||||||
|
return {data(), _count};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <detail::ptrdiff_t Count >
|
||||||
|
XGBOOST_DEVICE Span<element_type, Count> last() const { // NOLINT
|
||||||
|
SPAN_CHECK(Count >=0 && size() - Count >= 0);
|
||||||
|
return {data() + size() - Count, Count};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span<element_type, dynamic_extent> last( // NOLINT
|
||||||
|
detail::ptrdiff_t _count) const {
|
||||||
|
SPAN_CHECK(_count >= 0 && _count <= size());
|
||||||
|
return subspan(size() - _count, _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* If Count is std::dynamic_extent, r.size() == this->size() - Offset;
|
||||||
|
* Otherwise r.size() == Count.
|
||||||
|
*/
|
||||||
|
template < detail::ptrdiff_t Offset,
|
||||||
|
detail::ptrdiff_t Count = dynamic_extent >
|
||||||
|
XGBOOST_DEVICE auto subspan() const -> // NOLINT
|
||||||
|
Span<element_type,
|
||||||
|
detail::ExtentValue<Extent, Offset, Count>::value> {
|
||||||
|
SPAN_CHECK(Offset >= 0 && Offset < size());
|
||||||
|
SPAN_CHECK(Count == dynamic_extent ||
|
||||||
|
Count >= 0 && Offset + Count <= size());
|
||||||
|
|
||||||
|
return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
|
||||||
|
}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE Span<element_type, dynamic_extent> subspan( // NOLINT
|
||||||
|
detail::ptrdiff_t _offset,
|
||||||
|
detail::ptrdiff_t _count = dynamic_extent) const {
|
||||||
|
SPAN_CHECK(_offset >= 0 && _offset < size());
|
||||||
|
SPAN_CHECK(_count == dynamic_extent ||
|
||||||
|
_count >= 0 && _offset + _count <= size());
|
||||||
|
|
||||||
|
return {data() + _offset, _count ==
|
||||||
|
dynamic_extent ? size() - _offset : _count};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
index_type size_;
|
||||||
|
pointer data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE bool operator==(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
if (l.size() != r.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto l_beg = l.cbegin(), r_beg = r.cbegin(); l_beg != l.cend();
|
||||||
|
++l_beg, ++r_beg) {
|
||||||
|
if (*l_beg != *r_beg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE constexpr bool operator!=(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
return !(l == r);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE constexpr bool operator<(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
return detail::LexicographicalCompare(l.begin(), l.end(),
|
||||||
|
r.begin(), r.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE constexpr bool operator<=(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
return !(l > r);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE constexpr bool operator>(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
return detail::LexicographicalCompare<
|
||||||
|
typename Span<T, X>::iterator, typename Span<U, Y>::iterator,
|
||||||
|
detail::Greater<typename Span<T, X>::element_type>>(l.begin(), l.end(),
|
||||||
|
r.begin(), r.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t X, class U, detail::ptrdiff_t Y>
|
||||||
|
XGBOOST_DEVICE constexpr bool operator>=(Span<T, X> l, Span<U, Y> r) {
|
||||||
|
return !(l < r);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t E>
|
||||||
|
XGBOOST_DEVICE auto as_bytes(Span<T, E> s) __span_noexcept -> // NOLINT
|
||||||
|
Span<const byte, detail::ExtentAsBytesValue<T, E>::value> {
|
||||||
|
return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, detail::ptrdiff_t E>
|
||||||
|
XGBOOST_DEVICE auto as_writable_bytes(Span<T, E> s) __span_noexcept -> // NOLINT
|
||||||
|
Span<byte, detail::ExtentAsBytesValue<T, E>::value> {
|
||||||
|
return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace xgboost
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) &&_MSC_VER < 1910
|
||||||
|
#undef constexpr
|
||||||
|
#pragma pop_macro("constexpr")
|
||||||
|
#undef __span_noexcept
|
||||||
|
#endif // _MSC_VER < 1910
|
||||||
|
|
||||||
|
#endif // XGBOOST_COMMON_SPAN_H_
|
||||||
@ -58,8 +58,8 @@ void SimpleDMatrix::MakeOneBatch(SparsePage* pcol, bool sorted) {
|
|||||||
for (long i = 0; i < batch_size; ++i) { // NOLINT(*)
|
for (long i = 0; i < batch_size; ++i) { // NOLINT(*)
|
||||||
int tid = omp_get_thread_num();
|
int tid = omp_get_thread_num();
|
||||||
auto inst = batch[i];
|
auto inst = batch[i];
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
for (auto& ins : inst) {
|
||||||
builder.AddBudget(inst[j].index, tid);
|
builder.AddBudget(ins.index, tid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,11 +72,11 @@ void SimpleDMatrix::MakeOneBatch(SparsePage* pcol, bool sorted) {
|
|||||||
for (long i = 0; i < static_cast<long>(batch.Size()); ++i) { // NOLINT(*)
|
for (long i = 0; i < static_cast<long>(batch.Size()); ++i) { // NOLINT(*)
|
||||||
int tid = omp_get_thread_num();
|
int tid = omp_get_thread_num();
|
||||||
auto inst = batch[i];
|
auto inst = batch[i];
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
for (auto& ins : inst) {
|
||||||
builder.Push(
|
builder.Push(ins.index,
|
||||||
inst[j].index,
|
Entry(static_cast<bst_uint>(batch.base_rowid + i),
|
||||||
Entry(static_cast<bst_uint>(batch.base_rowid + i), inst[j].fvalue),
|
ins.fvalue),
|
||||||
tid);
|
tid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class SimpleDMatrix : public DMatrix {
|
|||||||
|
|
||||||
size_t GetColSize(size_t cidx) const override {
|
size_t GetColSize(size_t cidx) const override {
|
||||||
auto& batch = *col_iter_.column_page_;
|
auto& batch = *col_iter_.column_page_;
|
||||||
return batch[cidx].length;
|
return batch[cidx].size();
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetColDensity(size_t cidx) const override {
|
float GetColDensity(size_t cidx) const override {
|
||||||
|
|||||||
@ -166,9 +166,9 @@ class GBLinear : public GradientBooster {
|
|||||||
for (int gid = 0; gid < ngroup; ++gid) {
|
for (int gid = 0; gid < ngroup; ++gid) {
|
||||||
bst_float *p_contribs = &contribs[(row_idx * ngroup + gid) * ncolumns];
|
bst_float *p_contribs = &contribs[(row_idx * ngroup + gid) * ncolumns];
|
||||||
// calculate linear terms' contributions
|
// calculate linear terms' contributions
|
||||||
for (bst_uint c = 0; c < inst.length; ++c) {
|
for (auto& ins : inst) {
|
||||||
if (inst[c].index >= model_.param.num_feature) continue;
|
if (ins.index >= model_.param.num_feature) continue;
|
||||||
p_contribs[inst[c].index] = inst[c].fvalue * model_[inst[c].index][gid];
|
p_contribs[ins.index] = ins.fvalue * model_[ins.index][gid];
|
||||||
}
|
}
|
||||||
// add base margin to BIAS
|
// add base margin to BIAS
|
||||||
p_contribs[ncolumns - 1] = model_.bias()[gid] +
|
p_contribs[ncolumns - 1] = model_.bias()[gid] +
|
||||||
@ -268,9 +268,9 @@ class GBLinear : public GradientBooster {
|
|||||||
inline void Pred(const SparsePage::Inst &inst, bst_float *preds, int gid,
|
inline void Pred(const SparsePage::Inst &inst, bst_float *preds, int gid,
|
||||||
bst_float base) {
|
bst_float base) {
|
||||||
bst_float psum = model_.bias()[gid] + base;
|
bst_float psum = model_.bias()[gid] + base;
|
||||||
for (bst_uint i = 0; i < inst.length; ++i) {
|
for (const auto& ins : inst) {
|
||||||
if (inst[i].index >= model_.param.num_feature) continue;
|
if (ins.index >= model_.param.num_feature) continue;
|
||||||
psum += inst[i].fvalue * model_[inst[i].index][gid];
|
psum += ins.fvalue * model_[ins.index][gid];
|
||||||
}
|
}
|
||||||
preds[gid] = psum;
|
preds[gid] = psum;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,7 +69,7 @@ inline std::pair<double, double> GetGradient(int group_idx, int num_group, int f
|
|||||||
while (iter->Next()) {
|
while (iter->Next()) {
|
||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
auto col = batch[fidx];
|
auto col = batch[fidx];
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_float v = col[j].fvalue;
|
const bst_float v = col[j].fvalue;
|
||||||
auto &p = gpair[col[j].index * num_group + group_idx];
|
auto &p = gpair[col[j].index * num_group + group_idx];
|
||||||
@ -100,7 +100,7 @@ inline std::pair<double, double> GetGradientParallel(int group_idx, int num_grou
|
|||||||
while (iter->Next()) {
|
while (iter->Next()) {
|
||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
auto col = batch[fidx];
|
auto col = batch[fidx];
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static) reduction(+ : sum_grad, sum_hess)
|
#pragma omp parallel for schedule(static) reduction(+ : sum_grad, sum_hess)
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_float v = col[j].fvalue;
|
const bst_float v = col[j].fvalue;
|
||||||
@ -159,7 +159,7 @@ inline void UpdateResidualParallel(int fidx, int group_idx, int num_group,
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
auto col = batch[fidx];
|
auto col = batch[fidx];
|
||||||
// update grad value
|
// update grad value
|
||||||
const auto num_row = static_cast<bst_omp_uint>(col.length);
|
const auto num_row = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint j = 0; j < num_row; ++j) {
|
for (bst_omp_uint j = 0; j < num_row; ++j) {
|
||||||
GradientPair &p = (*in_gpair)[col[j].index * num_group + group_idx];
|
GradientPair &p = (*in_gpair)[col[j].index * num_group + group_idx];
|
||||||
@ -331,7 +331,7 @@ class GreedyFeatureSelector : public FeatureSelector {
|
|||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint i = 0; i < nfeat; ++i) {
|
for (bst_omp_uint i = 0; i < nfeat; ++i) {
|
||||||
const auto col = batch[i];
|
const auto col = batch[i];
|
||||||
const bst_uint ndata = col.length;
|
const bst_uint ndata = col.size();
|
||||||
auto &sums = gpair_sums_[group_idx * nfeat + i];
|
auto &sums = gpair_sums_[group_idx * nfeat + i];
|
||||||
for (bst_uint j = 0u; j < ndata; ++j) {
|
for (bst_uint j = 0u; j < ndata; ++j) {
|
||||||
const bst_float v = col[j].fvalue;
|
const bst_float v = col[j].fvalue;
|
||||||
@ -399,7 +399,7 @@ class ThriftyFeatureSelector : public FeatureSelector {
|
|||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint i = 0; i < nfeat; ++i) {
|
for (bst_omp_uint i = 0; i < nfeat; ++i) {
|
||||||
const auto col = batch[i];
|
const auto col = batch[i];
|
||||||
const bst_uint ndata = col.length;
|
const bst_uint ndata = col.size();
|
||||||
for (bst_uint gid = 0u; gid < ngroup; ++gid) {
|
for (bst_uint gid = 0u; gid < ngroup; ++gid) {
|
||||||
auto &sums = gpair_sums_[gid * nfeat + i];
|
auto &sums = gpair_sums_[gid * nfeat + i];
|
||||||
for (bst_uint j = 0u; j < ndata; ++j) {
|
for (bst_uint j = 0u; j < ndata; ++j) {
|
||||||
|
|||||||
@ -118,13 +118,13 @@ class DeviceShard {
|
|||||||
return e1.index < e2.index;
|
return e1.index < e2.index;
|
||||||
};
|
};
|
||||||
auto column_begin =
|
auto column_begin =
|
||||||
std::lower_bound(col.data, col.data + col.length,
|
std::lower_bound(col.data(), col.data() + col.size(),
|
||||||
Entry(row_begin, 0.0f), cmp);
|
Entry(row_begin, 0.0f), cmp);
|
||||||
auto column_end =
|
auto column_end =
|
||||||
std::upper_bound(col.data, col.data + col.length,
|
std::upper_bound(col.data(), col.data() + col.size(),
|
||||||
Entry(row_end, 0.0f), cmp);
|
Entry(row_end, 0.0f), cmp);
|
||||||
column_segments.push_back(
|
column_segments.push_back(
|
||||||
std::make_pair(column_begin - col.data, column_end - col.data));
|
std::make_pair(column_begin - col.data(), column_end - col.data()));
|
||||||
row_ptr_.push_back(row_ptr_.back() + column_end - column_begin);
|
row_ptr_.push_back(row_ptr_.back() + column_end - column_begin);
|
||||||
}
|
}
|
||||||
ba_.Allocate(device_idx, param.silent, &data_, row_ptr_.back(), &gpair_,
|
ba_.Allocate(device_idx, param.silent, &data_, row_ptr_.back(), &gpair_,
|
||||||
@ -134,7 +134,7 @@ class DeviceShard {
|
|||||||
auto col = batch[fidx];
|
auto col = batch[fidx];
|
||||||
auto seg = column_segments[fidx];
|
auto seg = column_segments[fidx];
|
||||||
dh::safe_cuda(cudaMemcpy(
|
dh::safe_cuda(cudaMemcpy(
|
||||||
data_.Data() + row_ptr_[fidx], col.data + seg.first,
|
data_.Data() + row_ptr_[fidx], col.data() + seg.first,
|
||||||
sizeof(Entry) * (seg.second - seg.first), cudaMemcpyHostToDevice));
|
sizeof(Entry) * (seg.second - seg.first), cudaMemcpyHostToDevice));
|
||||||
}
|
}
|
||||||
// Rescale indices with respect to current shard
|
// Rescale indices with respect to current shard
|
||||||
|
|||||||
@ -92,10 +92,10 @@ class ShotgunUpdater : public LinearUpdater {
|
|||||||
auto col = batch[ii];
|
auto col = batch[ii];
|
||||||
for (int gid = 0; gid < ngroup; ++gid) {
|
for (int gid = 0; gid < ngroup; ++gid) {
|
||||||
double sum_grad = 0.0, sum_hess = 0.0;
|
double sum_grad = 0.0, sum_hess = 0.0;
|
||||||
for (bst_uint j = 0; j < col.length; ++j) {
|
for (auto& c : col) {
|
||||||
GradientPair &p = gpair[col[j].index * ngroup + gid];
|
GradientPair &p = gpair[c.index * ngroup + gid];
|
||||||
if (p.GetHess() < 0.0f) continue;
|
if (p.GetHess() < 0.0f) continue;
|
||||||
const bst_float v = col[j].fvalue;
|
const bst_float v = c.fvalue;
|
||||||
sum_grad += p.GetGrad() * v;
|
sum_grad += p.GetGrad() * v;
|
||||||
sum_hess += p.GetHess() * v * v;
|
sum_hess += p.GetHess() * v * v;
|
||||||
}
|
}
|
||||||
@ -107,10 +107,10 @@ class ShotgunUpdater : public LinearUpdater {
|
|||||||
if (dw == 0.f) continue;
|
if (dw == 0.f) continue;
|
||||||
w += dw;
|
w += dw;
|
||||||
// update grad values
|
// update grad values
|
||||||
for (bst_uint j = 0; j < col.length; ++j) {
|
for (auto& c : col) {
|
||||||
GradientPair &p = gpair[col[j].index * ngroup + gid];
|
GradientPair &p = gpair[c.index * ngroup + gid];
|
||||||
if (p.GetHess() < 0.0f) continue;
|
if (p.GetHess() < 0.0f) continue;
|
||||||
p += GradientPair(p.GetHess() * col[j].fvalue * dw, 0);
|
p += GradientPair(p.GetHess() * c.fvalue * dw, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../common/span.h"
|
||||||
#include "../common/device_helpers.cuh"
|
#include "../common/device_helpers.cuh"
|
||||||
#include "../common/host_device_vector.h"
|
#include "../common/host_device_vector.h"
|
||||||
#include "./regression_loss.h"
|
#include "./regression_loss.h"
|
||||||
@ -44,8 +45,8 @@ struct GPURegLossParam : public dmlc::Parameter<GPURegLossParam> {
|
|||||||
// GPU kernel for gradient computation
|
// GPU kernel for gradient computation
|
||||||
template<typename Loss>
|
template<typename Loss>
|
||||||
__global__ void get_gradient_k
|
__global__ void get_gradient_k
|
||||||
(GradientPair *__restrict__ out_gpair, unsigned int *__restrict__ label_correct,
|
(common::Span<GradientPair> out_gpair, common::Span<unsigned int> label_correct,
|
||||||
const float * __restrict__ preds, const float * __restrict__ labels,
|
common::Span<const float> preds, common::Span<const float> labels,
|
||||||
const float * __restrict__ weights, int n, float scale_pos_weight) {
|
const float * __restrict__ weights, int n, float scale_pos_weight) {
|
||||||
int i = threadIdx.x + blockIdx.x * blockDim.x;
|
int i = threadIdx.x + blockIdx.x * blockDim.x;
|
||||||
if (i >= n)
|
if (i >= n)
|
||||||
@ -56,14 +57,14 @@ __global__ void get_gradient_k
|
|||||||
if (label == 1.0f)
|
if (label == 1.0f)
|
||||||
w *= scale_pos_weight;
|
w *= scale_pos_weight;
|
||||||
if (!Loss::CheckLabel(label))
|
if (!Loss::CheckLabel(label))
|
||||||
atomicAnd(label_correct, 0);
|
atomicAnd(label_correct.data(), 0);
|
||||||
out_gpair[i] = GradientPair
|
out_gpair[i] = GradientPair
|
||||||
(Loss::FirstOrderGradient(p, label) * w, Loss::SecondOrderGradient(p, label) * w);
|
(Loss::FirstOrderGradient(p, label) * w, Loss::SecondOrderGradient(p, label) * w);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GPU kernel for predicate transformation
|
// GPU kernel for predicate transformation
|
||||||
template<typename Loss>
|
template<typename Loss>
|
||||||
__global__ void pred_transform_k(float * __restrict__ preds, int n) {
|
__global__ void pred_transform_k(common::Span<float> preds, int n) {
|
||||||
int i = threadIdx.x + blockIdx.x * blockDim.x;
|
int i = threadIdx.x + blockIdx.x * blockDim.x;
|
||||||
if (i >= n)
|
if (i >= n)
|
||||||
return;
|
return;
|
||||||
@ -144,8 +145,8 @@ class GPURegLossObj : public ObjFunction {
|
|||||||
size_t n = preds->DeviceSize(d);
|
size_t n = preds->DeviceSize(d);
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
get_gradient_k<Loss><<<dh::DivRoundUp(n, block), block>>>
|
get_gradient_k<Loss><<<dh::DivRoundUp(n, block), block>>>
|
||||||
(out_gpair->DevicePointer(d), label_correct_.DevicePointer(d),
|
(out_gpair->DeviceSpan(d), label_correct_.DeviceSpan(d),
|
||||||
preds->DevicePointer(d), labels_.DevicePointer(d),
|
preds->DeviceSpan(d), labels_.DeviceSpan(d),
|
||||||
info.weights_.size() > 0 ? weights_.DevicePointer(d) : nullptr,
|
info.weights_.size() > 0 ? weights_.DevicePointer(d) : nullptr,
|
||||||
n, param_.scale_pos_weight);
|
n, param_.scale_pos_weight);
|
||||||
dh::safe_cuda(cudaGetLastError());
|
dh::safe_cuda(cudaGetLastError());
|
||||||
@ -180,7 +181,8 @@ class GPURegLossObj : public ObjFunction {
|
|||||||
const int block = 256;
|
const int block = 256;
|
||||||
size_t n = preds->DeviceSize(d);
|
size_t n = preds->DeviceSize(d);
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
pred_transform_k<Loss><<<dh::DivRoundUp(n, block), block>>>(preds->DevicePointer(d), n);
|
pred_transform_k<Loss><<<dh::DivRoundUp(n, block), block>>>(
|
||||||
|
preds->DeviceSpan(d), n);
|
||||||
dh::safe_cuda(cudaGetLastError());
|
dh::safe_cuda(cudaGetLastError());
|
||||||
}
|
}
|
||||||
dh::safe_cuda(cudaDeviceSynchronize());
|
dh::safe_cuda(cudaDeviceSynchronize());
|
||||||
|
|||||||
@ -49,9 +49,9 @@ class BaseMaker: public TreeUpdater {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
for (bst_uint fid = 0; fid < batch.Size(); ++fid) {
|
for (bst_uint fid = 0; fid < batch.Size(); ++fid) {
|
||||||
auto c = batch[fid];
|
auto c = batch[fid];
|
||||||
if (c.length != 0) {
|
if (c.size() != 0) {
|
||||||
fminmax_[fid * 2 + 0] = std::max(-c[0].fvalue, fminmax_[fid * 2 + 0]);
|
fminmax_[fid * 2 + 0] = std::max(-c[0].fvalue, fminmax_[fid * 2 + 0]);
|
||||||
fminmax_[fid * 2 + 1] = std::max(c[c.length - 1].fvalue, fminmax_[fid * 2 + 1]);
|
fminmax_[fid * 2 + 1] = std::max(c[c.size() - 1].fvalue, fminmax_[fid * 2 + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,9 +106,9 @@ class BaseMaker: public TreeUpdater {
|
|||||||
inline static int NextLevel(const SparsePage::Inst &inst, const RegTree &tree, int nid) {
|
inline static int NextLevel(const SparsePage::Inst &inst, const RegTree &tree, int nid) {
|
||||||
const RegTree::Node &n = tree[nid];
|
const RegTree::Node &n = tree[nid];
|
||||||
bst_uint findex = n.SplitIndex();
|
bst_uint findex = n.SplitIndex();
|
||||||
for (unsigned i = 0; i < inst.length; ++i) {
|
for (const auto& ins : inst) {
|
||||||
if (findex == inst[i].index) {
|
if (findex == ins.index) {
|
||||||
if (inst[i].fvalue < n.SplitCond()) {
|
if (ins.fvalue < n.SplitCond()) {
|
||||||
return n.LeftChild();
|
return n.LeftChild();
|
||||||
} else {
|
} else {
|
||||||
return n.RightChild();
|
return n.RightChild();
|
||||||
@ -250,7 +250,7 @@ class BaseMaker: public TreeUpdater {
|
|||||||
auto it = std::lower_bound(sorted_split_set.begin(), sorted_split_set.end(), fid);
|
auto it = std::lower_bound(sorted_split_set.begin(), sorted_split_set.end(), fid);
|
||||||
|
|
||||||
if (it != sorted_split_set.end() && *it == fid) {
|
if (it != sorted_split_set.end() && *it == fid) {
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_uint ridx = col[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
@ -308,7 +308,7 @@ class BaseMaker: public TreeUpdater {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
for (auto fid : fsplits) {
|
for (auto fid : fsplits) {
|
||||||
auto col = batch[fid];
|
auto col = batch[fid];
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_uint ridx = col[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
|
|||||||
@ -269,7 +269,7 @@ class ColMaker: public TreeUpdater {
|
|||||||
const std::vector<GradientPair> &gpair) {
|
const std::vector<GradientPair> &gpair) {
|
||||||
// TODO(tqchen): double check stats order.
|
// TODO(tqchen): double check stats order.
|
||||||
const MetaInfo& info = fmat.Info();
|
const MetaInfo& info = fmat.Info();
|
||||||
const bool ind = col.length != 0 && col.data[0].fvalue == col.data[col.length - 1].fvalue;
|
const bool ind = col.size() != 0 && col[0].fvalue == col[col.size() - 1].fvalue;
|
||||||
bool need_forward = param_.NeedForwardSearch(fmat.GetColDensity(fid), ind);
|
bool need_forward = param_.NeedForwardSearch(fmat.GetColDensity(fid), ind);
|
||||||
bool need_backward = param_.NeedBackwardSearch(fmat.GetColDensity(fid), ind);
|
bool need_backward = param_.NeedBackwardSearch(fmat.GetColDensity(fid), ind);
|
||||||
const std::vector<int> &qexpand = qexpand_;
|
const std::vector<int> &qexpand = qexpand_;
|
||||||
@ -281,8 +281,8 @@ class ColMaker: public TreeUpdater {
|
|||||||
for (int j : qexpand) {
|
for (int j : qexpand) {
|
||||||
temp[j].stats.Clear();
|
temp[j].stats.Clear();
|
||||||
}
|
}
|
||||||
bst_uint step = (col.length + this->nthread_ - 1) / this->nthread_;
|
bst_uint step = (col.size() + this->nthread_ - 1) / this->nthread_;
|
||||||
bst_uint end = std::min(col.length, step * (tid + 1));
|
bst_uint end = std::min(static_cast<bst_uint>(col.size()), step * (tid + 1));
|
||||||
for (bst_uint i = tid * step; i < end; ++i) {
|
for (bst_uint i = tid * step; i < end; ++i) {
|
||||||
const bst_uint ridx = col[i].index;
|
const bst_uint ridx = col[i].index;
|
||||||
const int nid = position_[ridx];
|
const int nid = position_[ridx];
|
||||||
@ -363,8 +363,8 @@ class ColMaker: public TreeUpdater {
|
|||||||
GradStats c(param_), cright(param_);
|
GradStats c(param_), cright(param_);
|
||||||
const int tid = omp_get_thread_num();
|
const int tid = omp_get_thread_num();
|
||||||
std::vector<ThreadEntry> &temp = stemp_[tid];
|
std::vector<ThreadEntry> &temp = stemp_[tid];
|
||||||
bst_uint step = (col.length + this->nthread_ - 1) / this->nthread_;
|
bst_uint step = (col.size() + this->nthread_ - 1) / this->nthread_;
|
||||||
bst_uint end = std::min(col.length, step * (tid + 1));
|
bst_uint end = std::min(static_cast<bst_uint>(col.size()), step * (tid + 1));
|
||||||
for (bst_uint i = tid * step; i < end; ++i) {
|
for (bst_uint i = tid * step; i < end; ++i) {
|
||||||
const bst_uint ridx = col[i].index;
|
const bst_uint ridx = col[i].index;
|
||||||
const int nid = position_[ridx];
|
const int nid = position_[ridx];
|
||||||
@ -620,13 +620,13 @@ class ColMaker: public TreeUpdater {
|
|||||||
int fid = feat_set[i];
|
int fid = feat_set[i];
|
||||||
const int tid = omp_get_thread_num();
|
const int tid = omp_get_thread_num();
|
||||||
auto c = batch[fid];
|
auto c = batch[fid];
|
||||||
const bool ind = c.length != 0 && c.data[0].fvalue == c.data[c.length - 1].fvalue;
|
const bool ind = c.size() != 0 && c[0].fvalue == c[c.size() - 1].fvalue;
|
||||||
if (param_.NeedForwardSearch(fmat.GetColDensity(fid), ind)) {
|
if (param_.NeedForwardSearch(fmat.GetColDensity(fid), ind)) {
|
||||||
this->EnumerateSplit(c.data, c.data + c.length, +1,
|
this->EnumerateSplit(c.data(), c.data() + c.size(), +1,
|
||||||
fid, gpair, info, stemp_[tid]);
|
fid, gpair, info, stemp_[tid]);
|
||||||
}
|
}
|
||||||
if (param_.NeedBackwardSearch(fmat.GetColDensity(fid), ind)) {
|
if (param_.NeedBackwardSearch(fmat.GetColDensity(fid), ind)) {
|
||||||
this->EnumerateSplit(c.data + c.length - 1, c.data - 1, -1,
|
this->EnumerateSplit(c.data() + c.size() - 1, c.data() - 1, -1,
|
||||||
fid, gpair, info, stemp_[tid]);
|
fid, gpair, info, stemp_[tid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -734,7 +734,7 @@ class ColMaker: public TreeUpdater {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
for (auto fid : fsplits) {
|
for (auto fid : fsplits) {
|
||||||
auto col = batch[fid];
|
auto col = batch[fid];
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_uint ridx = col[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
@ -865,7 +865,7 @@ class DistColMaker : public ColMaker {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
for (auto fid : fsplits) {
|
for (auto fid : fsplits) {
|
||||||
auto col = batch[fid];
|
auto col = batch[fid];
|
||||||
const auto ndata = static_cast<bst_omp_uint>(col.length);
|
const auto ndata = static_cast<bst_omp_uint>(col.size());
|
||||||
#pragma omp parallel for schedule(static)
|
#pragma omp parallel for schedule(static)
|
||||||
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
for (bst_omp_uint j = 0; j < ndata; ++j) {
|
||||||
const bst_uint ridx = col[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
|
|||||||
@ -669,7 +669,7 @@ class GPUMaker : public TreeUpdater {
|
|||||||
auto &batch = iter->Value();
|
auto &batch = iter->Value();
|
||||||
for (int i = 0; i < batch.Size(); i++) {
|
for (int i = 0; i < batch.Size(); i++) {
|
||||||
auto col = batch[i];
|
auto col = batch[i];
|
||||||
for (const Entry* it = col.data; it != col.data + col.length;
|
for (const Entry* it = col.data(); it != col.data() + col.size();
|
||||||
it++) {
|
it++) {
|
||||||
int inst_id = static_cast<int>(it->index);
|
int inst_id = static_cast<int>(it->index);
|
||||||
fval->push_back(it->fvalue);
|
fval->push_back(it->fvalue);
|
||||||
|
|||||||
@ -496,13 +496,13 @@ class CQHistMaker: public HistMaker<TStats> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void UpdateHistCol(const std::vector<GradientPair> &gpair,
|
inline void UpdateHistCol(const std::vector<GradientPair> &gpair,
|
||||||
const SparsePage::Inst &c,
|
const SparsePage::Inst &col,
|
||||||
const MetaInfo &info,
|
const MetaInfo &info,
|
||||||
const RegTree &tree,
|
const RegTree &tree,
|
||||||
const std::vector<bst_uint> &fset,
|
const std::vector<bst_uint> &fset,
|
||||||
bst_uint fid_offset,
|
bst_uint fid_offset,
|
||||||
std::vector<HistEntry> *p_temp) {
|
std::vector<HistEntry> *p_temp) {
|
||||||
if (c.length == 0) return;
|
if (col.size() == 0) return;
|
||||||
// initialize sbuilder for use
|
// initialize sbuilder for use
|
||||||
std::vector<HistEntry> &hbuilder = *p_temp;
|
std::vector<HistEntry> &hbuilder = *p_temp;
|
||||||
hbuilder.resize(tree.param.num_nodes);
|
hbuilder.resize(tree.param.num_nodes);
|
||||||
@ -514,46 +514,46 @@ class CQHistMaker: public HistMaker<TStats> {
|
|||||||
}
|
}
|
||||||
if (TStats::kSimpleStats != 0 && this->param_.cache_opt != 0) {
|
if (TStats::kSimpleStats != 0 && this->param_.cache_opt != 0) {
|
||||||
constexpr bst_uint kBuffer = 32;
|
constexpr bst_uint kBuffer = 32;
|
||||||
bst_uint align_length = c.length / kBuffer * kBuffer;
|
bst_uint align_length = col.size() / kBuffer * kBuffer;
|
||||||
int buf_position[kBuffer];
|
int buf_position[kBuffer];
|
||||||
GradientPair buf_gpair[kBuffer];
|
GradientPair buf_gpair[kBuffer];
|
||||||
for (bst_uint j = 0; j < align_length; j += kBuffer) {
|
for (bst_uint j = 0; j < align_length; j += kBuffer) {
|
||||||
for (bst_uint i = 0; i < kBuffer; ++i) {
|
for (bst_uint i = 0; i < kBuffer; ++i) {
|
||||||
bst_uint ridx = c[j + i].index;
|
bst_uint ridx = col[j + i].index;
|
||||||
buf_position[i] = this->position_[ridx];
|
buf_position[i] = this->position_[ridx];
|
||||||
buf_gpair[i] = gpair[ridx];
|
buf_gpair[i] = gpair[ridx];
|
||||||
}
|
}
|
||||||
for (bst_uint i = 0; i < kBuffer; ++i) {
|
for (bst_uint i = 0; i < kBuffer; ++i) {
|
||||||
const int nid = buf_position[i];
|
const int nid = buf_position[i];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
hbuilder[nid].Add(c[j + i].fvalue, buf_gpair[i]);
|
hbuilder[nid].Add(col[j + i].fvalue, buf_gpair[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (bst_uint j = align_length; j < c.length; ++j) {
|
for (bst_uint j = align_length; j < col.size(); ++j) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
hbuilder[nid].Add(c[j].fvalue, gpair[ridx]);
|
hbuilder[nid].Add(col[j].fvalue, gpair[ridx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (bst_uint j = 0; j < c.length; ++j) {
|
for (const auto& c : col) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = c.index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
hbuilder[nid].Add(c[j].fvalue, gpair, info, ridx);
|
hbuilder[nid].Add(c.fvalue, gpair, info, ridx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inline void UpdateSketchCol(const std::vector<GradientPair> &gpair,
|
inline void UpdateSketchCol(const std::vector<GradientPair> &gpair,
|
||||||
const SparsePage::Inst &c,
|
const SparsePage::Inst &col,
|
||||||
const RegTree &tree,
|
const RegTree &tree,
|
||||||
size_t work_set_size,
|
size_t work_set_size,
|
||||||
bst_uint offset,
|
bst_uint offset,
|
||||||
std::vector<BaseMaker::SketchEntry> *p_temp) {
|
std::vector<BaseMaker::SketchEntry> *p_temp) {
|
||||||
if (c.length == 0) return;
|
if (col.size() == 0) return;
|
||||||
// initialize sbuilder for use
|
// initialize sbuilder for use
|
||||||
std::vector<BaseMaker::SketchEntry> &sbuilder = *p_temp;
|
std::vector<BaseMaker::SketchEntry> &sbuilder = *p_temp;
|
||||||
sbuilder.resize(tree.param.num_nodes);
|
sbuilder.resize(tree.param.num_nodes);
|
||||||
@ -565,18 +565,18 @@ class CQHistMaker: public HistMaker<TStats> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// first pass, get sum of weight, TODO, optimization to skip first pass
|
// first pass, get sum of weight, TODO, optimization to skip first pass
|
||||||
for (bst_uint j = 0; j < c.length; ++j) {
|
for (const auto& c : col) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = c.index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
sbuilder[nid].sum_total += gpair[ridx].GetHess();
|
sbuilder[nid].sum_total += gpair[ridx].GetHess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if only one value, no need to do second pass
|
// if only one value, no need to do second pass
|
||||||
if (c[0].fvalue == c[c.length-1].fvalue) {
|
if (col[0].fvalue == col[col.size()-1].fvalue) {
|
||||||
for (size_t i = 0; i < this->qexpand_.size(); ++i) {
|
for (size_t i = 0; i < this->qexpand_.size(); ++i) {
|
||||||
const int nid = this->qexpand_[i];
|
const int nid = this->qexpand_[i];
|
||||||
sbuilder[nid].sketch->Push(c[0].fvalue, static_cast<bst_float>(sbuilder[nid].sum_total));
|
sbuilder[nid].sketch->Push(col[0].fvalue, static_cast<bst_float>(sbuilder[nid].sum_total));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -589,35 +589,35 @@ class CQHistMaker: public HistMaker<TStats> {
|
|||||||
// second pass, build the sketch
|
// second pass, build the sketch
|
||||||
if (TStats::kSimpleStats != 0 && this->param_.cache_opt != 0) {
|
if (TStats::kSimpleStats != 0 && this->param_.cache_opt != 0) {
|
||||||
constexpr bst_uint kBuffer = 32;
|
constexpr bst_uint kBuffer = 32;
|
||||||
bst_uint align_length = c.length / kBuffer * kBuffer;
|
bst_uint align_length = col.size() / kBuffer * kBuffer;
|
||||||
int buf_position[kBuffer];
|
int buf_position[kBuffer];
|
||||||
bst_float buf_hess[kBuffer];
|
bst_float buf_hess[kBuffer];
|
||||||
for (bst_uint j = 0; j < align_length; j += kBuffer) {
|
for (bst_uint j = 0; j < align_length; j += kBuffer) {
|
||||||
for (bst_uint i = 0; i < kBuffer; ++i) {
|
for (bst_uint i = 0; i < kBuffer; ++i) {
|
||||||
bst_uint ridx = c[j + i].index;
|
bst_uint ridx = col[j + i].index;
|
||||||
buf_position[i] = this->position_[ridx];
|
buf_position[i] = this->position_[ridx];
|
||||||
buf_hess[i] = gpair[ridx].GetHess();
|
buf_hess[i] = gpair[ridx].GetHess();
|
||||||
}
|
}
|
||||||
for (bst_uint i = 0; i < kBuffer; ++i) {
|
for (bst_uint i = 0; i < kBuffer; ++i) {
|
||||||
const int nid = buf_position[i];
|
const int nid = buf_position[i];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
sbuilder[nid].Push(c[j + i].fvalue, buf_hess[i], max_size);
|
sbuilder[nid].Push(col[j + i].fvalue, buf_hess[i], max_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (bst_uint j = align_length; j < c.length; ++j) {
|
for (bst_uint j = align_length; j < col.size(); ++j) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = col[j].index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
sbuilder[nid].Push(c[j].fvalue, gpair[ridx].GetHess(), max_size);
|
sbuilder[nid].Push(col[j].fvalue, gpair[ridx].GetHess(), max_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (bst_uint j = 0; j < c.length; ++j) {
|
for (const auto& c : col) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = c.index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
sbuilder[nid].Push(c[j].fvalue, gpair[ridx].GetHess(), max_size);
|
sbuilder[nid].Push(c.fvalue, gpair[ridx].GetHess(), max_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,8 +794,8 @@ class QuantileHistMaker: public HistMaker<TStats> {
|
|||||||
if (this->node2workindex_[nid] < 0) {
|
if (this->node2workindex_[nid] < 0) {
|
||||||
this->position_[ridx] = ~nid;
|
this->position_[ridx] = ~nid;
|
||||||
} else {
|
} else {
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
for (auto& ins : inst) {
|
||||||
builder.AddBudget(inst[j].index, omp_get_thread_num());
|
builder.AddBudget(ins.index, omp_get_thread_num());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -807,9 +807,9 @@ class QuantileHistMaker: public HistMaker<TStats> {
|
|||||||
const bst_uint ridx = static_cast<bst_uint>(batch.base_rowid + i);
|
const bst_uint ridx = static_cast<bst_uint>(batch.base_rowid + i);
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
for (bst_uint j = 0; j < inst.length; ++j) {
|
for (auto& ins : inst) {
|
||||||
builder.Push(inst[j].index,
|
builder.Push(ins.index,
|
||||||
Entry(nid, inst[j].fvalue),
|
Entry(nid, ins.fvalue),
|
||||||
omp_get_thread_num());
|
omp_get_thread_num());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,7 +155,7 @@ class SketchMaker: public BaseMaker {
|
|||||||
this->UpdateSketchCol(gpair, batch[fidx], tree,
|
this->UpdateSketchCol(gpair, batch[fidx], tree,
|
||||||
node_stats_,
|
node_stats_,
|
||||||
fidx,
|
fidx,
|
||||||
batch[fidx].length == nrows,
|
batch[fidx].size() == nrows,
|
||||||
&thread_sketch_[omp_get_thread_num()]);
|
&thread_sketch_[omp_get_thread_num()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,13 +174,13 @@ class SketchMaker: public BaseMaker {
|
|||||||
}
|
}
|
||||||
// update sketch information in column fid
|
// update sketch information in column fid
|
||||||
inline void UpdateSketchCol(const std::vector<GradientPair> &gpair,
|
inline void UpdateSketchCol(const std::vector<GradientPair> &gpair,
|
||||||
const SparsePage::Inst &c,
|
const SparsePage::Inst &col,
|
||||||
const RegTree &tree,
|
const RegTree &tree,
|
||||||
const std::vector<SKStats> &nstats,
|
const std::vector<SKStats> &nstats,
|
||||||
bst_uint fid,
|
bst_uint fid,
|
||||||
bool col_full,
|
bool col_full,
|
||||||
std::vector<SketchEntry> *p_temp) {
|
std::vector<SketchEntry> *p_temp) {
|
||||||
if (c.length == 0) return;
|
if (col.size() == 0) return;
|
||||||
// initialize sbuilder for use
|
// initialize sbuilder for use
|
||||||
std::vector<SketchEntry> &sbuilder = *p_temp;
|
std::vector<SketchEntry> &sbuilder = *p_temp;
|
||||||
sbuilder.resize(tree.param.num_nodes * 3);
|
sbuilder.resize(tree.param.num_nodes * 3);
|
||||||
@ -192,10 +192,10 @@ class SketchMaker: public BaseMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!col_full) {
|
if (!col_full) {
|
||||||
for (bst_uint j = 0; j < c.length; ++j) {
|
for (const auto& c : col) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = c.index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid > 0) {
|
||||||
const GradientPair &e = gpair[ridx];
|
const GradientPair &e = gpair[ridx];
|
||||||
if (e.GetGrad() >= 0.0f) {
|
if (e.GetGrad() >= 0.0f) {
|
||||||
sbuilder[3 * nid + 0].sum_total += e.GetGrad();
|
sbuilder[3 * nid + 0].sum_total += e.GetGrad();
|
||||||
@ -213,10 +213,10 @@ class SketchMaker: public BaseMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if only one value, no need to do second pass
|
// if only one value, no need to do second pass
|
||||||
if (c[0].fvalue == c[c.length-1].fvalue) {
|
if (col[0].fvalue == col[col.size()-1].fvalue) {
|
||||||
for (int nid : this->qexpand_) {
|
for (int nid : this->qexpand_) {
|
||||||
for (int k = 0; k < 3; ++k) {
|
for (int k = 0; k < 3; ++k) {
|
||||||
sbuilder[3 * nid + k].sketch->Push(c[0].fvalue,
|
sbuilder[3 * nid + k].sketch->Push(col[0].fvalue,
|
||||||
static_cast<bst_float>(
|
static_cast<bst_float>(
|
||||||
sbuilder[3 * nid + k].sum_total));
|
sbuilder[3 * nid + k].sum_total));
|
||||||
}
|
}
|
||||||
@ -231,17 +231,17 @@ class SketchMaker: public BaseMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// second pass, build the sketch
|
// second pass, build the sketch
|
||||||
for (bst_uint j = 0; j < c.length; ++j) {
|
for (const auto& c : col) {
|
||||||
const bst_uint ridx = c[j].index;
|
const bst_uint ridx = c.index;
|
||||||
const int nid = this->position_[ridx];
|
const int nid = this->position_[ridx];
|
||||||
if (nid >= 0) {
|
if (nid >= 0) {
|
||||||
const GradientPair &e = gpair[ridx];
|
const GradientPair &e = gpair[ridx];
|
||||||
if (e.GetGrad() >= 0.0f) {
|
if (e.GetGrad() >= 0.0f) {
|
||||||
sbuilder[3 * nid + 0].Push(c[j].fvalue, e.GetGrad(), max_size);
|
sbuilder[3 * nid + 0].Push(c.fvalue, e.GetGrad(), max_size);
|
||||||
} else {
|
} else {
|
||||||
sbuilder[3 * nid + 1].Push(c[j].fvalue, -e.GetGrad(), max_size);
|
sbuilder[3 * nid + 1].Push(c.fvalue, -e.GetGrad(), max_size);
|
||||||
}
|
}
|
||||||
sbuilder[3 * nid + 2].Push(c[j].fvalue, e.GetHess(), max_size);
|
sbuilder[3 * nid + 2].Push(c.fvalue, e.GetHess(), max_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int nid : this->qexpand_) {
|
for (int nid : this->qexpand_) {
|
||||||
|
|||||||
@ -59,7 +59,7 @@ TEST(c_api, XGDMatrixCreateFromMat_omp) {
|
|||||||
auto batch = iter->Value();
|
auto batch = iter->Value();
|
||||||
for (int i = 0; i < batch.Size(); i++) {
|
for (int i = 0; i < batch.Size(); i++) {
|
||||||
auto inst = batch[i];
|
auto inst = batch[i];
|
||||||
for (int j = 0; i < inst.length; i++) {
|
for (int j = 0; i < inst.size(); i++) {
|
||||||
ASSERT_EQ(inst[j].fvalue, 1.5);
|
ASSERT_EQ(inst[j].fvalue, 1.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
tests/cpp/common/test_host_device_vector.cu
Normal file
22
tests/cpp/common/test_host_device_vector.cu
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2018 XGBoost contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "../../../src/common/host_device_vector.h"
|
||||||
|
#include "../../../src/common/device_helpers.cuh"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
TEST(HostDeviceVector, Span) {
|
||||||
|
HostDeviceVector<float> vec {1.0f, 2.0f, 3.0f, 4.0f};
|
||||||
|
vec.Reshard(GPUSet{0, 1});
|
||||||
|
auto span = vec.DeviceSpan(0);
|
||||||
|
ASSERT_EQ(vec.Size(), span.size());
|
||||||
|
ASSERT_EQ(vec.DevicePointer(0), span.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace xgboost
|
||||||
|
|
||||||
423
tests/cpp/common/test_span.cc
Normal file
423
tests/cpp/common/test_span.cc
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2018 XGBoost contributors
|
||||||
|
*/
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../../../src/common/span.h"
|
||||||
|
#include "test_span.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
TEST(Span, TestStatus) {
|
||||||
|
int status = 1;
|
||||||
|
TestTestStatus {&status}();
|
||||||
|
ASSERT_EQ(status, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, DlfConstructors) {
|
||||||
|
// Dynamic extent
|
||||||
|
{
|
||||||
|
Span<int> s;
|
||||||
|
ASSERT_EQ(s.size(), 0);
|
||||||
|
ASSERT_EQ(s.data(), nullptr);
|
||||||
|
|
||||||
|
Span<int const> cs;
|
||||||
|
ASSERT_EQ(cs.size(), 0);
|
||||||
|
ASSERT_EQ(cs.data(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static extent
|
||||||
|
{
|
||||||
|
Span<int, 0> s;
|
||||||
|
ASSERT_EQ(s.size(), 0);
|
||||||
|
ASSERT_EQ(s.data(), nullptr);
|
||||||
|
|
||||||
|
Span<int const, 0> cs;
|
||||||
|
ASSERT_EQ(cs.size(), 0);
|
||||||
|
ASSERT_EQ(cs.data(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init list.
|
||||||
|
{
|
||||||
|
Span<float> s {};
|
||||||
|
ASSERT_EQ(s.size(), 0);
|
||||||
|
ASSERT_EQ(s.data(), nullptr);
|
||||||
|
|
||||||
|
Span<int const> cs {};
|
||||||
|
ASSERT_EQ(cs.size(), 0);
|
||||||
|
ASSERT_EQ(cs.data(), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FromNullPtr) {
|
||||||
|
// dynamic extent
|
||||||
|
{
|
||||||
|
Span<float> s {nullptr, static_cast<Span<float>::index_type>(0)};
|
||||||
|
ASSERT_EQ(s.size(), 0);
|
||||||
|
ASSERT_EQ(s.data(), nullptr);
|
||||||
|
|
||||||
|
Span<float const> cs {nullptr, static_cast<Span<float>::index_type>(0)};
|
||||||
|
ASSERT_EQ(cs.size(), 0);
|
||||||
|
ASSERT_EQ(cs.data(), nullptr);
|
||||||
|
}
|
||||||
|
// static extent
|
||||||
|
{
|
||||||
|
Span<float, 0> s {nullptr, static_cast<Span<float>::index_type>(0)};
|
||||||
|
ASSERT_EQ(s.size(), 0);
|
||||||
|
ASSERT_EQ(s.data(), nullptr);
|
||||||
|
|
||||||
|
Span<float const, 0> cs {nullptr, static_cast<Span<float>::index_type>(0)};
|
||||||
|
ASSERT_EQ(cs.size(), 0);
|
||||||
|
ASSERT_EQ(cs.data(), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FromPtrLen) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr+16);
|
||||||
|
|
||||||
|
// static extent
|
||||||
|
{
|
||||||
|
Span<float> s (arr, 16);
|
||||||
|
ASSERT_EQ (s.size(), 16);
|
||||||
|
ASSERT_EQ (s.data(), arr);
|
||||||
|
|
||||||
|
for (Span<float>::index_type i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (s[i], arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<float const> cs (arr, 16);
|
||||||
|
ASSERT_EQ (cs.size(), 16);
|
||||||
|
ASSERT_EQ (cs.data(), arr);
|
||||||
|
|
||||||
|
for (Span<float const>::index_type i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (cs[i], arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
EXPECT_ANY_THROW(Span<float> tmp (arr, -1););
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamic extent
|
||||||
|
{
|
||||||
|
Span<float, 16> s (arr, 16);
|
||||||
|
ASSERT_EQ (s.size(), 16);
|
||||||
|
ASSERT_EQ (s.data(), arr);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (s[i], arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<float const, 16> cs (arr, 16);
|
||||||
|
ASSERT_EQ (cs.size(), 16);
|
||||||
|
ASSERT_EQ (cs.data(), arr);
|
||||||
|
|
||||||
|
for (Span<float const>::index_type i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (cs[i], arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FromFirstLast) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr+16);
|
||||||
|
|
||||||
|
// dynamic extent
|
||||||
|
{
|
||||||
|
Span<float> s (arr, arr + 16);
|
||||||
|
ASSERT_EQ (s.size(), 16);
|
||||||
|
ASSERT_EQ (s.data(), arr);
|
||||||
|
ASSERT_EQ (s.data() + s.size(), arr + 16);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (s[i], arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<float const> cs (arr, arr + 16);
|
||||||
|
ASSERT_EQ (cs.size(), 16);
|
||||||
|
ASSERT_EQ (cs.data(), arr);
|
||||||
|
ASSERT_EQ (cs.data() + cs.size(), arr + 16);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (cs[i], arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static extent
|
||||||
|
{
|
||||||
|
Span<float, 16> s (arr, arr + 16);
|
||||||
|
ASSERT_EQ (s.size(), 16);
|
||||||
|
ASSERT_EQ (s.data(), arr);
|
||||||
|
ASSERT_EQ (s.data() + s.size(), arr + 16);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (s[i], arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<float const> cs (arr, arr + 16);
|
||||||
|
ASSERT_EQ (cs.size(), 16);
|
||||||
|
ASSERT_EQ (cs.data(), arr);
|
||||||
|
ASSERT_EQ (cs.data() + cs.size(), arr + 16);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ (cs[i], arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BaseClass {
|
||||||
|
virtual void operator()() {}
|
||||||
|
};
|
||||||
|
struct DerivedClass : public BaseClass {
|
||||||
|
virtual void operator()() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Span, FromOther) {
|
||||||
|
|
||||||
|
// convert constructor
|
||||||
|
{
|
||||||
|
Span<DerivedClass> derived;
|
||||||
|
Span<BaseClass> base { derived };
|
||||||
|
ASSERT_EQ(base.size(), derived.size());
|
||||||
|
ASSERT_EQ(base.data(), derived.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
// default copy constructor
|
||||||
|
{
|
||||||
|
Span<float> s0 (arr);
|
||||||
|
Span<float> s1 (s0);
|
||||||
|
ASSERT_EQ(s0.size(), s1.size());
|
||||||
|
ASSERT_EQ(s0.data(), s1.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FromArray) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
{
|
||||||
|
Span<float> s (arr);
|
||||||
|
ASSERT_EQ(&arr[0], s.data());
|
||||||
|
ASSERT_EQ(s.size(), 16);
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ(arr[i], s[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Span<float, 16> s (arr);
|
||||||
|
ASSERT_EQ(&arr[0], s.data());
|
||||||
|
ASSERT_EQ(s.size(), 16);
|
||||||
|
for (size_t i = 0; i < 16; ++i) {
|
||||||
|
ASSERT_EQ(arr[i], s[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FromContainer) {
|
||||||
|
std::vector<float> vec (16);
|
||||||
|
InitializeRange(vec.begin(), vec.end());
|
||||||
|
|
||||||
|
Span<float> s(vec);
|
||||||
|
ASSERT_EQ(s.size(), vec.size());
|
||||||
|
ASSERT_EQ(s.data(), vec.data());
|
||||||
|
|
||||||
|
bool res = std::equal(vec.begin(), vec.end(), s.begin());
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, Assignment) {
|
||||||
|
int status = 1;
|
||||||
|
TestAssignment{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpanIter, Construct) {
|
||||||
|
int status = 1;
|
||||||
|
TestIterConstruct{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpanIter, Ref) {
|
||||||
|
int status = 1;
|
||||||
|
TestIterRef{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpanIter, Calculate) {
|
||||||
|
int status = 1;
|
||||||
|
TestIterCalculate{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpanIter, Compare) {
|
||||||
|
int status = 1;
|
||||||
|
TestIterCompare{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, BeginEnd) {
|
||||||
|
int status = 1;
|
||||||
|
TestBeginEnd{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, RBeginREnd) {
|
||||||
|
int status = 1;
|
||||||
|
TestRBeginREnd{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, ElementAccess) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
size_t j = 0;
|
||||||
|
for (auto i : s) {
|
||||||
|
ASSERT_EQ(i, arr[j]);
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s[16]);
|
||||||
|
EXPECT_ANY_THROW(s[-1]);
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s(16));
|
||||||
|
EXPECT_ANY_THROW(s(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, Obversers) {
|
||||||
|
int status = 1;
|
||||||
|
TestObservers{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, FirstLast) {
|
||||||
|
// static extent
|
||||||
|
{
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float, 4> first = s.first<4>();
|
||||||
|
|
||||||
|
ASSERT_EQ(first.size(), 4);
|
||||||
|
ASSERT_EQ(first.data(), arr);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < first.size(); ++i) {
|
||||||
|
ASSERT_EQ(first[i], arr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s.first<-1>());
|
||||||
|
EXPECT_ANY_THROW(s.first<17>());
|
||||||
|
EXPECT_ANY_THROW(s.first<32>());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float, 4> last = s.last<4>();
|
||||||
|
|
||||||
|
ASSERT_EQ(last.size(), 4);
|
||||||
|
ASSERT_EQ(last.data(), arr + 12);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < last.size(); ++i) {
|
||||||
|
ASSERT_EQ(last[i], arr[i+12]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s.last<-1>());
|
||||||
|
EXPECT_ANY_THROW(s.last<17>());
|
||||||
|
EXPECT_ANY_THROW(s.last<32>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// dynamic extent
|
||||||
|
{
|
||||||
|
float *arr = new float[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
Span<float> s (arr, 16);
|
||||||
|
Span<float> first = s.first(4);
|
||||||
|
|
||||||
|
ASSERT_EQ(first.size(), 4);
|
||||||
|
ASSERT_EQ(first.data(), s.data());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < first.size(); ++i) {
|
||||||
|
ASSERT_EQ(first[i], s[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s.first(-1));
|
||||||
|
EXPECT_ANY_THROW(s.first(17));
|
||||||
|
EXPECT_ANY_THROW(s.first(32));
|
||||||
|
|
||||||
|
delete [] arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
float *arr = new float[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
Span<float> s (arr, 16);
|
||||||
|
Span<float> last = s.last(4);
|
||||||
|
|
||||||
|
ASSERT_EQ(last.size(), 4);
|
||||||
|
ASSERT_EQ(last.data(), s.data() + 12);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < last.size(); ++i) {
|
||||||
|
ASSERT_EQ(s[12 + i], last[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s.last(-1));
|
||||||
|
EXPECT_ANY_THROW(s.last(17));
|
||||||
|
EXPECT_ANY_THROW(s.last(32));
|
||||||
|
|
||||||
|
delete [] arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, Subspan) {
|
||||||
|
int arr[16] {0};
|
||||||
|
Span<int> s1 (arr);
|
||||||
|
auto s2 = s1.subspan<4>();
|
||||||
|
ASSERT_EQ(s1.size() - 4, s2.size());
|
||||||
|
|
||||||
|
auto s3 = s1.subspan(2, 4);
|
||||||
|
ASSERT_EQ(s1.data() + 2, s3.data());
|
||||||
|
ASSERT_EQ(s3.size(), 4);
|
||||||
|
|
||||||
|
auto s4 = s1.subspan(2, dynamic_extent);
|
||||||
|
ASSERT_EQ(s1.data() + 2, s4.data());
|
||||||
|
ASSERT_EQ(s4.size(), s1.size() - 2);
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s1.subspan(-1, 0));
|
||||||
|
EXPECT_ANY_THROW(s1.subspan(16, 0));
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(s1.subspan<-1>());
|
||||||
|
EXPECT_ANY_THROW(s1.subspan<16>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, Compare) {
|
||||||
|
int status = 1;
|
||||||
|
TestCompare{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, AsBytes) {
|
||||||
|
int status = 1;
|
||||||
|
TestAsBytes{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Span, AsWritableBytes) {
|
||||||
|
int status = 1;
|
||||||
|
TestAsWritableBytes{&status}();
|
||||||
|
ASSERT_EQ(status, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace xgboost
|
||||||
357
tests/cpp/common/test_span.cu
Normal file
357
tests/cpp/common/test_span.cu
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2018 XGBoost contributors
|
||||||
|
*/
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <thrust/host_vector.h>
|
||||||
|
#include <thrust/device_vector.h>
|
||||||
|
#include <thrust/execution_policy.h>
|
||||||
|
|
||||||
|
#include "../../../src/common/device_helpers.cuh"
|
||||||
|
#include "../../../src/common/span.h"
|
||||||
|
#include "test_span.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
struct TestStatus {
|
||||||
|
int *status_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TestStatus () {
|
||||||
|
dh::safe_cuda(cudaMalloc(&status_, sizeof(int)));
|
||||||
|
int h_status = 1;
|
||||||
|
dh::safe_cuda(cudaMemcpy(status_, &h_status,
|
||||||
|
sizeof(int), cudaMemcpyHostToDevice));
|
||||||
|
}
|
||||||
|
~TestStatus() {
|
||||||
|
dh::safe_cuda(cudaFree(status_));
|
||||||
|
}
|
||||||
|
|
||||||
|
int get() {
|
||||||
|
int h_status;
|
||||||
|
dh::safe_cuda(cudaMemcpy(&h_status, status_,
|
||||||
|
sizeof(int), cudaMemcpyDeviceToHost));
|
||||||
|
return h_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int* data() {
|
||||||
|
return status_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
__global__ void test_from_other_kernel(Span<float> span) {
|
||||||
|
// don't get optimized out
|
||||||
|
size_t idx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||||
|
|
||||||
|
if (idx >= span.size())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Test converting different T
|
||||||
|
__global__ void test_from_other_kernel_const(Span<float const, 16> span) {
|
||||||
|
// don't get optimized out
|
||||||
|
size_t idx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||||
|
|
||||||
|
if (idx >= span.size())
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Here we just test whether the code compiles.
|
||||||
|
*/
|
||||||
|
TEST(GPUSpan, FromOther) {
|
||||||
|
thrust::host_vector<float> h_vec (16);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
// dynamic extent
|
||||||
|
{
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_from_other_kernel<<<1, 16>>>(span);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_from_other_kernel_const<<<1, 16>>>(span);
|
||||||
|
}
|
||||||
|
// static extent
|
||||||
|
{
|
||||||
|
Span<float, 16> span(d_vec.data().get(), d_vec.data().get() + 16);
|
||||||
|
test_from_other_kernel<<<1, 16>>>(span);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Span<float, 16> span(d_vec.data().get(), d_vec.data().get() + 16);
|
||||||
|
test_from_other_kernel_const<<<1, 16>>>(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, Assignment) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestAssignment{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, TestStatus) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestTestStatus{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct TestEqual {
|
||||||
|
T *lhs_, *rhs_;
|
||||||
|
int *status_;
|
||||||
|
|
||||||
|
TestEqual(T* _lhs, T* _rhs, int * _status) :
|
||||||
|
lhs_(_lhs), rhs_(_rhs), status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()(size_t _idx) {
|
||||||
|
bool res = lhs_[_idx] == rhs_[_idx];
|
||||||
|
SPAN_ASSERT_TRUE(res, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(GPUSpan, WithTrust) {
|
||||||
|
// Not adviced to initialize span with host_vector, since h_vec.data() is
|
||||||
|
// a host function.
|
||||||
|
thrust::host_vector<float> h_vec (16);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
// Can't initialize span with device_vector, since d_vec.data() is not raw
|
||||||
|
// pointer
|
||||||
|
{
|
||||||
|
Span<float> s (d_vec.data().get(), d_vec.size());
|
||||||
|
|
||||||
|
ASSERT_EQ(d_vec.size(), s.size());
|
||||||
|
ASSERT_EQ(d_vec.data().get(), s.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TestStatus status;
|
||||||
|
thrust::device_vector<float> d_vec1 (d_vec.size());
|
||||||
|
thrust::copy(thrust::device, d_vec.begin(), d_vec.end(), d_vec1.begin());
|
||||||
|
Span<float> s (d_vec1.data().get(), d_vec.size());
|
||||||
|
|
||||||
|
dh::LaunchN(0, 16, TestEqual<float>{
|
||||||
|
thrust::raw_pointer_cast(d_vec1.data()),
|
||||||
|
s.data(), status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
|
||||||
|
// FIXME: memory error!
|
||||||
|
// bool res = thrust::equal(thrust::device,
|
||||||
|
// d_vec.begin(), d_vec.end(),
|
||||||
|
// s.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, BeginEnd) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestBeginEnd{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, RBeginREnd) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestRBeginREnd{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void test_modify_kernel(Span<float> span) {
|
||||||
|
size_t idx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||||
|
|
||||||
|
if (idx >= span.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
span[idx] = span.size() - idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, Modify) {
|
||||||
|
thrust::host_vector<float> h_vec (16);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
|
||||||
|
test_modify_kernel<<<1, 16>>>(span);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < d_vec.size(); ++i) {
|
||||||
|
ASSERT_EQ(d_vec[i], d_vec.size() - i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, Observers) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestObservers{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, Compare) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestIterCompare{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestElementAccess {
|
||||||
|
Span<float> span_;
|
||||||
|
|
||||||
|
XGBOOST_DEVICE TestElementAccess (Span<float> _span) : span_(_span) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE float operator()(size_t _idx) {
|
||||||
|
float tmp = span_[_idx];
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(GPUSpan, ElementAccess) {
|
||||||
|
EXPECT_DEATH({
|
||||||
|
thrust::host_vector<float> h_vec (16);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
dh::LaunchN(0, 17, TestElementAccess{span});}, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
__global__ void test_first_dynamic_kernel(Span<float> _span) {
|
||||||
|
_span.first<-1>();
|
||||||
|
}
|
||||||
|
__global__ void test_first_static_kernel(Span<float> _span) {
|
||||||
|
_span.first(-1);
|
||||||
|
}
|
||||||
|
__global__ void test_last_dynamic_kernel(Span<float> _span) {
|
||||||
|
_span.last<-1>();
|
||||||
|
}
|
||||||
|
__global__ void test_last_static_kernel(Span<float> _span) {
|
||||||
|
_span.last(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, FirstLast) {
|
||||||
|
// We construct vectors multiple times since thrust can not recover from
|
||||||
|
// death test.
|
||||||
|
auto lambda_first_dy = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_first_dynamic_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_first_dy(), "");
|
||||||
|
|
||||||
|
auto lambda_first_static = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_first_static_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_first_static(), "");
|
||||||
|
|
||||||
|
auto lambda_last_dy = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_last_dynamic_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_last_dy(), "");
|
||||||
|
|
||||||
|
auto lambda_last_static = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_last_static_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_last_static(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__global__ void test_subspan_dynamic_kernel(Span<float> _span) {
|
||||||
|
_span.subspan(16, 0);
|
||||||
|
}
|
||||||
|
__global__ void test_subspan_static_kernel(Span<float> _span) {
|
||||||
|
_span.subspan<16>();
|
||||||
|
}
|
||||||
|
TEST(GPUSpan, Subspan) {
|
||||||
|
auto lambda_subspan_dynamic = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_subspan_dynamic_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_subspan_dynamic(), "");
|
||||||
|
|
||||||
|
auto lambda_subspan_static = []() {
|
||||||
|
thrust::host_vector<float> h_vec (4);
|
||||||
|
InitializeRange(h_vec.begin(), h_vec.end());
|
||||||
|
|
||||||
|
thrust::device_vector<float> d_vec (h_vec.size());
|
||||||
|
thrust::copy(h_vec.begin(), h_vec.end(), d_vec.begin());
|
||||||
|
|
||||||
|
Span<float> span (d_vec.data().get(), d_vec.size());
|
||||||
|
test_subspan_static_kernel<<<1, 1>>>(span);
|
||||||
|
};
|
||||||
|
EXPECT_DEATH(lambda_subspan_static(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpanIter, Construct) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestIterConstruct{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpanIter, Ref) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestIterRef{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpanIter, Calculate) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestIterCalculate{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpanIter, Compare) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestIterCompare{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, AsBytes) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestAsBytes{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GPUSpan, AsWritableBytes) {
|
||||||
|
TestStatus status;
|
||||||
|
dh::LaunchN(0, 16, TestAsWritableBytes{status.data()});
|
||||||
|
ASSERT_EQ(status.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace xgboost
|
||||||
339
tests/cpp/common/test_span.h
Normal file
339
tests/cpp/common/test_span.h
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright 2018 XGBoost contributors
|
||||||
|
*/
|
||||||
|
#ifndef XGBOOST_TEST_SPAN_H_
|
||||||
|
#define XGBOOST_TEST_SPAN_H_
|
||||||
|
|
||||||
|
#include "../../include/xgboost/base.h"
|
||||||
|
#include "../../../src/common/span.h"
|
||||||
|
|
||||||
|
namespace xgboost {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
#define SPAN_ASSERT_TRUE(cond, status) \
|
||||||
|
if (!(cond)) { \
|
||||||
|
*(status) = -1; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SPAN_ASSERT_FALSE(cond, status) \
|
||||||
|
if ((cond)) { \
|
||||||
|
*(status) = -1; \
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
XGBOOST_DEVICE void InitializeRange(Iter _begin, Iter _end) {
|
||||||
|
float j = 0;
|
||||||
|
for (Iter i = _begin; i != _end; ++i, ++j) {
|
||||||
|
*i = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestTestStatus {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestTestStatus(int* _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
SPAN_ASSERT_TRUE(false, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestAssignment {
|
||||||
|
int* status_;
|
||||||
|
|
||||||
|
TestAssignment(int* _status) : status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
Span<float> s1;
|
||||||
|
|
||||||
|
float arr[] = {3, 4, 5};
|
||||||
|
|
||||||
|
Span<const float> s2 = arr;
|
||||||
|
SPAN_ASSERT_TRUE(s2.size() == 3, status_);
|
||||||
|
SPAN_ASSERT_TRUE(s2.data() == &arr[0], status_);
|
||||||
|
|
||||||
|
s2 = s1;
|
||||||
|
SPAN_ASSERT_TRUE(s2.empty(), status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestBeginEnd {
|
||||||
|
int* status_;
|
||||||
|
|
||||||
|
TestBeginEnd(int* _status) : status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float>::iterator beg { s.begin() };
|
||||||
|
Span<float>::iterator end { s.end() };
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(end == beg + 16, status_);
|
||||||
|
SPAN_ASSERT_TRUE(*beg == arr[0], status_);
|
||||||
|
SPAN_ASSERT_TRUE(*(end - 1) == arr[15], status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestRBeginREnd {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestRBeginREnd(int* _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float>::iterator rbeg { s.rbegin() };
|
||||||
|
Span<float>::iterator rend { s.rend() };
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(rbeg == rend + 16, status_);
|
||||||
|
SPAN_ASSERT_TRUE(*(rbeg - 1) == arr[15], status_);
|
||||||
|
SPAN_ASSERT_TRUE(*rend == arr[0], status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestObservers {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestObservers(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
// empty
|
||||||
|
{
|
||||||
|
float *arr = nullptr;
|
||||||
|
Span<float> s(arr, static_cast<Span<float>::index_type>(0));
|
||||||
|
SPAN_ASSERT_TRUE(s.empty(), status_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// size, size_types
|
||||||
|
{
|
||||||
|
float* arr = new float[16];
|
||||||
|
Span<float> s (arr, 16);
|
||||||
|
SPAN_ASSERT_TRUE(s.size() == 16, status_);
|
||||||
|
SPAN_ASSERT_TRUE(s.size_bytes() == 16 * sizeof(float), status_);
|
||||||
|
delete [] arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestCompare {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestCompare(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float lhs_arr[16], rhs_arr[16];
|
||||||
|
InitializeRange(lhs_arr, lhs_arr + 16);
|
||||||
|
InitializeRange(rhs_arr, rhs_arr + 16);
|
||||||
|
|
||||||
|
Span<float> lhs(lhs_arr);
|
||||||
|
Span<float> rhs(rhs_arr);
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(lhs == rhs, status_);
|
||||||
|
SPAN_ASSERT_FALSE(lhs != rhs, status_);
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(lhs <= rhs, status_);
|
||||||
|
SPAN_ASSERT_TRUE(lhs >= rhs, status_);
|
||||||
|
|
||||||
|
lhs[2] -= 1;
|
||||||
|
|
||||||
|
SPAN_ASSERT_FALSE(lhs == rhs, status_);
|
||||||
|
SPAN_ASSERT_TRUE(lhs < rhs, status_);
|
||||||
|
SPAN_ASSERT_FALSE(lhs > rhs, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestIterConstruct {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestIterConstruct(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
Span<float>::iterator it1;
|
||||||
|
Span<float>::iterator it2;
|
||||||
|
SPAN_ASSERT_TRUE(it1 == it2, status_);
|
||||||
|
|
||||||
|
Span<float>::const_iterator cit1;
|
||||||
|
Span<float>::const_iterator cit2;
|
||||||
|
SPAN_ASSERT_TRUE(cit1 == cit2, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestIterRef {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestIterRef(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
SPAN_ASSERT_TRUE(*(s.begin()) == s[0], status_);
|
||||||
|
SPAN_ASSERT_TRUE(*(s.end() - 1) == s[15], status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestIterCalculate {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestIterCalculate(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float>::iterator beg { s.begin() };
|
||||||
|
|
||||||
|
beg += 4;
|
||||||
|
SPAN_ASSERT_TRUE(*beg == 4, status_);
|
||||||
|
|
||||||
|
beg -= 2;
|
||||||
|
SPAN_ASSERT_TRUE(*beg == 2, status_);
|
||||||
|
|
||||||
|
++beg;
|
||||||
|
SPAN_ASSERT_TRUE(*beg == 3, status_);
|
||||||
|
|
||||||
|
--beg;
|
||||||
|
SPAN_ASSERT_TRUE(*beg == 2, status_);
|
||||||
|
|
||||||
|
beg++;
|
||||||
|
beg--;
|
||||||
|
SPAN_ASSERT_TRUE(*beg == 2, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestIterCompare {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestIterCompare(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
Span<float> s (arr);
|
||||||
|
Span<float>::iterator left { s.begin() };
|
||||||
|
Span<float>::iterator right { s.end() };
|
||||||
|
|
||||||
|
left += 1;
|
||||||
|
right -= 15;
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(left == right, status_);
|
||||||
|
|
||||||
|
SPAN_ASSERT_TRUE(left >= right, status_);
|
||||||
|
SPAN_ASSERT_TRUE(left <= right, status_);
|
||||||
|
|
||||||
|
++right;
|
||||||
|
SPAN_ASSERT_TRUE(right > left, status_);
|
||||||
|
SPAN_ASSERT_TRUE(left < right, status_);
|
||||||
|
SPAN_ASSERT_TRUE(left <= right, status_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestAsBytes {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestAsBytes(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
{
|
||||||
|
const Span<const float> s {arr};
|
||||||
|
const Span<const byte> bs = as_bytes(s);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size() == s.size_bytes(), status_);
|
||||||
|
SPAN_ASSERT_TRUE(static_cast<const void*>(bs.data()) ==
|
||||||
|
static_cast<const void*>(s.data()),
|
||||||
|
status_);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Span<float> s;
|
||||||
|
const Span<const byte> bs = as_bytes(s);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size() == s.size(), status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size() == 0, status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size_bytes() == 0, status_);
|
||||||
|
SPAN_ASSERT_TRUE(static_cast<const void*>(bs.data()) ==
|
||||||
|
static_cast<const void*>(s.data()),
|
||||||
|
status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.data() == nullptr, status_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestAsWritableBytes {
|
||||||
|
int * status_;
|
||||||
|
|
||||||
|
TestAsWritableBytes(int * _status): status_(_status) {}
|
||||||
|
|
||||||
|
XGBOOST_DEVICE void operator()() {
|
||||||
|
this->operator()(0);
|
||||||
|
}
|
||||||
|
XGBOOST_DEVICE void operator()(int _idx) {
|
||||||
|
float arr[16];
|
||||||
|
InitializeRange(arr, arr + 16);
|
||||||
|
|
||||||
|
{
|
||||||
|
Span<float> s;
|
||||||
|
Span<byte> bs = as_writable_bytes(s);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size() == s.size(), status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size_bytes() == s.size_bytes(), status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size() == 0, status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.size_bytes() == 0, status_);
|
||||||
|
SPAN_ASSERT_TRUE(bs.data() == nullptr, status_);
|
||||||
|
SPAN_ASSERT_TRUE(static_cast<void*>(bs.data()) ==
|
||||||
|
static_cast<void*>(s.data()), status_);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Span<float> s { arr };
|
||||||
|
Span<byte> bs { as_writable_bytes(s) };
|
||||||
|
SPAN_ASSERT_TRUE(s.size_bytes() == bs.size_bytes(), status_);
|
||||||
|
SPAN_ASSERT_TRUE(static_cast<void*>(bs.data()) ==
|
||||||
|
static_cast<void*>(s.data()), status_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace xgboost
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -25,7 +25,7 @@ TEST(SimpleCSRSource, SaveLoadBinary) {
|
|||||||
row_iter_read->BeforeFirst(); row_iter_read->Next();
|
row_iter_read->BeforeFirst(); row_iter_read->Next();
|
||||||
auto first_row = row_iter->Value()[0];
|
auto first_row = row_iter->Value()[0];
|
||||||
auto first_row_read = row_iter_read->Value()[0];
|
auto first_row_read = row_iter_read->Value()[0];
|
||||||
EXPECT_EQ(first_row.length, first_row_read.length);
|
EXPECT_EQ(first_row.size(), first_row_read.size());
|
||||||
EXPECT_EQ(first_row[2].index, first_row_read[2].index);
|
EXPECT_EQ(first_row[2].index, first_row_read[2].index);
|
||||||
EXPECT_EQ(first_row[2].fvalue, first_row_read[2].fvalue);
|
EXPECT_EQ(first_row[2].fvalue, first_row_read[2].fvalue);
|
||||||
row_iter = nullptr; row_iter_read = nullptr;
|
row_iter = nullptr; row_iter_read = nullptr;
|
||||||
|
|||||||
@ -31,7 +31,7 @@ TEST(SimpleDMatrix, RowAccess) {
|
|||||||
row_iter->BeforeFirst();
|
row_iter->BeforeFirst();
|
||||||
row_iter->Next();
|
row_iter->Next();
|
||||||
auto first_row = row_iter->Value()[0];
|
auto first_row = row_iter->Value()[0];
|
||||||
ASSERT_EQ(first_row.length, 3);
|
ASSERT_EQ(first_row.size(), 3);
|
||||||
EXPECT_EQ(first_row[2].index, 2);
|
EXPECT_EQ(first_row[2].index, 2);
|
||||||
EXPECT_EQ(first_row[2].fvalue, 20);
|
EXPECT_EQ(first_row[2].fvalue, 20);
|
||||||
row_iter = nullptr;
|
row_iter = nullptr;
|
||||||
@ -70,7 +70,7 @@ TEST(SimpleDMatrix, ColAccessWithoutBatches) {
|
|||||||
EXPECT_EQ(col_iter->Value().Size(), dmat->Info().num_col_)
|
EXPECT_EQ(col_iter->Value().Size(), dmat->Info().num_col_)
|
||||||
<< "Expected batch size = number of cells as #batches is 1.";
|
<< "Expected batch size = number of cells as #batches is 1.";
|
||||||
for (int i = 0; i < static_cast<int>(col_iter->Value().Size()); ++i) {
|
for (int i = 0; i < static_cast<int>(col_iter->Value().Size()); ++i) {
|
||||||
EXPECT_EQ(col_iter->Value()[i].length, dmat->GetColSize(i))
|
EXPECT_EQ(col_iter->Value()[i].size(), dmat->GetColSize(i))
|
||||||
<< "Expected length of each colbatch = colsize as #batches is 1.";
|
<< "Expected length of each colbatch = colsize as #batches is 1.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ TEST(SparsePageDMatrix, RowAccess) {
|
|||||||
row_iter->BeforeFirst();
|
row_iter->BeforeFirst();
|
||||||
row_iter->Next();
|
row_iter->Next();
|
||||||
auto first_row = row_iter->Value()[0];
|
auto first_row = row_iter->Value()[0];
|
||||||
ASSERT_EQ(first_row.length, 3);
|
ASSERT_EQ(first_row.size(), 3);
|
||||||
EXPECT_EQ(first_row[2].index, 2);
|
EXPECT_EQ(first_row[2].index, 2);
|
||||||
EXPECT_EQ(first_row[2].fvalue, 20);
|
EXPECT_EQ(first_row[2].fvalue, 20);
|
||||||
row_iter = nullptr;
|
row_iter = nullptr;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user