temp merge, disable 1 line, SetValid
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
#include <dmlc/data.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstddef> // for size_t
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../c_api/c_api_error.h"
|
||||
#include "../common/error_msg.h" // for MaxFeatureSize
|
||||
#include "../common/math.h"
|
||||
#include "array_interface.h"
|
||||
#include "arrow-cdi.h"
|
||||
@@ -300,9 +301,9 @@ class ArrayAdapter : public detail::SingleBatchDataIter<ArrayAdapterBatch> {
|
||||
array_interface_ = ArrayInterface<2>(get<Object const>(j));
|
||||
batch_ = ArrayAdapterBatch{array_interface_};
|
||||
}
|
||||
ArrayAdapterBatch const& Value() const override { return batch_; }
|
||||
size_t NumRows() const { return array_interface_.Shape(0); }
|
||||
size_t NumColumns() const { return array_interface_.Shape(1); }
|
||||
[[nodiscard]] ArrayAdapterBatch const& Value() const override { return batch_; }
|
||||
[[nodiscard]] std::size_t NumRows() const { return array_interface_.Shape(0); }
|
||||
[[nodiscard]] std::size_t NumColumns() const { return array_interface_.Shape(1); }
|
||||
|
||||
private:
|
||||
ArrayAdapterBatch batch_;
|
||||
@@ -476,7 +477,6 @@ class CSCArrayAdapterBatch : public detail::NoMetaInfo {
|
||||
ArrayInterface<1> indptr_;
|
||||
ArrayInterface<1> indices_;
|
||||
ArrayInterface<1> values_;
|
||||
bst_row_t n_rows_;
|
||||
|
||||
class Line {
|
||||
std::size_t column_idx_;
|
||||
@@ -502,11 +502,8 @@ class CSCArrayAdapterBatch : public detail::NoMetaInfo {
|
||||
static constexpr bool kIsRowMajor = false;
|
||||
|
||||
CSCArrayAdapterBatch(ArrayInterface<1> indptr, ArrayInterface<1> indices,
|
||||
ArrayInterface<1> values, bst_row_t n_rows)
|
||||
: indptr_{std::move(indptr)},
|
||||
indices_{std::move(indices)},
|
||||
values_{std::move(values)},
|
||||
n_rows_{n_rows} {}
|
||||
ArrayInterface<1> values)
|
||||
: indptr_{std::move(indptr)}, indices_{std::move(indices)}, values_{std::move(values)} {}
|
||||
|
||||
std::size_t Size() const { return indptr_.n - 1; }
|
||||
Line GetLine(std::size_t idx) const {
|
||||
@@ -541,8 +538,7 @@ class CSCArrayAdapter : public detail::SingleBatchDataIter<CSCArrayAdapterBatch>
|
||||
indices_{indices},
|
||||
values_{values},
|
||||
num_rows_{num_rows},
|
||||
batch_{
|
||||
CSCArrayAdapterBatch{indptr_, indices_, values_, static_cast<bst_row_t>(num_rows_)}} {}
|
||||
batch_{CSCArrayAdapterBatch{indptr_, indices_, values_}} {}
|
||||
|
||||
// JVM package sends 0 as unknown
|
||||
size_t NumRows() const { return num_rows_ == 0 ? kAdapterUnknownSize : num_rows_; }
|
||||
|
||||
@@ -386,7 +386,7 @@ inline bool ArrayInterfaceHandler::IsCudaPtr(void const *) { return false; }
|
||||
* numpy has the proper support even though it's in the __cuda_array_interface__
|
||||
* protocol defined by numba.
|
||||
*/
|
||||
template <int32_t D, bool allow_mask = (D == 1)>
|
||||
template <std::int32_t D, bool allow_mask = (D == 1)>
|
||||
class ArrayInterface {
|
||||
static_assert(D > 0, "Invalid dimension for array interface.");
|
||||
|
||||
@@ -457,7 +457,7 @@ class ArrayInterface {
|
||||
|
||||
explicit ArrayInterface(std::string const &str) : ArrayInterface{StringView{str}} {}
|
||||
|
||||
explicit ArrayInterface(StringView str) : ArrayInterface<D>{Json::Load(str)} {}
|
||||
explicit ArrayInterface(StringView str) : ArrayInterface{Json::Load(str)} {}
|
||||
|
||||
void AssignType(StringView typestr) {
|
||||
using T = ArrayInterfaceHandler::Type;
|
||||
@@ -590,9 +590,9 @@ class ArrayInterface {
|
||||
};
|
||||
|
||||
template <std::int32_t D, typename Fn>
|
||||
void DispatchDType(ArrayInterface<D> const array, std::int32_t device, Fn fn) {
|
||||
void DispatchDType(ArrayInterface<D> const array, DeviceOrd device, Fn fn) {
|
||||
// Only used for cuDF at the moment.
|
||||
CHECK_EQ(array.valid.Size(), 0);
|
||||
CHECK_EQ(array.valid.Capacity(), 0);
|
||||
auto dispatch = [&](auto t) {
|
||||
using T = std::remove_const_t<decltype(t)> const;
|
||||
// Set the data size to max as we don't know the original size of a sliced array:
|
||||
|
||||
144
src/data/data.cc
144
src/data/data.cc
@@ -4,42 +4,57 @@
|
||||
*/
|
||||
#include "xgboost/data.h"
|
||||
|
||||
#include <dmlc/registry.h>
|
||||
#include <dmlc/registry.h> // for DMLC_REGISTRY_ENABLE, DMLC_REGISTRY_LINK_TAG
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <algorithm> // for copy, max, none_of, min
|
||||
#include <atomic> // for atomic
|
||||
#include <cmath> // for abs
|
||||
#include <cstdint> // for uint64_t, int32_t, uint8_t, uint32_t
|
||||
#include <cstring> // for size_t, strcmp, memcpy
|
||||
#include <exception> // for exception
|
||||
#include <iostream> // for operator<<, basic_ostream, basic_ostream::op...
|
||||
#include <map> // for map, operator!=
|
||||
#include <numeric> // for accumulate, partial_sum
|
||||
#include <tuple> // for get, apply
|
||||
#include <type_traits> // for remove_pointer_t, remove_reference
|
||||
|
||||
#include "../collective/communicator-inl.h"
|
||||
#include "../collective/communicator.h"
|
||||
#include "../common/algorithm.h" // for StableSort
|
||||
#include "../common/api_entry.h" // for XGBAPIThreadLocalEntry
|
||||
#include "../common/common.h"
|
||||
#include "../common/error_msg.h" // for InfInData, GroupWeight, GroupSize
|
||||
#include "../common/group_data.h"
|
||||
#include "../common/io.h"
|
||||
#include "../common/linalg_op.h"
|
||||
#include "../common/math.h"
|
||||
#include "../common/numeric.h" // for Iota
|
||||
#include "../common/threading_utils.h"
|
||||
#include "../common/version.h"
|
||||
#include "../data/adapter.h"
|
||||
#include "../data/iterative_dmatrix.h"
|
||||
#include "./sparse_page_dmatrix.h"
|
||||
#include "./sparse_page_source.h"
|
||||
#include "dmlc/io.h"
|
||||
#include "file_iterator.h"
|
||||
#include "simple_dmatrix.h"
|
||||
#include "sparse_page_writer.h"
|
||||
#include "validation.h"
|
||||
#include "xgboost/c_api.h"
|
||||
#include "xgboost/context.h"
|
||||
#include "xgboost/host_device_vector.h"
|
||||
#include "xgboost/learner.h"
|
||||
#include "xgboost/linalg.h" // Vector
|
||||
#include "xgboost/logging.h"
|
||||
#include "xgboost/string_view.h"
|
||||
#include "xgboost/version_config.h"
|
||||
#include "../collective/communicator-inl.h" // for GetRank, GetWorldSize, Allreduce, IsFederated
|
||||
#include "../collective/communicator.h" // for Operation
|
||||
#include "../common/algorithm.h" // for StableSort
|
||||
#include "../common/api_entry.h" // for XGBAPIThreadLocalEntry
|
||||
#include "../common/common.h" // for Split
|
||||
#include "../common/error_msg.h" // for GroupSize, GroupWeight, InfInData
|
||||
#include "../common/group_data.h" // for ParallelGroupBuilder
|
||||
#include "../common/io.h" // for PeekableInStream
|
||||
#include "../common/linalg_op.h" // for ElementWiseTransformHost
|
||||
#include "../common/math.h" // for CheckNAN
|
||||
#include "../common/numeric.h" // for Iota, RunLengthEncode
|
||||
#include "../common/threading_utils.h" // for ParallelFor
|
||||
#include "../common/version.h" // for Version
|
||||
#include "../data/adapter.h" // for COOTuple, FileAdapter, IsValidFunctor
|
||||
#include "../data/iterative_dmatrix.h" // for IterativeDMatrix
|
||||
#include "./sparse_page_dmatrix.h" // for SparsePageDMatrix
|
||||
#include "array_interface.h" // for ArrayInterfaceHandler, ArrayInterface, Dispa...
|
||||
#include "dmlc/base.h" // for BeginPtr
|
||||
#include "dmlc/common.h" // for OMPException
|
||||
#include "dmlc/data.h" // for Parser
|
||||
#include "dmlc/endian.h" // for ByteSwap, DMLC_IO_NO_ENDIAN_SWAP
|
||||
#include "dmlc/io.h" // for Stream
|
||||
#include "dmlc/thread_local.h" // for ThreadLocalStore
|
||||
#include "ellpack_page.h" // for EllpackPage
|
||||
#include "file_iterator.h" // for ValidateFileFormat, FileIterator, Next, Reset
|
||||
#include "gradient_index.h" // for GHistIndexMatrix
|
||||
#include "simple_dmatrix.h" // for SimpleDMatrix
|
||||
#include "sparse_page_writer.h" // for SparsePageFormatReg
|
||||
#include "validation.h" // for LabelsCheck, WeightsCheck, ValidateQueryGroup
|
||||
#include "xgboost/base.h" // for bst_group_t, bst_row_t, bst_float, bst_ulong
|
||||
#include "xgboost/context.h" // for Context
|
||||
#include "xgboost/host_device_vector.h" // for HostDeviceVector
|
||||
#include "xgboost/learner.h" // for HostDeviceVector
|
||||
#include "xgboost/linalg.h" // for Tensor, Stack, TensorView, Vector, ArrayInte...
|
||||
#include "xgboost/logging.h" // for Error, LogCheck_EQ, CHECK, CHECK_EQ, LOG
|
||||
#include "xgboost/span.h" // for Span, operator!=, SpanIterator
|
||||
#include "xgboost/string_view.h" // for operator==, operator<<, StringView
|
||||
|
||||
namespace dmlc {
|
||||
DMLC_REGISTRY_ENABLE(::xgboost::data::SparsePageFormatReg<::xgboost::SparsePage>);
|
||||
@@ -351,7 +366,7 @@ MetaInfo MetaInfo::Slice(common::Span<int32_t const> ridxs) const {
|
||||
// Groups is maintained by a higher level Python function. We should aim at deprecating
|
||||
// the slice function.
|
||||
if (this->labels.Size() != this->num_row_) {
|
||||
auto t_labels = this->labels.View(this->labels.Data()->DeviceIdx());
|
||||
auto t_labels = this->labels.View(this->labels.Data()->Device());
|
||||
out.labels.Reshape(ridxs.size(), labels.Shape(1));
|
||||
out.labels.Data()->HostVector() =
|
||||
Gather(this->labels.Data()->HostVector(), ridxs, t_labels.Stride(0));
|
||||
@@ -379,7 +394,7 @@ MetaInfo MetaInfo::Slice(common::Span<int32_t const> ridxs) const {
|
||||
if (this->base_margin_.Size() != this->num_row_) {
|
||||
CHECK_EQ(this->base_margin_.Size() % this->num_row_, 0)
|
||||
<< "Incorrect size of base margin vector.";
|
||||
auto t_margin = this->base_margin_.View(this->base_margin_.Data()->DeviceIdx());
|
||||
auto t_margin = this->base_margin_.View(this->base_margin_.Data()->Device());
|
||||
out.base_margin_.Reshape(ridxs.size(), t_margin.Shape(1));
|
||||
out.base_margin_.Data()->HostVector() =
|
||||
Gather(this->base_margin_.Data()->HostVector(), ridxs, t_margin.Stride(0));
|
||||
@@ -416,7 +431,8 @@ void CopyTensorInfoImpl(Context const& ctx, Json arr_interface, linalg::Tensor<T
|
||||
p_out->Reshape(array.shape);
|
||||
return;
|
||||
}
|
||||
CHECK(array.valid.Size() == 0) << "Meta info like label or weight can not have missing value.";
|
||||
CHECK_EQ(array.valid.Capacity(), 0)
|
||||
<< "Meta info like label or weight can not have missing value.";
|
||||
if (array.is_contiguous && array.type == ToDType<T>::kType) {
|
||||
// Handle contigious
|
||||
p_out->ModifyInplace([&](HostDeviceVector<T>* data, common::Span<size_t, D> shape) {
|
||||
@@ -429,10 +445,10 @@ void CopyTensorInfoImpl(Context const& ctx, Json arr_interface, linalg::Tensor<T
|
||||
return;
|
||||
}
|
||||
p_out->Reshape(array.shape);
|
||||
auto t_out = p_out->View(Context::kCpuId);
|
||||
auto t_out = p_out->View(DeviceOrd::CPU());
|
||||
CHECK(t_out.CContiguous());
|
||||
auto const shape = t_out.Shape();
|
||||
DispatchDType(array, Context::kCpuId, [&](auto&& in) {
|
||||
DispatchDType(array, DeviceOrd::CPU(), [&](auto&& in) {
|
||||
linalg::ElementWiseTransformHost(t_out, ctx.Threads(), [&](auto i, auto) {
|
||||
return std::apply(in, linalg::UnravelIndex<D>(i, shape));
|
||||
});
|
||||
@@ -548,7 +564,7 @@ void MetaInfo::SetInfo(Context const& ctx, const char* key, const void* dptr, Da
|
||||
CHECK(key);
|
||||
auto proc = [&](auto cast_d_ptr) {
|
||||
using T = std::remove_pointer_t<decltype(cast_d_ptr)>;
|
||||
auto t = linalg::TensorView<T, 1>(common::Span<T>{cast_d_ptr, num}, {num}, Context::kCpuId);
|
||||
auto t = linalg::TensorView<T, 1>(common::Span<T>{cast_d_ptr, num}, {num}, DeviceOrd::CPU());
|
||||
CHECK(t.CContiguous());
|
||||
Json interface {
|
||||
linalg::ArrayInterface(t)
|
||||
@@ -723,11 +739,14 @@ void MetaInfo::SynchronizeNumberOfColumns() {
|
||||
namespace {
|
||||
template <typename T>
|
||||
void CheckDevice(std::int32_t device, HostDeviceVector<T> const& v) {
|
||||
CHECK(v.DeviceIdx() == Context::kCpuId || device == Context::kCpuId || v.DeviceIdx() == device)
|
||||
<< "Data is resided on a different device than `gpu_id`. "
|
||||
<< "Device that data is on: " << v.DeviceIdx() << ", "
|
||||
<< "`gpu_id` for XGBoost: " << device;
|
||||
bool valid = v.Device().IsCPU() || device == Context::kCpuId || v.DeviceIdx() == device;
|
||||
if (!valid) {
|
||||
LOG(FATAL) << "Invalid device ordinal. Data is associated with a different device ordinal than "
|
||||
"the booster. The device ordinal of the data is: "
|
||||
<< v.DeviceIdx() << "; the device ordinal of the Booster is: " << device;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::int32_t D>
|
||||
void CheckDevice(std::int32_t device, linalg::Tensor<T, D> const& v) {
|
||||
CheckDevice(device, *v.Data());
|
||||
@@ -806,10 +825,10 @@ DMatrix::~DMatrix() {
|
||||
}
|
||||
}
|
||||
|
||||
DMatrix *TryLoadBinary(std::string fname, bool silent) {
|
||||
int magic;
|
||||
std::unique_ptr<dmlc::Stream> fi(
|
||||
dmlc::Stream::Create(fname.c_str(), "r", true));
|
||||
namespace {
|
||||
DMatrix* TryLoadBinary(std::string fname, bool silent) {
|
||||
std::int32_t magic;
|
||||
std::unique_ptr<dmlc::Stream> fi(dmlc::Stream::Create(fname.c_str(), "r", true));
|
||||
if (fi != nullptr) {
|
||||
common::PeekableInStream is(fi.get());
|
||||
if (is.PeekRead(&magic, sizeof(magic)) == sizeof(magic)) {
|
||||
@@ -817,11 +836,10 @@ DMatrix *TryLoadBinary(std::string fname, bool silent) {
|
||||
dmlc::ByteSwap(&magic, sizeof(magic), 1);
|
||||
}
|
||||
if (magic == data::SimpleDMatrix::kMagic) {
|
||||
DMatrix *dmat = new data::SimpleDMatrix(&is);
|
||||
DMatrix* dmat = new data::SimpleDMatrix(&is);
|
||||
if (!silent) {
|
||||
LOG(CONSOLE) << dmat->Info().num_row_ << 'x' << dmat->Info().num_col_
|
||||
<< " matrix with " << dmat->Info().num_nonzero_
|
||||
<< " entries loaded from " << fname;
|
||||
LOG(CONSOLE) << dmat->Info().num_row_ << 'x' << dmat->Info().num_col_ << " matrix with "
|
||||
<< dmat->Info().num_nonzero_ << " entries loaded from " << fname;
|
||||
}
|
||||
return dmat;
|
||||
}
|
||||
@@ -829,6 +847,7 @@ DMatrix *TryLoadBinary(std::string fname, bool silent) {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DMatrix* DMatrix::Load(const std::string& uri, bool silent, DataSplitMode data_split_mode) {
|
||||
auto need_split = false;
|
||||
@@ -840,7 +859,7 @@ DMatrix* DMatrix::Load(const std::string& uri, bool silent, DataSplitMode data_s
|
||||
}
|
||||
|
||||
std::string fname, cache_file;
|
||||
size_t dlm_pos = uri.find('#');
|
||||
auto dlm_pos = uri.find('#');
|
||||
if (dlm_pos != std::string::npos) {
|
||||
cache_file = uri.substr(dlm_pos + 1, uri.length());
|
||||
fname = uri.substr(0, dlm_pos);
|
||||
@@ -852,14 +871,11 @@ DMatrix* DMatrix::Load(const std::string& uri, bool silent, DataSplitMode data_s
|
||||
for (size_t i = 0; i < cache_shards.size(); ++i) {
|
||||
size_t pos = cache_shards[i].rfind('.');
|
||||
if (pos == std::string::npos) {
|
||||
os << cache_shards[i]
|
||||
<< ".r" << collective::GetRank()
|
||||
<< "-" << collective::GetWorldSize();
|
||||
os << cache_shards[i] << ".r" << collective::GetRank() << "-"
|
||||
<< collective::GetWorldSize();
|
||||
} else {
|
||||
os << cache_shards[i].substr(0, pos)
|
||||
<< ".r" << collective::GetRank()
|
||||
<< "-" << collective::GetWorldSize()
|
||||
<< cache_shards[i].substr(pos, cache_shards[i].length());
|
||||
os << cache_shards[i].substr(0, pos) << ".r" << collective::GetRank() << "-"
|
||||
<< collective::GetWorldSize() << cache_shards[i].substr(pos, cache_shards[i].length());
|
||||
}
|
||||
if (i + 1 != cache_shards.size()) {
|
||||
os << ':';
|
||||
@@ -890,12 +906,12 @@ DMatrix* DMatrix::Load(const std::string& uri, bool silent, DataSplitMode data_s
|
||||
LOG(CONSOLE) << "Load part of data " << partid << " of " << npart << " parts";
|
||||
}
|
||||
|
||||
data::ValidateFileFormat(fname);
|
||||
DMatrix* dmat {nullptr};
|
||||
DMatrix* dmat{nullptr};
|
||||
|
||||
if (cache_file.empty()) {
|
||||
std::unique_ptr<dmlc::Parser<uint32_t>> parser(
|
||||
dmlc::Parser<uint32_t>::Create(fname.c_str(), partid, npart, "auto"));
|
||||
fname = data::ValidateFileFormat(fname);
|
||||
std::unique_ptr<dmlc::Parser<std::uint32_t>> parser(
|
||||
dmlc::Parser<std::uint32_t>::Create(fname.c_str(), partid, npart, "auto"));
|
||||
data::FileAdapter adapter(parser.get());
|
||||
dmat = DMatrix::Create(&adapter, std::numeric_limits<float>::quiet_NaN(), Context{}.Threads(),
|
||||
cache_file, data_split_mode);
|
||||
|
||||
@@ -45,7 +45,8 @@ void CopyTensorInfoImpl(CUDAContext const* ctx, Json arr_interface, linalg::Tens
|
||||
p_out->Reshape(array.shape);
|
||||
return;
|
||||
}
|
||||
CHECK(array.valid.Size() == 0) << "Meta info like label or weight can not have missing value.";
|
||||
CHECK_EQ(array.valid.Capacity(), 0)
|
||||
<< "Meta info like label or weight can not have missing value.";
|
||||
auto ptr_device = SetDeviceToPtr(array.data);
|
||||
p_out->SetDevice(ptr_device);
|
||||
|
||||
@@ -67,7 +68,7 @@ void CopyTensorInfoImpl(CUDAContext const* ctx, Json arr_interface, linalg::Tens
|
||||
return;
|
||||
}
|
||||
p_out->Reshape(array.shape);
|
||||
auto t = p_out->View(ptr_device);
|
||||
auto t = p_out->View(DeviceOrd::CUDA(ptr_device));
|
||||
linalg::ElementWiseTransformDevice(
|
||||
t,
|
||||
[=] __device__(size_t i, T) {
|
||||
|
||||
@@ -3,12 +3,20 @@
|
||||
*/
|
||||
#if !defined(XGBOOST_USE_CUDA) && !defined(XGBOOST_USE_HIP)
|
||||
|
||||
#include "ellpack_page.h"
|
||||
|
||||
#include <xgboost/data.h>
|
||||
|
||||
// dummy implementation of EllpackPage in case CUDA is not used
|
||||
namespace xgboost {
|
||||
|
||||
class EllpackPageImpl {};
|
||||
class EllpackPageImpl {
|
||||
common::HistogramCuts cuts_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] common::HistogramCuts& Cuts() { return cuts_; }
|
||||
[[nodiscard]] common::HistogramCuts const& Cuts() const { return cuts_; }
|
||||
};
|
||||
|
||||
EllpackPage::EllpackPage() = default;
|
||||
|
||||
@@ -32,5 +40,16 @@ size_t EllpackPage::Size() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] common::HistogramCuts& EllpackPage::Cuts() {
|
||||
LOG(FATAL) << "Internal Error: XGBoost is not compiled with CUDA but "
|
||||
"EllpackPage is required";
|
||||
return impl_->Cuts();
|
||||
}
|
||||
|
||||
[[nodiscard]] common::HistogramCuts const& EllpackPage::Cuts() const {
|
||||
LOG(FATAL) << "Internal Error: XGBoost is not compiled with CUDA but "
|
||||
"EllpackPage is required";
|
||||
return impl_->Cuts();
|
||||
}
|
||||
} // namespace xgboost
|
||||
#endif // XGBOOST_USE_CUDA || XGBOOST_USE_HIP
|
||||
|
||||
@@ -4,12 +4,17 @@
|
||||
#include <thrust/iterator/discard_iterator.h>
|
||||
#include <thrust/iterator/transform_output_iterator.h>
|
||||
|
||||
#include <algorithm> // for copy
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "../common/categorical.h"
|
||||
#include "../common/cuda_context.cuh"
|
||||
#include "../common/hist_util.cuh"
|
||||
#include "../common/random.h"
|
||||
#include "../common/transform_iterator.h" // MakeIndexTransformIter
|
||||
#include "./ellpack_page.cuh"
|
||||
#include "device_adapter.cuh" // for HasInfInData
|
||||
#include "ellpack_page.h"
|
||||
#include "gradient_index.h"
|
||||
#include "xgboost/data.h"
|
||||
|
||||
@@ -32,6 +37,16 @@ size_t EllpackPage::Size() const { return impl_->Size(); }
|
||||
|
||||
void EllpackPage::SetBaseRowId(std::size_t row_id) { impl_->SetBaseRowId(row_id); }
|
||||
|
||||
[[nodiscard]] common::HistogramCuts& EllpackPage::Cuts() {
|
||||
CHECK(impl_);
|
||||
return impl_->Cuts();
|
||||
}
|
||||
|
||||
[[nodiscard]] common::HistogramCuts const& EllpackPage::Cuts() const {
|
||||
CHECK(impl_);
|
||||
return impl_->Cuts();
|
||||
}
|
||||
|
||||
// Bin each input data entry, store the bin indices in compressed form.
|
||||
__global__ void CompressBinEllpackKernel(
|
||||
common::CompressedBufferWriter wr,
|
||||
@@ -128,7 +143,11 @@ EllpackPageImpl::EllpackPageImpl(Context const* ctx, DMatrix* dmat, const BatchP
|
||||
monitor_.Start("Quantiles");
|
||||
// Create the quantile sketches for the dmatrix and initialize HistogramCuts.
|
||||
row_stride = GetRowStride(dmat);
|
||||
cuts_ = common::DeviceSketch(ctx->gpu_id, dmat, param.max_bin);
|
||||
if (!param.hess.empty()) {
|
||||
cuts_ = common::DeviceSketchWithHessian(ctx, dmat, param.max_bin, param.hess);
|
||||
} else {
|
||||
cuts_ = common::DeviceSketch(ctx, dmat, param.max_bin);
|
||||
}
|
||||
monitor_.Stop("Quantiles");
|
||||
|
||||
monitor_.Start("InitCompressedData");
|
||||
@@ -343,7 +362,8 @@ void CopyGHistToEllpack(GHistIndexMatrix const& page, common::Span<size_t const>
|
||||
auto d_csc_indptr = dh::ToSpan(csc_indptr);
|
||||
|
||||
auto bin_type = page.index.GetBinTypeSize();
|
||||
common::CompressedBufferWriter writer{page.cut.TotalBins() + 1}; // +1 for null value
|
||||
common::CompressedBufferWriter writer{page.cut.TotalBins() +
|
||||
static_cast<std::size_t>(1)}; // +1 for null value
|
||||
|
||||
dh::LaunchN(row_stride * page.Size(), [=] __device__(size_t idx) mutable {
|
||||
auto ridx = idx / row_stride;
|
||||
@@ -387,8 +407,15 @@ EllpackPageImpl::EllpackPageImpl(Context const* ctx, GHistIndexMatrix const& pag
|
||||
|
||||
// copy gidx
|
||||
common::CompressedByteT* d_compressed_buffer = gidx_buffer.DevicePointer();
|
||||
dh::device_vector<size_t> row_ptr(page.row_ptr);
|
||||
dh::device_vector<size_t> row_ptr(page.row_ptr.size());
|
||||
auto d_row_ptr = dh::ToSpan(row_ptr);
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
dh::safe_cuda(cudaMemcpyAsync(d_row_ptr.data(), page.row_ptr.data(), d_row_ptr.size_bytes(),
|
||||
cudaMemcpyHostToDevice, ctx->CUDACtx()->Stream()));
|
||||
#elif defined(XGBOOST_USE_HIP)
|
||||
dh::safe_cuda(hipMemcpyAsync(d_row_ptr.data(), page.row_ptr.data(), d_row_ptr.size_bytes(),
|
||||
hipMemcpyHostToDevice, ctx->CUDACtx()->Stream()));
|
||||
#endif
|
||||
|
||||
auto accessor = this->GetDeviceAccessor(ctx->gpu_id, ft);
|
||||
auto null = accessor.NullValue();
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/*!
|
||||
* Copyright 2019 by XGBoost Contributors
|
||||
/**
|
||||
* Copyright 2019-2023, XGBoost Contributors
|
||||
*/
|
||||
|
||||
#ifndef XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
#define XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
#ifndef XGBOOST_DATA_ELLPACK_PAGE_CUH_
|
||||
#define XGBOOST_DATA_ELLPACK_PAGE_CUH_
|
||||
|
||||
#include <thrust/binary_search.h>
|
||||
#include <xgboost/data.h>
|
||||
|
||||
#include "../common/categorical.h"
|
||||
#include "../common/compressed_iterator.h"
|
||||
#include "../common/device_helpers.cuh"
|
||||
#include "../common/hist_util.h"
|
||||
#include "../common/categorical.h"
|
||||
#include <thrust/binary_search.h>
|
||||
#include "ellpack_page.h"
|
||||
|
||||
namespace xgboost {
|
||||
/** \brief Struct for accessing and manipulating an ELLPACK matrix on the
|
||||
@@ -194,8 +195,8 @@ class EllpackPageImpl {
|
||||
base_rowid = row_id;
|
||||
}
|
||||
|
||||
common::HistogramCuts& Cuts() { return cuts_; }
|
||||
common::HistogramCuts const& Cuts() const { return cuts_; }
|
||||
[[nodiscard]] common::HistogramCuts& Cuts() { return cuts_; }
|
||||
[[nodiscard]] common::HistogramCuts const& Cuts() const { return cuts_; }
|
||||
|
||||
/*! \return Estimation of memory cost of this page. */
|
||||
static size_t MemCostBytes(size_t num_rows, size_t row_stride, const common::HistogramCuts&cuts) ;
|
||||
@@ -256,4 +257,4 @@ inline size_t GetRowStride(DMatrix* dmat) {
|
||||
}
|
||||
} // namespace xgboost
|
||||
|
||||
#endif // XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
#endif // XGBOOST_DATA_ELLPACK_PAGE_CUH_
|
||||
|
||||
59
src/data/ellpack_page.h
Normal file
59
src/data/ellpack_page.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2017-2023 by XGBoost Contributors
|
||||
*/
|
||||
#ifndef XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
#define XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "../common/hist_util.h" // for HistogramCuts
|
||||
#include "xgboost/context.h" // for Context
|
||||
#include "xgboost/data.h" // for DMatrix, BatchParam
|
||||
|
||||
namespace xgboost {
|
||||
class EllpackPageImpl;
|
||||
/**
|
||||
* @brief A page stored in ELLPACK format.
|
||||
*
|
||||
* This class uses the PImpl idiom (https://en.cppreference.com/w/cpp/language/pimpl) to avoid
|
||||
* including CUDA-specific implementation details in the header.
|
||||
*/
|
||||
class EllpackPage {
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*
|
||||
* This is used in the external memory case. An empty ELLPACK page is constructed with its content
|
||||
* set later by the reader.
|
||||
*/
|
||||
EllpackPage();
|
||||
/**
|
||||
* @brief Constructor from an existing DMatrix.
|
||||
*
|
||||
* This is used in the in-memory case. The ELLPACK page is constructed from an existing DMatrix
|
||||
* in CSR format.
|
||||
*/
|
||||
explicit EllpackPage(Context const* ctx, DMatrix* dmat, const BatchParam& param);
|
||||
|
||||
/*! \brief Destructor. */
|
||||
~EllpackPage();
|
||||
|
||||
EllpackPage(EllpackPage&& that);
|
||||
|
||||
/*! \return Number of instances in the page. */
|
||||
[[nodiscard]] size_t Size() const;
|
||||
|
||||
/*! \brief Set the base row id for this page. */
|
||||
void SetBaseRowId(std::size_t row_id);
|
||||
|
||||
[[nodiscard]] const EllpackPageImpl* Impl() const { return impl_.get(); }
|
||||
EllpackPageImpl* Impl() { return impl_.get(); }
|
||||
|
||||
[[nodiscard]] common::HistogramCuts& Cuts();
|
||||
[[nodiscard]] common::HistogramCuts const& Cuts() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<EllpackPageImpl> impl_;
|
||||
};
|
||||
} // namespace xgboost
|
||||
#endif // XGBOOST_DATA_ELLPACK_PAGE_H_
|
||||
@@ -1,60 +1,59 @@
|
||||
/*!
|
||||
* Copyright 2019-2021 XGBoost contributors
|
||||
/**
|
||||
* Copyright 2019-2023, XGBoost contributors
|
||||
*/
|
||||
#include <xgboost/data.h>
|
||||
#include <dmlc/registry.h>
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
|
||||
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
|
||||
#include "../common/ref_resource_view.h" // for ReadVec, WriteVec
|
||||
#include "ellpack_page.cuh"
|
||||
#include "sparse_page_writer.h"
|
||||
#include "histogram_cut_format.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
#include "histogram_cut_format.h" // for ReadHistogramCuts, WriteHistogramCuts
|
||||
#include "sparse_page_writer.h" // for SparsePageFormat
|
||||
|
||||
namespace xgboost::data {
|
||||
DMLC_REGISTRY_FILE_TAG(ellpack_page_raw_format);
|
||||
|
||||
|
||||
class EllpackPageRawFormat : public SparsePageFormat<EllpackPage> {
|
||||
public:
|
||||
bool Read(EllpackPage* page, dmlc::SeekStream* fi) override {
|
||||
bool Read(EllpackPage* page, common::AlignedResourceReadStream* fi) override {
|
||||
auto* impl = page->Impl();
|
||||
if (!ReadHistogramCuts(&impl->Cuts(), fi)) {
|
||||
return false;
|
||||
}
|
||||
fi->Read(&impl->n_rows);
|
||||
fi->Read(&impl->is_dense);
|
||||
fi->Read(&impl->row_stride);
|
||||
fi->Read(&impl->gidx_buffer.HostVector());
|
||||
if (!fi->Read(&impl->n_rows)) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&impl->is_dense)) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&impl->row_stride)) {
|
||||
return false;
|
||||
}
|
||||
if (!common::ReadVec(fi, &impl->gidx_buffer.HostVector())) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&impl->base_rowid)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Write(const EllpackPage& page, dmlc::Stream* fo) override {
|
||||
size_t bytes = 0;
|
||||
size_t Write(const EllpackPage& page, common::AlignedFileWriteStream* fo) override {
|
||||
std::size_t bytes{0};
|
||||
auto* impl = page.Impl();
|
||||
bytes += WriteHistogramCuts(impl->Cuts(), fo);
|
||||
fo->Write(impl->n_rows);
|
||||
bytes += sizeof(impl->n_rows);
|
||||
fo->Write(impl->is_dense);
|
||||
bytes += sizeof(impl->is_dense);
|
||||
fo->Write(impl->row_stride);
|
||||
bytes += sizeof(impl->row_stride);
|
||||
bytes += fo->Write(impl->n_rows);
|
||||
bytes += fo->Write(impl->is_dense);
|
||||
bytes += fo->Write(impl->row_stride);
|
||||
CHECK(!impl->gidx_buffer.ConstHostVector().empty());
|
||||
fo->Write(impl->gidx_buffer.HostVector());
|
||||
bytes += impl->gidx_buffer.ConstHostSpan().size_bytes() + sizeof(uint64_t);
|
||||
fo->Write(impl->base_rowid);
|
||||
bytes += sizeof(impl->base_rowid);
|
||||
bytes += common::WriteVec(fo, impl->gidx_buffer.HostVector());
|
||||
bytes += fo->Write(impl->base_rowid);
|
||||
return bytes;
|
||||
}
|
||||
};
|
||||
|
||||
XGBOOST_REGISTER_ELLPACK_PAGE_FORMAT(raw)
|
||||
.describe("Raw ELLPACK binary data format.")
|
||||
.set_body([]() {
|
||||
return new EllpackPageRawFormat();
|
||||
});
|
||||
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
.set_body([]() { return new EllpackPageRawFormat(); });
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <utility>
|
||||
|
||||
#include "ellpack_page.cuh"
|
||||
#include "ellpack_page.h" // for EllpackPage
|
||||
#include "ellpack_page_source.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
namespace xgboost::data {
|
||||
void EllpackPageSource::Fetch() {
|
||||
#if defined(XGBOOST_USE_CUDA)
|
||||
dh::safe_cuda(cudaSetDevice(device_));
|
||||
@@ -31,5 +31,4 @@ void EllpackPageSource::Fetch() {
|
||||
this->WriteCache();
|
||||
}
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
#define XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_
|
||||
|
||||
#include <xgboost/data.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/hist_util.h"
|
||||
#include "ellpack_page.h" // for EllpackPage
|
||||
#include "sparse_page_source.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
|
||||
namespace xgboost::data {
|
||||
class EllpackPageSource : public PageSourceIncMixIn<EllpackPage> {
|
||||
bool is_dense_;
|
||||
size_t row_stride_;
|
||||
@@ -52,8 +52,7 @@ inline void EllpackPageSource::Fetch() {
|
||||
(void)(is_dense_);
|
||||
common::AssertGPUSupport();
|
||||
}
|
||||
#endif // !defined(XGBOOST_USE_CUDA) && !defined(XGBOOST_USE_HIP)
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
#endif // !defined(XGBOOST_USE_CUDA)
|
||||
} // namespace xgboost::data
|
||||
|
||||
#endif // XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_
|
||||
|
||||
51
src/data/file_iterator.cc
Normal file
51
src/data/file_iterator.cc
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost contributors
|
||||
*/
|
||||
#include "file_iterator.h"
|
||||
|
||||
#include <xgboost/logging.h> // for LogCheck_EQ, LogCheck_LE, CHECK_EQ, CHECK_LE, LOG, LOG_...
|
||||
|
||||
#include <filesystem> // for weakly_canonical, path, u8path
|
||||
#include <map> // for map, operator==
|
||||
#include <ostream> // for operator<<, basic_ostream, istringstream
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "../common/common.h" // for Split
|
||||
#include "xgboost/string_view.h" // for operator<<, StringView
|
||||
|
||||
namespace xgboost::data {
|
||||
std::string ValidateFileFormat(std::string const& uri) {
|
||||
std::vector<std::string> name_args_cache = common::Split(uri, '#');
|
||||
CHECK_LE(name_args_cache.size(), 2)
|
||||
<< "Only one `#` is allowed in file path for cachefile specification";
|
||||
|
||||
std::vector<std::string> name_args = common::Split(name_args_cache[0], '?');
|
||||
StringView msg{"URI parameter `format` is required for loading text data: filename?format=csv"};
|
||||
CHECK_EQ(name_args.size(), 2) << msg;
|
||||
|
||||
std::map<std::string, std::string> args;
|
||||
std::vector<std::string> arg_list = common::Split(name_args[1], '&');
|
||||
for (size_t i = 0; i < arg_list.size(); ++i) {
|
||||
std::istringstream is(arg_list[i]);
|
||||
std::pair<std::string, std::string> kv;
|
||||
CHECK(std::getline(is, kv.first, '=')) << "Invalid uri argument format"
|
||||
<< " for key in arg " << i + 1;
|
||||
CHECK(std::getline(is, kv.second)) << "Invalid uri argument format"
|
||||
<< " for value in arg " << i + 1;
|
||||
args.insert(kv);
|
||||
}
|
||||
if (args.find("format") == args.cend()) {
|
||||
LOG(FATAL) << msg;
|
||||
}
|
||||
|
||||
auto path = common::Split(uri, '?')[0];
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
name_args[0] = fs::weakly_canonical(fs::u8path(path)).string();
|
||||
if (name_args_cache.size() == 1) {
|
||||
return name_args[0] + "?" + name_args[1];
|
||||
} else {
|
||||
return name_args[0] + "?" + name_args[1] + '#' + name_args_cache[1];
|
||||
}
|
||||
}
|
||||
} // namespace xgboost::data
|
||||
@@ -4,46 +4,20 @@
|
||||
#ifndef XGBOOST_DATA_FILE_ITERATOR_H_
|
||||
#define XGBOOST_DATA_FILE_ITERATOR_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <algorithm> // for max_element
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint32_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string> // for string
|
||||
#include <utility> // for move
|
||||
|
||||
#include "array_interface.h"
|
||||
#include "dmlc/data.h"
|
||||
#include "xgboost/c_api.h"
|
||||
#include "xgboost/json.h"
|
||||
#include "xgboost/linalg.h"
|
||||
#include "dmlc/data.h" // for RowBlock, Parser
|
||||
#include "xgboost/c_api.h" // for XGDMatrixSetDenseInfo, XGDMatrixFree, XGProxyDMatrixCreate
|
||||
#include "xgboost/linalg.h" // for ArrayInterfaceStr, MakeVec
|
||||
#include "xgboost/logging.h" // for CHECK
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
inline void ValidateFileFormat(std::string const& uri) {
|
||||
std::vector<std::string> name_cache = common::Split(uri, '#');
|
||||
CHECK_LE(name_cache.size(), 2)
|
||||
<< "Only one `#` is allowed in file path for cachefile specification";
|
||||
|
||||
std::vector<std::string> name_args = common::Split(name_cache[0], '?');
|
||||
CHECK_LE(name_args.size(), 2) << "only one `?` is allowed in file path.";
|
||||
|
||||
StringView msg{"URI parameter `format` is required for loading text data: filename?format=csv"};
|
||||
CHECK_EQ(name_args.size(), 2) << msg;
|
||||
|
||||
std::map<std::string, std::string> args;
|
||||
std::vector<std::string> arg_list = common::Split(name_args[1], '&');
|
||||
for (size_t i = 0; i < arg_list.size(); ++i) {
|
||||
std::istringstream is(arg_list[i]);
|
||||
std::pair<std::string, std::string> kv;
|
||||
CHECK(std::getline(is, kv.first, '=')) << "Invalid uri argument format"
|
||||
<< " for key in arg " << i + 1;
|
||||
CHECK(std::getline(is, kv.second)) << "Invalid uri argument format"
|
||||
<< " for value in arg " << i + 1;
|
||||
args.insert(kv);
|
||||
}
|
||||
if (args.find("format") == args.cend()) {
|
||||
LOG(FATAL) << msg;
|
||||
}
|
||||
}
|
||||
namespace xgboost::data {
|
||||
[[nodiscard]] std::string ValidateFileFormat(std::string const& uri);
|
||||
|
||||
/**
|
||||
* An iterator for implementing external memory support with file inputs. Users of
|
||||
@@ -72,8 +46,7 @@ class FileIterator {
|
||||
|
||||
public:
|
||||
FileIterator(std::string uri, unsigned part_index, unsigned num_parts)
|
||||
: uri_{std::move(uri)}, part_idx_{part_index}, n_parts_{num_parts} {
|
||||
ValidateFileFormat(uri_);
|
||||
: uri_{ValidateFileFormat(std::move(uri))}, part_idx_{part_index}, n_parts_{num_parts} {
|
||||
XGProxyDMatrixCreate(&proxy_);
|
||||
}
|
||||
~FileIterator() {
|
||||
@@ -132,6 +105,5 @@ inline int Next(DataIterHandle self) {
|
||||
return static_cast<FileIterator*>(self)->Next();
|
||||
}
|
||||
} // namespace fileiter
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_FILE_ITERATOR_H_
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility> // std::forward
|
||||
#include <utility> // for forward
|
||||
|
||||
#include "../common/column_matrix.h"
|
||||
#include "../common/hist_util.h"
|
||||
#include "../common/numeric.h"
|
||||
#include "../common/threading_utils.h"
|
||||
#include "../common/transform_iterator.h" // MakeIndexTransformIter
|
||||
#include "../common/transform_iterator.h" // for MakeIndexTransformIter
|
||||
|
||||
namespace xgboost {
|
||||
|
||||
@@ -21,7 +20,7 @@ GHistIndexMatrix::GHistIndexMatrix() : columns_{std::make_unique<common::ColumnM
|
||||
|
||||
GHistIndexMatrix::GHistIndexMatrix(Context const *ctx, DMatrix *p_fmat, bst_bin_t max_bins_per_feat,
|
||||
double sparse_thresh, bool sorted_sketch,
|
||||
common::Span<float> hess)
|
||||
common::Span<float const> hess)
|
||||
: max_numeric_bins_per_feat{max_bins_per_feat} {
|
||||
CHECK(p_fmat->SingleColBlock());
|
||||
// We use sorted sketching for approx tree method since it's more efficient in
|
||||
@@ -29,7 +28,7 @@ GHistIndexMatrix::GHistIndexMatrix(Context const *ctx, DMatrix *p_fmat, bst_bin_
|
||||
cut = common::SketchOnDMatrix(ctx, p_fmat, max_bins_per_feat, sorted_sketch, hess);
|
||||
|
||||
const uint32_t nbins = cut.Ptrs().back();
|
||||
hit_count.resize(nbins, 0);
|
||||
hit_count = common::MakeFixedVecWithMalloc(nbins, std::size_t{0});
|
||||
hit_count_tloc_.resize(ctx->Threads() * nbins, 0);
|
||||
|
||||
size_t new_size = 1;
|
||||
@@ -37,8 +36,7 @@ GHistIndexMatrix::GHistIndexMatrix(Context const *ctx, DMatrix *p_fmat, bst_bin_
|
||||
new_size += batch.Size();
|
||||
}
|
||||
|
||||
row_ptr.resize(new_size);
|
||||
row_ptr[0] = 0;
|
||||
row_ptr = common::MakeFixedVecWithMalloc(new_size, std::size_t{0});
|
||||
|
||||
const bool isDense = p_fmat->IsDense();
|
||||
this->isDense_ = isDense;
|
||||
@@ -61,8 +59,8 @@ GHistIndexMatrix::GHistIndexMatrix(Context const *ctx, DMatrix *p_fmat, bst_bin_
|
||||
|
||||
GHistIndexMatrix::GHistIndexMatrix(MetaInfo const &info, common::HistogramCuts &&cuts,
|
||||
bst_bin_t max_bin_per_feat)
|
||||
: row_ptr(info.num_row_ + 1, 0),
|
||||
hit_count(cuts.TotalBins(), 0),
|
||||
: row_ptr{common::MakeFixedVecWithMalloc(info.num_row_ + 1, std::size_t{0})},
|
||||
hit_count{common::MakeFixedVecWithMalloc(cuts.TotalBins(), std::size_t{0})},
|
||||
cut{std::forward<common::HistogramCuts>(cuts)},
|
||||
max_numeric_bins_per_feat(max_bin_per_feat),
|
||||
isDense_{info.num_col_ * info.num_row_ == info.num_nonzero_} {}
|
||||
@@ -95,12 +93,10 @@ GHistIndexMatrix::GHistIndexMatrix(SparsePage const &batch, common::Span<Feature
|
||||
isDense_{isDense} {
|
||||
CHECK_GE(n_threads, 1);
|
||||
CHECK_EQ(row_ptr.size(), 0);
|
||||
// The number of threads is pegged to the batch size. If the OMP
|
||||
// block is parallelized on anything other than the batch/block size,
|
||||
// it should be reassigned
|
||||
row_ptr.resize(batch.Size() + 1, 0);
|
||||
row_ptr = common::MakeFixedVecWithMalloc(batch.Size() + 1, std::size_t{0});
|
||||
|
||||
const uint32_t nbins = cut.Ptrs().back();
|
||||
hit_count.resize(nbins, 0);
|
||||
hit_count = common::MakeFixedVecWithMalloc(nbins, std::size_t{0});
|
||||
hit_count_tloc_.resize(n_threads * nbins, 0);
|
||||
|
||||
this->PushBatch(batch, ft, n_threads);
|
||||
@@ -128,20 +124,45 @@ INSTANTIATION_PUSH(data::SparsePageAdapterBatch)
|
||||
#undef INSTANTIATION_PUSH
|
||||
|
||||
void GHistIndexMatrix::ResizeIndex(const size_t n_index, const bool isDense) {
|
||||
auto make_index = [this, n_index](auto t, common::BinTypeSize t_size) {
|
||||
// Must resize instead of allocating a new one. This function is called everytime a
|
||||
// new batch is pushed, and we grow the size accordingly without loosing the data the
|
||||
// previous batches.
|
||||
using T = decltype(t);
|
||||
std::size_t n_bytes = sizeof(T) * n_index;
|
||||
CHECK_GE(n_bytes, this->data.size());
|
||||
|
||||
auto resource = this->data.Resource();
|
||||
decltype(this->data) new_vec;
|
||||
if (!resource) {
|
||||
CHECK(this->data.empty());
|
||||
new_vec = common::MakeFixedVecWithMalloc(n_bytes, std::uint8_t{0});
|
||||
} else {
|
||||
CHECK(resource->Type() == common::ResourceHandler::kMalloc);
|
||||
auto malloc_resource = std::dynamic_pointer_cast<common::MallocResource>(resource);
|
||||
CHECK(malloc_resource);
|
||||
malloc_resource->Resize(n_bytes);
|
||||
|
||||
// gcc-11.3 doesn't work if DataAs is used.
|
||||
std::uint8_t *new_ptr = reinterpret_cast<std::uint8_t *>(malloc_resource->Data());
|
||||
new_vec = {new_ptr, n_bytes / sizeof(std::uint8_t), malloc_resource};
|
||||
}
|
||||
this->data = std::move(new_vec);
|
||||
this->index = common::Index{common::Span{data.data(), data.size()}, t_size};
|
||||
};
|
||||
|
||||
if ((MaxNumBinPerFeat() - 1 <= static_cast<int>(std::numeric_limits<uint8_t>::max())) &&
|
||||
isDense) {
|
||||
// compress dense index to uint8
|
||||
index.SetBinTypeSize(common::kUint8BinsTypeSize);
|
||||
index.Resize((sizeof(uint8_t)) * n_index);
|
||||
make_index(std::uint8_t{}, common::kUint8BinsTypeSize);
|
||||
} else if ((MaxNumBinPerFeat() - 1 > static_cast<int>(std::numeric_limits<uint8_t>::max()) &&
|
||||
MaxNumBinPerFeat() - 1 <= static_cast<int>(std::numeric_limits<uint16_t>::max())) &&
|
||||
isDense) {
|
||||
// compress dense index to uint16
|
||||
index.SetBinTypeSize(common::kUint16BinsTypeSize);
|
||||
index.Resize((sizeof(uint16_t)) * n_index);
|
||||
make_index(std::uint16_t{}, common::kUint16BinsTypeSize);
|
||||
} else {
|
||||
index.SetBinTypeSize(common::kUint32BinsTypeSize);
|
||||
index.Resize((sizeof(uint32_t)) * n_index);
|
||||
// no compression
|
||||
make_index(std::uint32_t{}, common::kUint32BinsTypeSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,11 +235,11 @@ float GHistIndexMatrix::GetFvalue(std::vector<std::uint32_t> const &ptrs,
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
|
||||
bool GHistIndexMatrix::ReadColumnPage(dmlc::SeekStream *fi) {
|
||||
bool GHistIndexMatrix::ReadColumnPage(common::AlignedResourceReadStream *fi) {
|
||||
return this->columns_->Read(fi, this->cut.Ptrs().data());
|
||||
}
|
||||
|
||||
size_t GHistIndexMatrix::WriteColumnPage(dmlc::Stream *fo) const {
|
||||
std::size_t GHistIndexMatrix::WriteColumnPage(common::AlignedFileWriteStream *fo) const {
|
||||
return this->columns_->Write(fo);
|
||||
}
|
||||
} // namespace xgboost
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Copyright 2022 by XGBoost Contributors
|
||||
/**
|
||||
* Copyright 2022-2023, XGBoost Contributors
|
||||
*/
|
||||
#include <memory> // std::unique_ptr
|
||||
|
||||
@@ -41,9 +41,9 @@ void SetIndexData(Context const* ctx, EllpackPageImpl const* page,
|
||||
}
|
||||
|
||||
void GetRowPtrFromEllpack(Context const* ctx, EllpackPageImpl const* page,
|
||||
std::vector<size_t>* p_out) {
|
||||
common::RefResourceView<std::size_t>* p_out) {
|
||||
auto& row_ptr = *p_out;
|
||||
row_ptr.resize(page->Size() + 1, 0);
|
||||
row_ptr = common::MakeFixedVecWithMalloc(page->Size() + 1, std::size_t{0});
|
||||
if (page->is_dense) {
|
||||
std::fill(row_ptr.begin() + 1, row_ptr.end(), page->row_stride);
|
||||
} else {
|
||||
@@ -95,7 +95,7 @@ GHistIndexMatrix::GHistIndexMatrix(Context const* ctx, MetaInfo const& info,
|
||||
ctx, page, &hit_count_tloc_, [&](auto bin_idx, auto) { return bin_idx; }, this);
|
||||
}
|
||||
|
||||
this->hit_count.resize(n_bins_total, 0);
|
||||
this->hit_count = common::MakeFixedVecWithMalloc(n_bins_total, std::size_t{0});
|
||||
this->GatherHitCount(ctx->Threads(), n_bins_total);
|
||||
|
||||
// sanity checks
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
#include <atomic> // for atomic
|
||||
#include <cinttypes> // for uint32_t
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory>
|
||||
#include <memory> // for make_unique
|
||||
#include <vector>
|
||||
|
||||
#include "../common/categorical.h"
|
||||
#include "../common/error_msg.h" // for InfInData
|
||||
#include "../common/hist_util.h"
|
||||
#include "../common/numeric.h"
|
||||
#include "../common/ref_resource_view.h" // for RefResourceView
|
||||
#include "../common/threading_utils.h"
|
||||
#include "../common/transform_iterator.h" // for MakeIndexTransformIter
|
||||
#include "adapter.h"
|
||||
@@ -25,9 +26,11 @@
|
||||
namespace xgboost {
|
||||
namespace common {
|
||||
class ColumnMatrix;
|
||||
class AlignedFileWriteStream;
|
||||
} // namespace common
|
||||
/*!
|
||||
* \brief preprocessed global index matrix, in CSR format
|
||||
|
||||
/**
|
||||
* @brief preprocessed global index matrix, in CSR format.
|
||||
*
|
||||
* Transform floating values to integer index in histogram This is a global histogram
|
||||
* index for CPU histogram. On GPU ellpack page is used.
|
||||
@@ -133,20 +136,22 @@ class GHistIndexMatrix {
|
||||
}
|
||||
|
||||
public:
|
||||
/*! \brief row pointer to rows by element position */
|
||||
std::vector<size_t> row_ptr;
|
||||
/*! \brief The index data */
|
||||
/** @brief row pointer to rows by element position */
|
||||
common::RefResourceView<std::size_t> row_ptr;
|
||||
/** @brief data storage for index. */
|
||||
common::RefResourceView<std::uint8_t> data;
|
||||
/** @brief The histogram index. */
|
||||
common::Index index;
|
||||
/*! \brief hit count of each index, used for constructing the ColumnMatrix */
|
||||
std::vector<size_t> hit_count;
|
||||
/*! \brief The corresponding cuts */
|
||||
/** @brief hit count of each index, used for constructing the ColumnMatrix */
|
||||
common::RefResourceView<std::size_t> hit_count;
|
||||
/** @brief The corresponding cuts */
|
||||
common::HistogramCuts cut;
|
||||
/** \brief max_bin for each feature. */
|
||||
/** @brief max_bin for each feature. */
|
||||
bst_bin_t max_numeric_bins_per_feat;
|
||||
/*! \brief base row index for current page (used by external memory) */
|
||||
size_t base_rowid{0};
|
||||
/** @brief base row index for current page (used by external memory) */
|
||||
bst_row_t base_rowid{0};
|
||||
|
||||
bst_bin_t MaxNumBinPerFeat() const {
|
||||
[[nodiscard]] bst_bin_t MaxNumBinPerFeat() const {
|
||||
return std::max(static_cast<bst_bin_t>(cut.MaxCategory() + 1), max_numeric_bins_per_feat);
|
||||
}
|
||||
|
||||
@@ -155,7 +160,7 @@ class GHistIndexMatrix {
|
||||
* \brief Constrcutor for SimpleDMatrix.
|
||||
*/
|
||||
GHistIndexMatrix(Context const* ctx, DMatrix* x, bst_bin_t max_bins_per_feat,
|
||||
double sparse_thresh, bool sorted_sketch, common::Span<float> hess = {});
|
||||
double sparse_thresh, bool sorted_sketch, common::Span<float const> hess = {});
|
||||
/**
|
||||
* \brief Constructor for Iterative DMatrix. Initialize basic information and prepare
|
||||
* for push batch.
|
||||
@@ -218,29 +223,30 @@ class GHistIndexMatrix {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDense() const {
|
||||
return isDense_;
|
||||
}
|
||||
[[nodiscard]] bool IsDense() const { return isDense_; }
|
||||
void SetDense(bool is_dense) { isDense_ = is_dense; }
|
||||
/**
|
||||
* \brief Get the local row index.
|
||||
* @brief Get the local row index.
|
||||
*/
|
||||
size_t RowIdx(size_t ridx) const { return row_ptr[ridx - base_rowid]; }
|
||||
[[nodiscard]] std::size_t RowIdx(size_t ridx) const { return row_ptr[ridx - base_rowid]; }
|
||||
|
||||
bst_row_t Size() const { return row_ptr.empty() ? 0 : row_ptr.size() - 1; }
|
||||
bst_feature_t Features() const { return cut.Ptrs().size() - 1; }
|
||||
[[nodiscard]] bst_row_t Size() const { return row_ptr.empty() ? 0 : row_ptr.size() - 1; }
|
||||
[[nodiscard]] bst_feature_t Features() const { return cut.Ptrs().size() - 1; }
|
||||
|
||||
bool ReadColumnPage(dmlc::SeekStream* fi);
|
||||
size_t WriteColumnPage(dmlc::Stream* fo) const;
|
||||
[[nodiscard]] bool ReadColumnPage(common::AlignedResourceReadStream* fi);
|
||||
[[nodiscard]] std::size_t WriteColumnPage(common::AlignedFileWriteStream* fo) const;
|
||||
|
||||
common::ColumnMatrix const& Transpose() const;
|
||||
[[nodiscard]] common::ColumnMatrix const& Transpose() const;
|
||||
|
||||
bst_bin_t GetGindex(size_t ridx, size_t fidx) const;
|
||||
[[nodiscard]] bst_bin_t GetGindex(size_t ridx, size_t fidx) const;
|
||||
|
||||
float GetFvalue(size_t ridx, size_t fidx, bool is_cat) const;
|
||||
float GetFvalue(std::vector<std::uint32_t> const& ptrs, std::vector<float> const& values,
|
||||
std::vector<float> const& mins, bst_row_t ridx, bst_feature_t fidx,
|
||||
bool is_cat) const;
|
||||
[[nodiscard]] float GetFvalue(size_t ridx, size_t fidx, bool is_cat) const;
|
||||
[[nodiscard]] float GetFvalue(std::vector<std::uint32_t> const& ptrs,
|
||||
std::vector<float> const& values, std::vector<float> const& mins,
|
||||
bst_row_t ridx, bst_feature_t fidx, bool is_cat) const;
|
||||
|
||||
[[nodiscard]] common::HistogramCuts& Cuts() { return cut; }
|
||||
[[nodiscard]] common::HistogramCuts const& Cuts() const { return cut; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<common::ColumnMatrix> columns_;
|
||||
@@ -294,5 +300,5 @@ void AssignColumnBinIndex(GHistIndexMatrix const& page, Fn&& assign) {
|
||||
}
|
||||
});
|
||||
}
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost
|
||||
#endif // XGBOOST_DATA_GRADIENT_INDEX_H_
|
||||
|
||||
@@ -1,38 +1,49 @@
|
||||
/*!
|
||||
* Copyright 2021-2022 XGBoost contributors
|
||||
/**
|
||||
* Copyright 2021-2023 XGBoost contributors
|
||||
*/
|
||||
#include "sparse_page_writer.h"
|
||||
#include "gradient_index.h"
|
||||
#include "histogram_cut_format.h"
|
||||
#include <cstddef> // for size_t
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <type_traits> // for underlying_type_t
|
||||
#include <vector> // for vector
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
#include "../common/io.h" // for AlignedResourceReadStream
|
||||
#include "../common/ref_resource_view.h" // for ReadVec, WriteVec
|
||||
#include "gradient_index.h" // for GHistIndexMatrix
|
||||
#include "histogram_cut_format.h" // for ReadHistogramCuts
|
||||
#include "sparse_page_writer.h" // for SparsePageFormat
|
||||
|
||||
namespace xgboost::data {
|
||||
class GHistIndexRawFormat : public SparsePageFormat<GHistIndexMatrix> {
|
||||
public:
|
||||
bool Read(GHistIndexMatrix* page, dmlc::SeekStream* fi) override {
|
||||
bool Read(GHistIndexMatrix* page, common::AlignedResourceReadStream* fi) override {
|
||||
CHECK(fi);
|
||||
|
||||
if (!ReadHistogramCuts(&page->cut, fi)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// indptr
|
||||
fi->Read(&page->row_ptr);
|
||||
// data
|
||||
std::vector<uint8_t> data;
|
||||
if (!fi->Read(&data)) {
|
||||
if (!common::ReadVec(fi, &page->row_ptr)) {
|
||||
return false;
|
||||
}
|
||||
page->index.Resize(data.size());
|
||||
std::copy(data.cbegin(), data.cend(), page->index.begin());
|
||||
// bin type
|
||||
|
||||
// data
|
||||
// - bin type
|
||||
// Old gcc doesn't support reading from enum.
|
||||
std::underlying_type_t<common::BinTypeSize> uint_bin_type{0};
|
||||
if (!fi->Read(&uint_bin_type)) {
|
||||
return false;
|
||||
}
|
||||
common::BinTypeSize size_type =
|
||||
static_cast<common::BinTypeSize>(uint_bin_type);
|
||||
page->index.SetBinTypeSize(size_type);
|
||||
common::BinTypeSize size_type = static_cast<common::BinTypeSize>(uint_bin_type);
|
||||
// - index buffer
|
||||
if (!common::ReadVec(fi, &page->data)) {
|
||||
return false;
|
||||
}
|
||||
// - index
|
||||
page->index = common::Index{common::Span{page->data.data(), page->data.size()}, size_type};
|
||||
|
||||
// hit count
|
||||
if (!fi->Read(&page->hit_count)) {
|
||||
if (!common::ReadVec(fi, &page->hit_count)) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&page->max_numeric_bins_per_feat)) {
|
||||
@@ -50,38 +61,35 @@ class GHistIndexRawFormat : public SparsePageFormat<GHistIndexMatrix> {
|
||||
page->index.SetBinOffset(page->cut.Ptrs());
|
||||
}
|
||||
|
||||
page->ReadColumnPage(fi);
|
||||
if (!page->ReadColumnPage(fi)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Write(GHistIndexMatrix const &page, dmlc::Stream *fo) override {
|
||||
size_t bytes = 0;
|
||||
std::size_t Write(GHistIndexMatrix const& page, common::AlignedFileWriteStream* fo) override {
|
||||
std::size_t bytes = 0;
|
||||
bytes += WriteHistogramCuts(page.cut, fo);
|
||||
// indptr
|
||||
fo->Write(page.row_ptr);
|
||||
bytes += page.row_ptr.size() * sizeof(decltype(page.row_ptr)::value_type) +
|
||||
sizeof(uint64_t);
|
||||
bytes += common::WriteVec(fo, page.row_ptr);
|
||||
|
||||
// data
|
||||
std::vector<uint8_t> data(page.index.begin(), page.index.end());
|
||||
fo->Write(data);
|
||||
bytes += data.size() * sizeof(decltype(data)::value_type) + sizeof(uint64_t);
|
||||
// bin type
|
||||
std::underlying_type_t<common::BinTypeSize> uint_bin_type =
|
||||
page.index.GetBinTypeSize();
|
||||
fo->Write(uint_bin_type);
|
||||
bytes += sizeof(page.index.GetBinTypeSize());
|
||||
// - bin type
|
||||
std::underlying_type_t<common::BinTypeSize> uint_bin_type = page.index.GetBinTypeSize();
|
||||
bytes += fo->Write(uint_bin_type);
|
||||
// - index buffer
|
||||
std::vector<std::uint8_t> data(page.index.begin(), page.index.end());
|
||||
bytes += fo->Write(static_cast<std::uint64_t>(data.size()));
|
||||
if (!data.empty()) {
|
||||
bytes += fo->Write(data.data(), data.size());
|
||||
}
|
||||
|
||||
// hit count
|
||||
fo->Write(page.hit_count);
|
||||
bytes +=
|
||||
page.hit_count.size() * sizeof(decltype(page.hit_count)::value_type) +
|
||||
sizeof(uint64_t);
|
||||
bytes += common::WriteVec(fo, page.hit_count);
|
||||
// max_bins, base row, is_dense
|
||||
fo->Write(page.max_numeric_bins_per_feat);
|
||||
bytes += sizeof(page.max_numeric_bins_per_feat);
|
||||
fo->Write(page.base_rowid);
|
||||
bytes += sizeof(page.base_rowid);
|
||||
fo->Write(page.IsDense());
|
||||
bytes += sizeof(page.IsDense());
|
||||
bytes += fo->Write(page.max_numeric_bins_per_feat);
|
||||
bytes += fo->Write(page.base_rowid);
|
||||
bytes += fo->Write(page.IsDense());
|
||||
|
||||
bytes += page.WriteColumnPage(fo);
|
||||
return bytes;
|
||||
@@ -93,6 +101,4 @@ DMLC_REGISTRY_FILE_TAG(gradient_index_format);
|
||||
XGBOOST_REGISTER_GHIST_INDEX_PAGE_FORMAT(raw)
|
||||
.describe("Raw GHistIndex binary data format.")
|
||||
.set_body([]() { return new GHistIndexRawFormat(); });
|
||||
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/*!
|
||||
* Copyright 2021-2022 by XGBoost Contributors
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost Contributors
|
||||
*/
|
||||
#include "gradient_index_page_source.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
namespace xgboost::data {
|
||||
void GradientIndexPageSource::Fetch() {
|
||||
if (!this->ReadCache()) {
|
||||
if (count_ != 0 && !sync_) {
|
||||
@@ -21,5 +20,4 @@ void GradientIndexPageSource::Fetch() {
|
||||
this->WriteCache();
|
||||
}
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
/*!
|
||||
* Copyright 2021 XGBoost contributors
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost contributors
|
||||
*/
|
||||
#ifndef XGBOOST_DATA_HISTOGRAM_CUT_FORMAT_H_
|
||||
#define XGBOOST_DATA_HISTOGRAM_CUT_FORMAT_H_
|
||||
|
||||
#include "../common/hist_util.h"
|
||||
#include <dmlc/io.h> // for Stream
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
inline bool ReadHistogramCuts(common::HistogramCuts *cuts, dmlc::SeekStream *fi) {
|
||||
if (!fi->Read(&cuts->cut_values_.HostVector())) {
|
||||
#include <cstddef> // for size_t
|
||||
|
||||
#include "../common/hist_util.h" // for HistogramCuts
|
||||
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
|
||||
#include "../common/ref_resource_view.h" // for WriteVec, ReadVec
|
||||
|
||||
namespace xgboost::data {
|
||||
inline bool ReadHistogramCuts(common::HistogramCuts *cuts, common::AlignedResourceReadStream *fi) {
|
||||
if (!common::ReadVec(fi, &cuts->cut_values_.HostVector())) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&cuts->cut_ptrs_.HostVector())) {
|
||||
if (!common::ReadVec(fi, &cuts->cut_ptrs_.HostVector())) {
|
||||
return false;
|
||||
}
|
||||
if (!fi->Read(&cuts->min_vals_.HostVector())) {
|
||||
if (!common::ReadVec(fi, &cuts->min_vals_.HostVector())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t WriteHistogramCuts(common::HistogramCuts const &cuts, dmlc::Stream *fo) {
|
||||
size_t bytes = 0;
|
||||
fo->Write(cuts.cut_values_.ConstHostVector());
|
||||
bytes += cuts.cut_values_.ConstHostSpan().size_bytes() + sizeof(uint64_t);
|
||||
fo->Write(cuts.cut_ptrs_.ConstHostVector());
|
||||
bytes += cuts.cut_ptrs_.ConstHostSpan().size_bytes() + sizeof(uint64_t);
|
||||
fo->Write(cuts.min_vals_.ConstHostVector());
|
||||
bytes += cuts.min_vals_.ConstHostSpan().size_bytes() + sizeof(uint64_t);
|
||||
inline std::size_t WriteHistogramCuts(common::HistogramCuts const &cuts,
|
||||
common::AlignedFileWriteStream *fo) {
|
||||
std::size_t bytes = 0;
|
||||
bytes += common::WriteVec(fo, cuts.Values());
|
||||
bytes += common::WriteVec(fo, cuts.Ptrs());
|
||||
bytes += common::WriteVec(fo, cuts.MinValues());
|
||||
return bytes;
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_HISTOGRAM_CUT_FORMAT_H_
|
||||
|
||||
@@ -33,10 +33,11 @@ IterativeDMatrix::IterativeDMatrix(DataIterHandle iter_handle, DMatrixHandle pro
|
||||
bool valid = iter.Next();
|
||||
CHECK(valid) << "Iterative DMatrix must have at least 1 batch.";
|
||||
|
||||
auto d = MakeProxy(proxy_)->DeviceIdx();
|
||||
auto pctx = MakeProxy(proxy_)->Ctx();
|
||||
|
||||
Context ctx;
|
||||
ctx.UpdateAllowUnknown(Args{{"nthread", std::to_string(nthread)}, {"gpu_id", std::to_string(d)}});
|
||||
ctx.UpdateAllowUnknown(
|
||||
Args{{"nthread", std::to_string(nthread)}, {"device", pctx->DeviceName()}});
|
||||
// hardcoded parameter.
|
||||
BatchParam p{max_bin, tree::TrainParam::DftSparseThreshold()};
|
||||
|
||||
@@ -240,9 +241,9 @@ void IterativeDMatrix::InitFromCPU(Context const* ctx, BatchParam const& p,
|
||||
* Generate gradient index.
|
||||
*/
|
||||
this->ghist_ = std::make_unique<GHistIndexMatrix>(Info(), std::move(cuts), p.max_bin);
|
||||
size_t rbegin = 0;
|
||||
size_t prev_sum = 0;
|
||||
size_t i = 0;
|
||||
std::size_t rbegin = 0;
|
||||
std::size_t prev_sum = 0;
|
||||
std::size_t i = 0;
|
||||
while (iter.Next()) {
|
||||
HostAdapterDispatch(proxy, [&](auto const& batch) {
|
||||
proxy->Info().num_nonzero_ = batch_nnz[i];
|
||||
|
||||
@@ -31,10 +31,10 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
dh::XGBCachingDeviceAllocator<char> alloc;
|
||||
|
||||
auto num_rows = [&]() {
|
||||
return Dispatch(proxy, [](auto const& value) { return value.NumRows(); });
|
||||
return cuda_impl::Dispatch(proxy, [](auto const& value) { return value.NumRows(); });
|
||||
};
|
||||
auto num_cols = [&]() {
|
||||
return Dispatch(proxy, [](auto const& value) { return value.NumCols(); });
|
||||
return cuda_impl::Dispatch(proxy, [](auto const& value) { return value.NumCols(); });
|
||||
};
|
||||
|
||||
size_t row_stride = 0;
|
||||
@@ -86,7 +86,7 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
get_device());
|
||||
auto* p_sketch = &sketch_containers.back();
|
||||
proxy->Info().weights_.SetDevice(get_device());
|
||||
Dispatch(proxy, [&](auto const& value) {
|
||||
cuda_impl::Dispatch(proxy, [&](auto const& value) {
|
||||
common::AdapterDeviceSketch(value, p.max_bin, proxy->Info(), missing, p_sketch);
|
||||
});
|
||||
}
|
||||
@@ -94,7 +94,7 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
accumulated_rows += batch_rows;
|
||||
dh::device_vector<size_t> row_counts(batch_rows + 1, 0);
|
||||
common::Span<size_t> row_counts_span(row_counts.data().get(), row_counts.size());
|
||||
row_stride = std::max(row_stride, Dispatch(proxy, [=](auto const& value) {
|
||||
row_stride = std::max(row_stride, cuda_impl::Dispatch(proxy, [=](auto const& value) {
|
||||
return GetRowCounts(value, row_counts_span, get_device(), missing);
|
||||
}));
|
||||
|
||||
@@ -129,7 +129,7 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
sketch_containers.clear();
|
||||
sketch_containers.shrink_to_fit();
|
||||
|
||||
final_sketch.MakeCuts(&cuts);
|
||||
final_sketch.MakeCuts(&cuts, this->info_.IsColumnSplit());
|
||||
} else {
|
||||
GetCutsFromRef(ctx, ref, Info().num_col_, p, &cuts);
|
||||
}
|
||||
@@ -137,7 +137,7 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
this->info_.num_row_ = accumulated_rows;
|
||||
this->info_.num_nonzero_ = nnz;
|
||||
|
||||
auto init_page = [this, &proxy, &cuts, row_stride, accumulated_rows, get_device]() {
|
||||
auto init_page = [this, &cuts, row_stride, accumulated_rows, get_device]() {
|
||||
if (!ellpack_) {
|
||||
// Should be put inside the while loop to protect against empty batch. In
|
||||
// that case device id is invalid.
|
||||
@@ -165,14 +165,14 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
|
||||
auto rows = num_rows();
|
||||
dh::device_vector<size_t> row_counts(rows + 1, 0);
|
||||
common::Span<size_t> row_counts_span(row_counts.data().get(), row_counts.size());
|
||||
Dispatch(proxy, [=](auto const& value) {
|
||||
cuda_impl::Dispatch(proxy, [=](auto const& value) {
|
||||
return GetRowCounts(value, row_counts_span, get_device(), missing);
|
||||
});
|
||||
auto is_dense = this->IsDense();
|
||||
|
||||
proxy->Info().feature_types.SetDevice(get_device());
|
||||
auto d_feature_types = proxy->Info().feature_types.ConstDeviceSpan();
|
||||
auto new_impl = Dispatch(proxy, [&](auto const& value) {
|
||||
auto new_impl = cuda_impl::Dispatch(proxy, [&](auto const& value) {
|
||||
return EllpackPageImpl(value, missing, get_device(), is_dense, row_counts_span,
|
||||
d_feature_types, row_stride, rows, cuts);
|
||||
});
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
/*!
|
||||
* Copyright 2021 by Contributors
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost Contributors
|
||||
* \file proxy_dmatrix.cc
|
||||
*/
|
||||
|
||||
#include "proxy_dmatrix.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
void DMatrixProxy::SetArrayData(char const *c_interface) {
|
||||
std::shared_ptr<ArrayAdapter> adapter{new ArrayAdapter(StringView{c_interface})};
|
||||
namespace xgboost::data {
|
||||
void DMatrixProxy::SetArrayData(StringView interface_str) {
|
||||
std::shared_ptr<ArrayAdapter> adapter{new ArrayAdapter{interface_str}};
|
||||
this->batch_ = adapter;
|
||||
this->Info().num_col_ = adapter->NumColumns();
|
||||
this->Info().num_row_ = adapter->NumRows();
|
||||
@@ -25,5 +24,38 @@ void DMatrixProxy::SetCSRData(char const *c_indptr, char const *c_indices,
|
||||
this->Info().num_row_ = adapter->NumRows();
|
||||
this->ctx_.gpu_id = Context::kCpuId;
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
|
||||
namespace cuda_impl {
|
||||
std::shared_ptr<DMatrix> CreateDMatrixFromProxy(Context const *ctx,
|
||||
std::shared_ptr<DMatrixProxy> proxy, float missing);
|
||||
#if !defined(XGBOOST_USE_CUDA) && !defined(XGBOOST_USE_HIP)
|
||||
std::shared_ptr<DMatrix> CreateDMatrixFromProxy(Context const *, std::shared_ptr<DMatrixProxy>,
|
||||
float) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif // XGBOOST_USE_CUDA
|
||||
} // namespace cuda_impl
|
||||
|
||||
std::shared_ptr<DMatrix> CreateDMatrixFromProxy(Context const *ctx,
|
||||
std::shared_ptr<DMatrixProxy> proxy,
|
||||
float missing) {
|
||||
bool type_error{false};
|
||||
std::shared_ptr<DMatrix> p_fmat{nullptr};
|
||||
if (proxy->Ctx()->IsCPU()) {
|
||||
p_fmat = data::HostAdapterDispatch<false>(
|
||||
proxy.get(),
|
||||
[&](auto const &adapter) {
|
||||
auto p_fmat =
|
||||
std::shared_ptr<DMatrix>(DMatrix::Create(adapter.get(), missing, ctx->Threads()));
|
||||
return p_fmat;
|
||||
},
|
||||
&type_error);
|
||||
} else {
|
||||
p_fmat = cuda_impl::CreateDMatrixFromProxy(ctx, proxy, missing);
|
||||
}
|
||||
|
||||
CHECK(p_fmat) << "Failed to fallback.";
|
||||
p_fmat->Info() = proxy->Info().Copy();
|
||||
return p_fmat;
|
||||
}
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -1,35 +1,47 @@
|
||||
/*!
|
||||
* Copyright 2020-2022, XGBoost contributors
|
||||
/**
|
||||
* Copyright 2020-2023, XGBoost contributors
|
||||
*/
|
||||
#include "proxy_dmatrix.h"
|
||||
#include "device_adapter.cuh"
|
||||
#include "proxy_dmatrix.cuh"
|
||||
#include "proxy_dmatrix.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
|
||||
namespace xgboost::data {
|
||||
void DMatrixProxy::FromCudaColumnar(StringView interface_str) {
|
||||
std::shared_ptr<data::CudfAdapter> adapter{new CudfAdapter{interface_str}};
|
||||
auto const& value = adapter->Value();
|
||||
auto adapter{std::make_shared<CudfAdapter>(interface_str)};
|
||||
this->batch_ = adapter;
|
||||
ctx_.gpu_id = adapter->DeviceIdx();
|
||||
this->Info().num_col_ = adapter->NumColumns();
|
||||
this->Info().num_row_ = adapter->NumRows();
|
||||
if (ctx_.gpu_id < 0) {
|
||||
if (adapter->DeviceIdx() < 0) {
|
||||
// empty data
|
||||
CHECK_EQ(this->Info().num_row_, 0);
|
||||
ctx_.gpu_id = dh::CurrentDevice();
|
||||
ctx_ = ctx_.MakeCUDA(dh::CurrentDevice());
|
||||
return;
|
||||
}
|
||||
ctx_ = ctx_.MakeCUDA(adapter->DeviceIdx());
|
||||
}
|
||||
|
||||
void DMatrixProxy::FromCudaArray(StringView interface_str) {
|
||||
std::shared_ptr<CupyAdapter> adapter(new CupyAdapter{StringView{interface_str}});
|
||||
auto adapter(std::make_shared<CupyAdapter>(StringView{interface_str}));
|
||||
this->batch_ = adapter;
|
||||
ctx_.gpu_id = adapter->DeviceIdx();
|
||||
this->Info().num_col_ = adapter->NumColumns();
|
||||
this->Info().num_row_ = adapter->NumRows();
|
||||
if (ctx_.gpu_id < 0) {
|
||||
if (adapter->DeviceIdx() < 0) {
|
||||
// empty data
|
||||
CHECK_EQ(this->Info().num_row_, 0);
|
||||
ctx_.gpu_id = dh::CurrentDevice();
|
||||
ctx_ = ctx_.MakeCUDA(dh::CurrentDevice());
|
||||
return;
|
||||
}
|
||||
ctx_ = ctx_.MakeCUDA(adapter->DeviceIdx());
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
|
||||
namespace cuda_impl {
|
||||
std::shared_ptr<DMatrix> CreateDMatrixFromProxy(Context const* ctx,
|
||||
std::shared_ptr<DMatrixProxy> proxy,
|
||||
float missing) {
|
||||
return Dispatch<false>(proxy.get(), [&](auto const& adapter) {
|
||||
auto p_fmat = std::shared_ptr<DMatrix>{DMatrix::Create(adapter.get(), missing, ctx->Threads())};
|
||||
return p_fmat;
|
||||
});
|
||||
}
|
||||
} // namespace cuda_impl
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -6,19 +6,34 @@
|
||||
#include "device_adapter.cuh"
|
||||
#include "proxy_dmatrix.h"
|
||||
|
||||
namespace xgboost::data {
|
||||
template <typename Fn>
|
||||
namespace xgboost::data::cuda_impl {
|
||||
template <bool get_value = true, typename Fn>
|
||||
decltype(auto) Dispatch(DMatrixProxy const* proxy, Fn fn) {
|
||||
if (proxy->Adapter().type() == typeid(std::shared_ptr<CupyAdapter>)) {
|
||||
auto value = std::any_cast<std::shared_ptr<CupyAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
if constexpr (get_value) {
|
||||
auto value = std::any_cast<std::shared_ptr<CupyAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
} else {
|
||||
auto value = std::any_cast<std::shared_ptr<CupyAdapter>>(proxy->Adapter());
|
||||
return fn(value);
|
||||
}
|
||||
} else if (proxy->Adapter().type() == typeid(std::shared_ptr<CudfAdapter>)) {
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
if constexpr (get_value) {
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
} else {
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter());
|
||||
return fn(value);
|
||||
}
|
||||
} else {
|
||||
LOG(FATAL) << "Unknown type: " << proxy->Adapter().type().name();
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
if constexpr (get_value) {
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
} else {
|
||||
auto value = std::any_cast<std::shared_ptr<CudfAdapter>>(proxy->Adapter());
|
||||
return fn(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace xgboost::data
|
||||
} // namespace xgboost::data::cuda_impl
|
||||
|
||||
@@ -62,7 +62,7 @@ class DMatrixProxy : public DMatrix {
|
||||
#endif // defined(XGBOOST_USE_CUDA) || defined(XGBOOST_USE_HIP)
|
||||
}
|
||||
|
||||
void SetArrayData(char const* c_interface);
|
||||
void SetArrayData(StringView interface_str);
|
||||
void SetCSRData(char const* c_indptr, char const* c_indices, char const* c_values,
|
||||
bst_feature_t n_features, bool on_host);
|
||||
|
||||
@@ -114,28 +114,62 @@ inline DMatrixProxy* MakeProxy(DMatrixHandle proxy) {
|
||||
return typed;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
/**
|
||||
* @brief Dispatch function call based on input type.
|
||||
*
|
||||
* @tparam get_value Whether the funciton Fn accept an adapter batch or the adapter itself.
|
||||
* @tparam Fn The type of the function to be dispatched.
|
||||
*
|
||||
* @param proxy The proxy object holding the reference to the input.
|
||||
* @param fn The function to be dispatched.
|
||||
* @param type_error[out] Set to ture if it's not null and the input data is not recognized by
|
||||
* the host.
|
||||
*
|
||||
* @return The return value of the function being dispatched.
|
||||
*/
|
||||
template <bool get_value = true, typename Fn>
|
||||
decltype(auto) HostAdapterDispatch(DMatrixProxy const* proxy, Fn fn, bool* type_error = nullptr) {
|
||||
if (proxy->Adapter().type() == typeid(std::shared_ptr<CSRArrayAdapter>)) {
|
||||
auto value = std::any_cast<std::shared_ptr<CSRArrayAdapter>>(proxy->Adapter())->Value();
|
||||
if constexpr (get_value) {
|
||||
auto value = std::any_cast<std::shared_ptr<CSRArrayAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
} else {
|
||||
auto value = std::any_cast<std::shared_ptr<CSRArrayAdapter>>(proxy->Adapter());
|
||||
return fn(value);
|
||||
}
|
||||
if (type_error) {
|
||||
*type_error = false;
|
||||
}
|
||||
return fn(value);
|
||||
} else if (proxy->Adapter().type() == typeid(std::shared_ptr<ArrayAdapter>)) {
|
||||
auto value = std::any_cast<std::shared_ptr<ArrayAdapter>>(proxy->Adapter())->Value();
|
||||
if constexpr (get_value) {
|
||||
auto value = std::any_cast<std::shared_ptr<ArrayAdapter>>(proxy->Adapter())->Value();
|
||||
return fn(value);
|
||||
} else {
|
||||
auto value = std::any_cast<std::shared_ptr<ArrayAdapter>>(proxy->Adapter());
|
||||
return fn(value);
|
||||
}
|
||||
if (type_error) {
|
||||
*type_error = false;
|
||||
}
|
||||
return fn(value);
|
||||
} else {
|
||||
if (type_error) {
|
||||
*type_error = true;
|
||||
} else {
|
||||
LOG(FATAL) << "Unknown type: " << proxy->Adapter().type().name();
|
||||
}
|
||||
return std::result_of_t<Fn(decltype(std::declval<std::shared_ptr<ArrayAdapter>>()->Value()))>();
|
||||
if constexpr (get_value) {
|
||||
return std::result_of_t<Fn(
|
||||
decltype(std::declval<std::shared_ptr<ArrayAdapter>>()->Value()))>();
|
||||
} else {
|
||||
return std::result_of_t<Fn(decltype(std::declval<std::shared_ptr<ArrayAdapter>>()))>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a `SimpleDMatrix` instance from a `DMatrixProxy`.
|
||||
*/
|
||||
std::shared_ptr<DMatrix> CreateDMatrixFromProxy(Context const* ctx,
|
||||
std::shared_ptr<DMatrixProxy> proxy, float missing);
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_PROXY_DMATRIX_H_
|
||||
|
||||
@@ -8,21 +8,21 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <numeric> // for accumulate
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../common/error_msg.h" // for InconsistentMaxBin
|
||||
#include "../common/random.h"
|
||||
#include "../common/threading_utils.h"
|
||||
#include "../collective/communicator-inl.h" // for GetWorldSize, GetRank, Allgather
|
||||
#include "../common/error_msg.h" // for InconsistentMaxBin
|
||||
#include "./simple_batch_iterator.h"
|
||||
#include "adapter.h"
|
||||
#include "batch_utils.h" // for CheckEmpty, RegenGHist
|
||||
#include "batch_utils.h" // for CheckEmpty, RegenGHist
|
||||
#include "ellpack_page.h" // for EllpackPage
|
||||
#include "gradient_index.h"
|
||||
#include "xgboost/c_api.h"
|
||||
#include "xgboost/data.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
namespace xgboost::data {
|
||||
MetaInfo& SimpleDMatrix::Info() { return info_; }
|
||||
|
||||
const MetaInfo& SimpleDMatrix::Info() const { return info_; }
|
||||
@@ -97,6 +97,10 @@ BatchSet<SparsePage> SimpleDMatrix::GetRowBatches() {
|
||||
BatchSet<CSCPage> SimpleDMatrix::GetColumnBatches(Context const* ctx) {
|
||||
// column page doesn't exist, generate it
|
||||
if (!column_page_) {
|
||||
auto n = std::numeric_limits<decltype(Entry::index)>::max();
|
||||
if (this->sparse_page_->Size() > n) {
|
||||
error::MaxSampleSize(n);
|
||||
}
|
||||
column_page_.reset(new CSCPage(sparse_page_->GetTranspose(info_.num_col_, ctx->Threads())));
|
||||
}
|
||||
auto begin_iter = BatchIterator<CSCPage>(new SimpleBatchIteratorImpl<CSCPage>(column_page_));
|
||||
@@ -106,6 +110,10 @@ BatchSet<CSCPage> SimpleDMatrix::GetColumnBatches(Context const* ctx) {
|
||||
BatchSet<SortedCSCPage> SimpleDMatrix::GetSortedColumnBatches(Context const* ctx) {
|
||||
// Sorted column page doesn't exist, generate it
|
||||
if (!sorted_column_page_) {
|
||||
auto n = std::numeric_limits<decltype(Entry::index)>::max();
|
||||
if (this->sparse_page_->Size() > n) {
|
||||
error::MaxSampleSize(n);
|
||||
}
|
||||
sorted_column_page_.reset(
|
||||
new SortedCSCPage(sparse_page_->GetTranspose(info_.num_col_, ctx->Threads())));
|
||||
sorted_column_page_->SortRows(ctx->Threads());
|
||||
@@ -427,5 +435,4 @@ SimpleDMatrix::SimpleDMatrix(RecordBatchesIterAdapter* adapter, float missing, i
|
||||
|
||||
fmat_ctx_ = ctx;
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -32,7 +32,7 @@ SimpleDMatrix::SimpleDMatrix(AdapterT* adapter, float missing, std::int32_t nthr
|
||||
#endif
|
||||
|
||||
Context ctx;
|
||||
ctx.Init(Args{{"nthread", std::to_string(nthread)}, {"gpu_id", std::to_string(device)}});
|
||||
ctx.Init(Args{{"nthread", std::to_string(nthread)}, {"device", DeviceOrd::CUDA(device).Name()}});
|
||||
|
||||
CHECK(adapter->NumRows() != kAdapterUnknownSize);
|
||||
CHECK(adapter->NumColumns() != kAdapterUnknownSize);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "./sparse_page_dmatrix.h"
|
||||
|
||||
#include "../collective/communicator-inl.h"
|
||||
#include "./simple_batch_iterator.h"
|
||||
#include "batch_utils.h" // for RegenGHist
|
||||
#include "gradient_index.h"
|
||||
|
||||
@@ -165,7 +164,10 @@ BatchSet<SortedCSCPage> SparsePageDMatrix::GetSortedColumnBatches(Context const
|
||||
|
||||
BatchSet<GHistIndexMatrix> SparsePageDMatrix::GetGradientIndex(Context const *ctx,
|
||||
const BatchParam ¶m) {
|
||||
CHECK_GE(param.max_bin, 2);
|
||||
if (param.Initialized()) {
|
||||
CHECK_GE(param.max_bin, 2);
|
||||
}
|
||||
detail::CheckEmpty(batch_param_, param);
|
||||
auto id = MakeCache(this, ".gradient_index.page", cache_prefix_, &cache_info_);
|
||||
this->InitializeSparsePage(ctx);
|
||||
if (!cache_info_.at(id)->written || detail::RegenGHist(batch_param_, param)) {
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
/**
|
||||
* Copyright 2021-2023 by XGBoost contributors
|
||||
*/
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "../common/hist_util.cuh"
|
||||
#include "batch_utils.h" // for CheckEmpty, RegenGHist
|
||||
#include "../common/hist_util.h" // for HistogramCuts
|
||||
#include "batch_utils.h" // for CheckEmpty, RegenGHist
|
||||
#include "ellpack_page.cuh"
|
||||
#include "sparse_page_dmatrix.h"
|
||||
#include "sparse_page_source.h"
|
||||
#include "xgboost/context.h" // for Context
|
||||
#include "xgboost/data.h" // for BatchParam
|
||||
|
||||
namespace xgboost::data {
|
||||
BatchSet<EllpackPage> SparsePageDMatrix::GetEllpackBatches(Context const* ctx,
|
||||
const BatchParam& param) {
|
||||
CHECK(ctx->IsCUDA());
|
||||
CHECK_GE(param.max_bin, 2);
|
||||
if (param.Initialized()) {
|
||||
CHECK_GE(param.max_bin, 2);
|
||||
}
|
||||
detail::CheckEmpty(batch_param_, param);
|
||||
auto id = MakeCache(this, ".ellpack.page", cache_prefix_, &cache_info_);
|
||||
size_t row_stride = 0;
|
||||
@@ -21,8 +27,13 @@ BatchSet<EllpackPage> SparsePageDMatrix::GetEllpackBatches(Context const* ctx,
|
||||
cache_info_.erase(id);
|
||||
MakeCache(this, ".ellpack.page", cache_prefix_, &cache_info_);
|
||||
std::unique_ptr<common::HistogramCuts> cuts;
|
||||
cuts.reset(
|
||||
new common::HistogramCuts{common::DeviceSketch(ctx->gpu_id, this, param.max_bin, 0)});
|
||||
if (!param.hess.empty()) {
|
||||
cuts = std::make_unique<common::HistogramCuts>(
|
||||
common::DeviceSketchWithHessian(ctx, this, param.max_bin, param.hess));
|
||||
} else {
|
||||
cuts =
|
||||
std::make_unique<common::HistogramCuts>(common::DeviceSketch(ctx, this, param.max_bin));
|
||||
}
|
||||
this->InitializeSparsePage(ctx); // reset after use.
|
||||
|
||||
row_stride = GetRowStride(this);
|
||||
@@ -31,10 +42,10 @@ BatchSet<EllpackPage> SparsePageDMatrix::GetEllpackBatches(Context const* ctx,
|
||||
batch_param_ = param;
|
||||
|
||||
auto ft = this->info_.feature_types.ConstDeviceSpan();
|
||||
ellpack_page_source_.reset(); // release resources.
|
||||
ellpack_page_source_.reset(new EllpackPageSource(
|
||||
ellpack_page_source_.reset(); // make sure resource is released before making new ones.
|
||||
ellpack_page_source_ = std::make_shared<EllpackPageSource>(
|
||||
this->missing_, ctx->Threads(), this->Info().num_col_, this->n_batches_, cache_info_.at(id),
|
||||
param, std::move(cuts), this->IsDense(), row_stride, ft, sparse_page_source_, ctx->gpu_id));
|
||||
param, std::move(cuts), this->IsDense(), row_stride, ft, sparse_page_source_, ctx->gpu_id);
|
||||
} else {
|
||||
CHECK(sparse_page_source_);
|
||||
ellpack_page_source_->Reset();
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
#ifndef XGBOOST_DATA_SPARSE_PAGE_DMATRIX_H_
|
||||
#define XGBOOST_DATA_SPARSE_PAGE_DMATRIX_H_
|
||||
|
||||
#include <xgboost/data.h>
|
||||
#include <xgboost/logging.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -20,35 +17,33 @@
|
||||
#include "ellpack_page_source.h"
|
||||
#include "gradient_index_page_source.h"
|
||||
#include "sparse_page_source.h"
|
||||
#include "xgboost/data.h"
|
||||
#include "xgboost/logging.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
namespace xgboost::data {
|
||||
/**
|
||||
* \brief DMatrix used for external memory.
|
||||
*
|
||||
* The external memory is created for controlling memory usage by splitting up data into
|
||||
* multiple batches. However that doesn't mean we will actually process exact 1 batch at
|
||||
* a time, which would be terribly slow considering that we have to loop through the
|
||||
* whole dataset for every tree split. So we use async pre-fetch and let caller to decide
|
||||
* how many batches it wants to process by returning data as shared pointer. The caller
|
||||
* can use async function to process the data or just stage those batches, making the
|
||||
* decision is out of the scope for sparse page dmatrix. These 2 optimizations might
|
||||
* defeat the purpose of splitting up dataset since if you load all the batches then the
|
||||
* memory usage is even worse than using a single batch. Essentially we need to control
|
||||
* how many batches can be in memory at the same time.
|
||||
* multiple batches. However that doesn't mean we will actually process exactly 1 batch
|
||||
* at a time, which would be terribly slow considering that we have to loop through the
|
||||
* whole dataset for every tree split. So we use async to pre-fetch pages and let the
|
||||
* caller to decide how many batches it wants to process by returning data as a shared
|
||||
* pointer. The caller can use async function to process the data or just stage those
|
||||
* batches based on its use cases. These two optimizations might defeat the purpose of
|
||||
* splitting up dataset since if you stage all the batches then the memory usage might be
|
||||
* even worse than using a single batch. As a result, we must control how many batches can
|
||||
* be in memory at any given time.
|
||||
*
|
||||
* Right now the write to the cache is sequential operation and is blocking, reading from
|
||||
* cache is async but with a hard coded limit of 4 pages as an heuristic. So by sparse
|
||||
* dmatrix itself there can be only 9 pages in main memory (might be of different types)
|
||||
* at the same time: 1 page pending for write, 4 pre-fetched sparse pages, 4 pre-fetched
|
||||
* dependent pages. If the caller stops iteration at the middle and start again, then the
|
||||
* number of pages in memory can hit 16 due to pre-fetching, but this should be a bug in
|
||||
* caller's code (XGBoost doesn't discard a large portion of data at the end, there's not
|
||||
* sampling algo that samples only the first portion of data).
|
||||
* Right now the write to the cache is a sequential operation and is blocking. Reading
|
||||
* from cache on ther other hand, is async but with a hard coded limit of 3 pages as an
|
||||
* heuristic. So by sparse dmatrix itself there can be only 7 pages in main memory (might
|
||||
* be of different types) at the same time: 1 page pending for write, 3 pre-fetched sparse
|
||||
* pages, 3 pre-fetched dependent pages.
|
||||
*
|
||||
* Of course if the caller decides to retain some batches to perform parallel processing,
|
||||
* then we might load all pages in memory, which is also considered as a bug in caller's
|
||||
* code. So if the algo supports external memory, it must be careful that queue for async
|
||||
* code. So if the algo supports external memory, it must be careful that queue for async
|
||||
* call must have an upper limit.
|
||||
*
|
||||
* Another assumption we make is that the data must be immutable so caller should never
|
||||
@@ -101,7 +96,7 @@ class SparsePageDMatrix : public DMatrix {
|
||||
MetaInfo &Info() override;
|
||||
const MetaInfo &Info() const override;
|
||||
Context const *Ctx() const override { return &fmat_ctx_; }
|
||||
|
||||
// The only DMatrix implementation that returns false.
|
||||
bool SingleColBlock() const override { return false; }
|
||||
DMatrix *Slice(common::Span<int32_t const>) override {
|
||||
LOG(FATAL) << "Slicing DMatrix is not supported for external memory.";
|
||||
@@ -153,6 +148,5 @@ inline std::string MakeCache(SparsePageDMatrix *ptr, std::string format, std::st
|
||||
}
|
||||
return id;
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_SPARSE_PAGE_DMATRIX_H_
|
||||
|
||||
@@ -1,59 +1,57 @@
|
||||
/*!
|
||||
* Copyright (c) 2015-2021 by Contributors
|
||||
/**
|
||||
* Copyright 2015-2023, XGBoost Contributors
|
||||
* \file sparse_page_raw_format.cc
|
||||
* Raw binary format of sparse page.
|
||||
*/
|
||||
#include <xgboost/data.h>
|
||||
#include <dmlc/registry.h>
|
||||
|
||||
#include "xgboost/logging.h"
|
||||
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
|
||||
#include "../common/ref_resource_view.h" // for WriteVec
|
||||
#include "./sparse_page_writer.h"
|
||||
#include "xgboost/data.h"
|
||||
#include "xgboost/logging.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
|
||||
namespace xgboost::data {
|
||||
DMLC_REGISTRY_FILE_TAG(sparse_page_raw_format);
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class SparsePageRawFormat : public SparsePageFormat<T> {
|
||||
public:
|
||||
bool Read(T* page, dmlc::SeekStream* fi) override {
|
||||
bool Read(T* page, common::AlignedResourceReadStream* fi) override {
|
||||
auto& offset_vec = page->offset.HostVector();
|
||||
if (!fi->Read(&offset_vec)) {
|
||||
if (!common::ReadVec(fi, &offset_vec)) {
|
||||
return false;
|
||||
}
|
||||
auto& data_vec = page->data.HostVector();
|
||||
CHECK_NE(page->offset.Size(), 0U) << "Invalid SparsePage file";
|
||||
data_vec.resize(offset_vec.back());
|
||||
if (page->data.Size() != 0) {
|
||||
size_t n_bytes = fi->Read(dmlc::BeginPtr(data_vec),
|
||||
(page->data).Size() * sizeof(Entry));
|
||||
CHECK_EQ(n_bytes, (page->data).Size() * sizeof(Entry))
|
||||
<< "Invalid SparsePage file";
|
||||
if (!common::ReadVec(fi, &data_vec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!fi->Read(&page->base_rowid, sizeof(page->base_rowid))) {
|
||||
return false;
|
||||
}
|
||||
fi->Read(&page->base_rowid, sizeof(page->base_rowid));
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Write(const T& page, dmlc::Stream* fo) override {
|
||||
std::size_t Write(const T& page, common::AlignedFileWriteStream* fo) override {
|
||||
const auto& offset_vec = page.offset.HostVector();
|
||||
const auto& data_vec = page.data.HostVector();
|
||||
CHECK(page.offset.Size() != 0 && offset_vec[0] == 0);
|
||||
CHECK_EQ(offset_vec.back(), page.data.Size());
|
||||
fo->Write(offset_vec);
|
||||
auto bytes = page.MemCostBytes();
|
||||
bytes += sizeof(uint64_t);
|
||||
|
||||
std::size_t bytes{0};
|
||||
bytes += common::WriteVec(fo, offset_vec);
|
||||
if (page.data.Size() != 0) {
|
||||
fo->Write(dmlc::BeginPtr(data_vec), page.data.Size() * sizeof(Entry));
|
||||
bytes += common::WriteVec(fo, data_vec);
|
||||
}
|
||||
fo->Write(&page.base_rowid, sizeof(page.base_rowid));
|
||||
bytes += sizeof(page.base_rowid);
|
||||
bytes += fo->Write(&page.base_rowid, sizeof(page.base_rowid));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
/*! \brief external memory column offset */
|
||||
std::vector<size_t> disk_offset_;
|
||||
};
|
||||
|
||||
XGBOOST_REGISTER_SPARSE_PAGE_FORMAT(raw)
|
||||
@@ -74,5 +72,4 @@ XGBOOST_REGISTER_SORTED_CSC_PAGE_FORMAT(raw)
|
||||
return new SparsePageRawFormat<SortedCSCPage>();
|
||||
});
|
||||
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
/*!
|
||||
* Copyright 2021 XGBoost contributors
|
||||
/**
|
||||
* Copyright 2021-2023, XGBoost contributors
|
||||
*/
|
||||
#include "../common/device_helpers.cuh" // for CurrentDevice
|
||||
#include "proxy_dmatrix.cuh" // for Dispatch, DMatrixProxy
|
||||
#include "simple_dmatrix.cuh" // for CopyToSparsePage
|
||||
#include "sparse_page_source.h"
|
||||
#include "proxy_dmatrix.cuh"
|
||||
#include "simple_dmatrix.cuh"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
#include "xgboost/data.h" // for SparsePage
|
||||
|
||||
namespace xgboost::data {
|
||||
namespace detail {
|
||||
std::size_t NSamplesDevice(DMatrixProxy *proxy) {
|
||||
return Dispatch(proxy, [](auto const &value) { return value.NumRows(); });
|
||||
return cuda_impl::Dispatch(proxy, [](auto const &value) { return value.NumRows(); });
|
||||
}
|
||||
|
||||
std::size_t NFeaturesDevice(DMatrixProxy *proxy) {
|
||||
return Dispatch(proxy, [](auto const &value) { return value.NumCols(); });
|
||||
return cuda_impl::Dispatch(proxy, [](auto const &value) { return value.NumCols(); });
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
void DevicePush(DMatrixProxy* proxy, float missing, SparsePage* page) {
|
||||
void DevicePush(DMatrixProxy *proxy, float missing, SparsePage *page) {
|
||||
auto device = proxy->DeviceIdx();
|
||||
if (device < 0) {
|
||||
device = dh::CurrentDevice();
|
||||
}
|
||||
CHECK_GE(device, 0);
|
||||
|
||||
Dispatch(proxy, [&](auto const &value) {
|
||||
CopyToSparsePage(value, device, missing, page);
|
||||
});
|
||||
cuda_impl::Dispatch(proxy,
|
||||
[&](auto const &value) { CopyToSparsePage(value, device, missing, page); });
|
||||
}
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
|
||||
@@ -1,45 +1,49 @@
|
||||
/*!
|
||||
* Copyright 2014-2022 by XGBoost Contributors
|
||||
/**
|
||||
* Copyright 2014-2023, XGBoost Contributors
|
||||
* \file sparse_page_source.h
|
||||
*/
|
||||
#ifndef XGBOOST_DATA_SPARSE_PAGE_SOURCE_H_
|
||||
#define XGBOOST_DATA_SPARSE_PAGE_SOURCE_H_
|
||||
|
||||
#include <algorithm> // std::min
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <algorithm> // for min
|
||||
#include <atomic> // for atomic
|
||||
#include <future> // for async
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex> // for mutex
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility> // for pair, move
|
||||
#include <vector>
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/io.h" // for PrivateMmapConstStream
|
||||
#include "../common/timer.h" // for Monitor, Timer
|
||||
#include "adapter.h"
|
||||
#include "proxy_dmatrix.h" // for DMatrixProxy
|
||||
#include "sparse_page_writer.h" // for SparsePageFormat
|
||||
#include "xgboost/base.h"
|
||||
#include "xgboost/data.h"
|
||||
|
||||
#include "adapter.h"
|
||||
#include "sparse_page_writer.h"
|
||||
#include "proxy_dmatrix.h"
|
||||
|
||||
#include "../common/common.h"
|
||||
#include "../common/timer.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
namespace xgboost::data {
|
||||
inline void TryDeleteCacheFile(const std::string& file) {
|
||||
if (std::remove(file.c_str()) != 0) {
|
||||
// Don't throw, this is called in a destructor.
|
||||
LOG(WARNING) << "Couldn't remove external memory cache file " << file
|
||||
<< "; you may want to remove it manually";
|
||||
<< "; you may want to remove it manually";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Information about the cache including path and page offsets.
|
||||
*/
|
||||
struct Cache {
|
||||
// whether the write to the cache is complete
|
||||
bool written;
|
||||
std::string name;
|
||||
std::string format;
|
||||
// offset into binary cache file.
|
||||
std::vector<size_t> offset;
|
||||
std::vector<std::uint64_t> offset;
|
||||
|
||||
Cache(bool w, std::string n, std::string fmt)
|
||||
: written{w}, name{std::move(n)}, format{std::move(fmt)} {
|
||||
@@ -51,11 +55,24 @@ struct Cache {
|
||||
return name + format;
|
||||
}
|
||||
|
||||
std::string ShardName() {
|
||||
[[nodiscard]] std::string ShardName() const {
|
||||
return ShardName(this->name, this->format);
|
||||
}
|
||||
|
||||
// The write is completed.
|
||||
/**
|
||||
* @brief Record a page with size of n_bytes.
|
||||
*/
|
||||
void Push(std::size_t n_bytes) { offset.push_back(n_bytes); }
|
||||
/**
|
||||
* @brief Returns the view start and length for the i^th page.
|
||||
*/
|
||||
[[nodiscard]] auto View(std::size_t i) const {
|
||||
std::uint64_t off = offset.at(i);
|
||||
std::uint64_t len = offset.at(i + 1) - offset[i];
|
||||
return std::pair{off, len};
|
||||
}
|
||||
/**
|
||||
* @brief Call this once the write for the cache is complete.
|
||||
*/
|
||||
void Commit() {
|
||||
if (!written) {
|
||||
std::partial_sum(offset.begin(), offset.end(), offset.begin());
|
||||
@@ -64,7 +81,7 @@ struct Cache {
|
||||
}
|
||||
};
|
||||
|
||||
// Prevents multi-threaded call.
|
||||
// Prevents multi-threaded call to `GetBatches`.
|
||||
class TryLockGuard {
|
||||
std::mutex& lock_;
|
||||
|
||||
@@ -77,74 +94,128 @@ class TryLockGuard {
|
||||
}
|
||||
};
|
||||
|
||||
// Similar to `dmlc::OMPException`, but doesn't need the threads to be joined before rethrow
|
||||
class ExceHandler {
|
||||
std::mutex mutex_;
|
||||
std::atomic<bool> flag_{false};
|
||||
std::exception_ptr curr_exce_{nullptr};
|
||||
|
||||
public:
|
||||
template <typename Fn>
|
||||
decltype(auto) Run(Fn&& fn) noexcept(true) {
|
||||
try {
|
||||
return fn();
|
||||
} catch (dmlc::Error const& e) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
if (!curr_exce_) {
|
||||
curr_exce_ = std::current_exception();
|
||||
}
|
||||
flag_ = true;
|
||||
} catch (std::exception const& e) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
if (!curr_exce_) {
|
||||
curr_exce_ = std::current_exception();
|
||||
}
|
||||
flag_ = true;
|
||||
} catch (...) {
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
if (!curr_exce_) {
|
||||
curr_exce_ = std::current_exception();
|
||||
}
|
||||
flag_ = true;
|
||||
}
|
||||
return std::invoke_result_t<Fn>();
|
||||
}
|
||||
|
||||
void Rethrow() noexcept(false) {
|
||||
if (flag_) {
|
||||
CHECK(curr_exce_);
|
||||
std::rethrow_exception(curr_exce_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base class for all page sources. Handles fetching, writing, and iteration.
|
||||
*/
|
||||
template <typename S>
|
||||
class SparsePageSourceImpl : public BatchIteratorImpl<S> {
|
||||
protected:
|
||||
// Prevents calling this iterator from multiple places(or threads).
|
||||
std::mutex single_threaded_;
|
||||
|
||||
// The current page.
|
||||
std::shared_ptr<S> page_;
|
||||
|
||||
bool at_end_ {false};
|
||||
float missing_;
|
||||
int nthreads_;
|
||||
std::int32_t nthreads_;
|
||||
bst_feature_t n_features_;
|
||||
|
||||
uint32_t count_{0};
|
||||
|
||||
uint32_t n_batches_ {0};
|
||||
// Index to the current page.
|
||||
std::uint32_t count_{0};
|
||||
// Total number of batches.
|
||||
std::uint32_t n_batches_{0};
|
||||
|
||||
std::shared_ptr<Cache> cache_info_;
|
||||
std::unique_ptr<dmlc::Stream> fo_;
|
||||
|
||||
using Ring = std::vector<std::future<std::shared_ptr<S>>>;
|
||||
// A ring storing futures to data. Since the DMatrix iterator is forward only, so we
|
||||
// can pre-fetch data in a ring.
|
||||
std::unique_ptr<Ring> ring_{new Ring};
|
||||
// Catching exception in pre-fetch threads to prevent segfault. Not always work though,
|
||||
// OOM error can be delayed due to lazy commit. On the bright side, if mmap is used then
|
||||
// OOM error should be rare.
|
||||
ExceHandler exce_;
|
||||
common::Monitor monitor_;
|
||||
|
||||
bool ReadCache() {
|
||||
CHECK(!at_end_);
|
||||
if (!cache_info_->written) {
|
||||
return false;
|
||||
}
|
||||
if (fo_) {
|
||||
fo_.reset(); // flush the data to disk.
|
||||
if (ring_->empty()) {
|
||||
ring_->resize(n_batches_);
|
||||
}
|
||||
// An heuristic for number of pre-fetched batches. We can make it part of BatchParam
|
||||
// to let user adjust number of pre-fetched batches when needed.
|
||||
uint32_t constexpr kPreFetch = 4;
|
||||
uint32_t constexpr kPreFetch = 3;
|
||||
|
||||
size_t n_prefetch_batches = std::min(kPreFetch, n_batches_);
|
||||
CHECK_GT(n_prefetch_batches, 0) << "total batches:" << n_batches_;
|
||||
size_t fetch_it = count_;
|
||||
std::size_t fetch_it = count_;
|
||||
|
||||
for (size_t i = 0; i < n_prefetch_batches; ++i, ++fetch_it) {
|
||||
exce_.Rethrow();
|
||||
|
||||
for (std::size_t i = 0; i < n_prefetch_batches; ++i, ++fetch_it) {
|
||||
fetch_it %= n_batches_; // ring
|
||||
if (ring_->at(fetch_it).valid()) {
|
||||
continue;
|
||||
}
|
||||
auto const *self = this; // make sure it's const
|
||||
auto const* self = this; // make sure it's const
|
||||
CHECK_LT(fetch_it, cache_info_->offset.size());
|
||||
ring_->at(fetch_it) = std::async(std::launch::async, [fetch_it, self]() {
|
||||
common::Timer timer;
|
||||
timer.Start();
|
||||
std::unique_ptr<SparsePageFormat<S>> fmt{CreatePageFormat<S>("raw")};
|
||||
auto n = self->cache_info_->ShardName();
|
||||
size_t offset = self->cache_info_->offset.at(fetch_it);
|
||||
std::unique_ptr<dmlc::SeekStream> fi{dmlc::SeekStream::CreateForRead(n.c_str())};
|
||||
fi->Seek(offset);
|
||||
CHECK_EQ(fi->Tell(), offset);
|
||||
ring_->at(fetch_it) = std::async(std::launch::async, [fetch_it, self, this]() {
|
||||
auto page = std::make_shared<S>();
|
||||
CHECK(fmt->Read(page.get(), fi.get()));
|
||||
LOG(INFO) << "Read a page in " << timer.ElapsedSeconds() << " seconds.";
|
||||
this->exce_.Run([&] {
|
||||
std::unique_ptr<SparsePageFormat<S>> fmt{CreatePageFormat<S>("raw")};
|
||||
auto name = self->cache_info_->ShardName();
|
||||
auto [offset, length] = self->cache_info_->View(fetch_it);
|
||||
auto fi = std::make_unique<common::PrivateMmapConstStream>(name, offset, length);
|
||||
CHECK(fmt->Read(page.get(), fi.get()));
|
||||
});
|
||||
return page;
|
||||
});
|
||||
}
|
||||
|
||||
CHECK_EQ(std::count_if(ring_->cbegin(), ring_->cend(), [](auto const& f) { return f.valid(); }),
|
||||
n_prefetch_batches)
|
||||
<< "Sparse DMatrix assumes forward iteration.";
|
||||
|
||||
monitor_.Start("Wait");
|
||||
page_ = (*ring_)[count_].get();
|
||||
CHECK(!(*ring_)[count_].valid());
|
||||
monitor_.Stop("Wait");
|
||||
|
||||
exce_.Rethrow();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,29 +224,41 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
|
||||
common::Timer timer;
|
||||
timer.Start();
|
||||
std::unique_ptr<SparsePageFormat<S>> fmt{CreatePageFormat<S>("raw")};
|
||||
if (!fo_) {
|
||||
auto n = cache_info_->ShardName();
|
||||
fo_.reset(dmlc::Stream::Create(n.c_str(), "w"));
|
||||
}
|
||||
auto bytes = fmt->Write(*page_, fo_.get());
|
||||
timer.Stop();
|
||||
|
||||
auto name = cache_info_->ShardName();
|
||||
std::unique_ptr<common::AlignedFileWriteStream> fo;
|
||||
if (this->Iter() == 0) {
|
||||
fo = std::make_unique<common::AlignedFileWriteStream>(StringView{name}, "wb");
|
||||
} else {
|
||||
fo = std::make_unique<common::AlignedFileWriteStream>(StringView{name}, "ab");
|
||||
}
|
||||
|
||||
auto bytes = fmt->Write(*page_, fo.get());
|
||||
|
||||
timer.Stop();
|
||||
// Not entirely accurate, the kernels doesn't have to flush the data.
|
||||
LOG(INFO) << static_cast<double>(bytes) / 1024.0 / 1024.0 << " MB written in "
|
||||
<< timer.ElapsedSeconds() << " seconds.";
|
||||
cache_info_->offset.push_back(bytes);
|
||||
cache_info_->Push(bytes);
|
||||
}
|
||||
|
||||
virtual void Fetch() = 0;
|
||||
|
||||
public:
|
||||
SparsePageSourceImpl(float missing, int nthreads, bst_feature_t n_features,
|
||||
uint32_t n_batches, std::shared_ptr<Cache> cache)
|
||||
: missing_{missing}, nthreads_{nthreads}, n_features_{n_features},
|
||||
n_batches_{n_batches}, cache_info_{std::move(cache)} {}
|
||||
SparsePageSourceImpl(float missing, int nthreads, bst_feature_t n_features, uint32_t n_batches,
|
||||
std::shared_ptr<Cache> cache)
|
||||
: missing_{missing},
|
||||
nthreads_{nthreads},
|
||||
n_features_{n_features},
|
||||
n_batches_{n_batches},
|
||||
cache_info_{std::move(cache)} {
|
||||
monitor_.Init(typeid(S).name()); // not pretty, but works for basic profiling
|
||||
}
|
||||
|
||||
SparsePageSourceImpl(SparsePageSourceImpl const &that) = delete;
|
||||
|
||||
~SparsePageSourceImpl() override {
|
||||
// Don't orphan the threads.
|
||||
for (auto& fu : *ring_) {
|
||||
if (fu.valid()) {
|
||||
fu.get();
|
||||
@@ -183,18 +266,18 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Iter() const { return count_; }
|
||||
[[nodiscard]] uint32_t Iter() const { return count_; }
|
||||
|
||||
const S &operator*() const override {
|
||||
CHECK(page_);
|
||||
return *page_;
|
||||
}
|
||||
|
||||
std::shared_ptr<S const> Page() const override {
|
||||
[[nodiscard]] std::shared_ptr<S const> Page() const override {
|
||||
return page_;
|
||||
}
|
||||
|
||||
bool AtEnd() const override {
|
||||
[[nodiscard]] bool AtEnd() const override {
|
||||
return at_end_;
|
||||
}
|
||||
|
||||
@@ -202,20 +285,23 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
|
||||
TryLockGuard guard{single_threaded_};
|
||||
at_end_ = false;
|
||||
count_ = 0;
|
||||
// Pre-fetch for the next round of iterations.
|
||||
this->Fetch();
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(XGBOOST_USE_CUDA) || defined(XGBOOST_USE_HIP)
|
||||
// Push data from CUDA.
|
||||
void DevicePush(DMatrixProxy* proxy, float missing, SparsePage* page);
|
||||
#else
|
||||
inline void DevicePush(DMatrixProxy*, float, SparsePage*) { common::AssertGPUSupport(); }
|
||||
#endif
|
||||
|
||||
class SparsePageSource : public SparsePageSourceImpl<SparsePage> {
|
||||
// This is the source from the user.
|
||||
DataIterProxy<DataIterResetCallback, XGDMatrixCallbackNext> iter_;
|
||||
DMatrixProxy* proxy_;
|
||||
size_t base_row_id_ {0};
|
||||
std::size_t base_row_id_{0};
|
||||
|
||||
void Fetch() final {
|
||||
page_ = std::make_shared<SparsePage>();
|
||||
@@ -244,7 +330,7 @@ class SparsePageSource : public SparsePageSourceImpl<SparsePage> {
|
||||
iter_{iter}, proxy_{proxy} {
|
||||
if (!cache_info_->written) {
|
||||
iter_.Reset();
|
||||
CHECK_EQ(iter_.Next(), 1) << "Must have at least 1 batch.";
|
||||
CHECK(iter_.Next()) << "Must have at least 1 batch.";
|
||||
}
|
||||
this->Fetch();
|
||||
}
|
||||
@@ -259,6 +345,7 @@ class SparsePageSource : public SparsePageSourceImpl<SparsePage> {
|
||||
}
|
||||
|
||||
if (at_end_) {
|
||||
CHECK_EQ(cache_info_->offset.size(), n_batches_ + 1);
|
||||
cache_info_->Commit();
|
||||
if (n_batches_ != 0) {
|
||||
CHECK_EQ(count_, n_batches_);
|
||||
@@ -371,6 +458,5 @@ class SortedCSCPageSource : public PageSourceIncMixIn<SortedCSCPage> {
|
||||
this->Fetch();
|
||||
}
|
||||
};
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_SPARSE_PAGE_SOURCE_H_
|
||||
|
||||
@@ -1,52 +1,44 @@
|
||||
/*!
|
||||
* Copyright (c) 2014-2019 by Contributors
|
||||
/**
|
||||
* Copyright 2014-2023, XGBoost Contributors
|
||||
* \file sparse_page_writer.h
|
||||
* \author Tianqi Chen
|
||||
*/
|
||||
#ifndef XGBOOST_DATA_SPARSE_PAGE_WRITER_H_
|
||||
#define XGBOOST_DATA_SPARSE_PAGE_WRITER_H_
|
||||
|
||||
#include <xgboost/data.h>
|
||||
#include <dmlc/io.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <functional> // for function
|
||||
#include <string> // for string
|
||||
|
||||
#if DMLC_ENABLE_STD_THREAD
|
||||
#include <dmlc/concurrency.h>
|
||||
#include <thread>
|
||||
#endif // DMLC_ENABLE_STD_THREAD
|
||||
|
||||
namespace xgboost {
|
||||
namespace data {
|
||||
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
|
||||
#include "dmlc/io.h" // for Stream
|
||||
#include "dmlc/registry.h" // for Registry, FunctionRegEntryBase
|
||||
#include "xgboost/data.h" // for SparsePage,CSCPage,SortedCSCPage,EllpackPage ...
|
||||
|
||||
namespace xgboost::data {
|
||||
template<typename T>
|
||||
struct SparsePageFormatReg;
|
||||
|
||||
/*!
|
||||
* \brief Format specification of SparsePage.
|
||||
/**
|
||||
* @brief Format specification of various data formats like SparsePage.
|
||||
*/
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
class SparsePageFormat {
|
||||
public:
|
||||
/*! \brief virtual destructor */
|
||||
virtual ~SparsePageFormat() = default;
|
||||
/*!
|
||||
* \brief Load all the segments into page, advance fi to end of the block.
|
||||
* \param page The data to read page into.
|
||||
* \param fi the input stream of the file
|
||||
* \return true of the loading as successful, false if end of file was reached
|
||||
/**
|
||||
* @brief Load all the segments into page, advance fi to end of the block.
|
||||
*
|
||||
* @param page The data to read page into.
|
||||
* @param fi the input stream of the file
|
||||
* @return true of the loading as successful, false if end of file was reached
|
||||
*/
|
||||
virtual bool Read(T* page, dmlc::SeekStream* fi) = 0;
|
||||
/*!
|
||||
* \brief save the data to fo, when a page was written.
|
||||
* \param fo output stream
|
||||
virtual bool Read(T* page, common::AlignedResourceReadStream* fi) = 0;
|
||||
/**
|
||||
* @brief save the data to fo, when a page was written.
|
||||
*
|
||||
* @param fo output stream
|
||||
*/
|
||||
virtual size_t Write(const T& page, dmlc::Stream* fo) = 0;
|
||||
virtual size_t Write(const T& page, common::AlignedFileWriteStream* fo) = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -105,6 +97,5 @@ struct SparsePageFormatReg
|
||||
DMLC_REGISTRY_REGISTER(SparsePageFormatReg<GHistIndexMatrix>, \
|
||||
GHistIndexPageFmt, Name)
|
||||
|
||||
} // namespace data
|
||||
} // namespace xgboost
|
||||
} // namespace xgboost::data
|
||||
#endif // XGBOOST_DATA_SPARSE_PAGE_WRITER_H_
|
||||
|
||||
Reference in New Issue
Block a user