[EM] Avoid writing cut matrix to cache. (#10444)

This commit is contained in:
Jiaming Yuan 2024-06-19 18:03:38 +08:00 committed by GitHub
parent 63418d2f35
commit e5f1720656
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 429 additions and 298 deletions

View File

@ -113,7 +113,10 @@ class MetaInfo {
MetaInfo Slice(common::Span<int32_t const> ridxs) const; MetaInfo Slice(common::Span<int32_t const> ridxs) const;
MetaInfo Copy() const; MetaInfo Copy() const;
/**
* @brief Whether the matrix is dense.
*/
bool IsDense() const { return num_col_ * num_row_ == num_nonzero_; }
/*! /*!
* \brief Get weight of each instances. * \brief Get weight of each instances.
* \param i Instance index. * \param i Instance index.
@ -538,10 +541,10 @@ class DMatrix {
/*! \brief virtual destructor */ /*! \brief virtual destructor */
virtual ~DMatrix(); virtual ~DMatrix();
/*! \brief Whether the matrix is dense. */ /**
[[nodiscard]] bool IsDense() const { * @brief Whether the matrix is dense.
return Info().num_nonzero_ == Info().num_row_ * Info().num_col_; */
} [[nodiscard]] bool IsDense() const { return this->Info().IsDense(); }
/** /**
* \brief Load DMatrix from URI. * \brief Load DMatrix from URI.

View File

@ -9,7 +9,6 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector>
#include "allreduce.h" #include "allreduce.h"
#include "broadcast.h" #include "broadcast.h"

View File

@ -162,6 +162,17 @@ class HistogramCuts {
} }
return vals[bin_idx - 1]; return vals[bin_idx - 1];
} }
void SetDevice(DeviceOrd d) const {
this->cut_ptrs_.SetDevice(d);
this->cut_ptrs_.ConstDevicePointer();
this->cut_values_.SetDevice(d);
this->cut_values_.ConstDevicePointer();
this->min_vals_.SetDevice(d);
this->min_vals_.ConstDevicePointer();
}
}; };
/** /**

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2019-2023, XGBoost contributors * Copyright 2019-2024, XGBoost contributors
*/ */
#ifndef XGBOOST_USE_CUDA #ifndef XGBOOST_USE_CUDA
@ -7,15 +7,17 @@
#include <xgboost/data.h> #include <xgboost/data.h>
#include <memory> // for shared_ptr
// dummy implementation of EllpackPage in case CUDA is not used // dummy implementation of EllpackPage in case CUDA is not used
namespace xgboost { namespace xgboost {
class EllpackPageImpl { class EllpackPageImpl {
common::HistogramCuts cuts_; std::shared_ptr<common::HistogramCuts> cuts_;
public: public:
[[nodiscard]] common::HistogramCuts& Cuts() { return cuts_; } [[nodiscard]] common::HistogramCuts const& Cuts() const { return *cuts_; }
[[nodiscard]] common::HistogramCuts const& Cuts() const { return cuts_; } [[nodiscard]] std::shared_ptr<common::HistogramCuts const> CutsShared() const { return cuts_; }
}; };
EllpackPage::EllpackPage() = default; EllpackPage::EllpackPage() = default;
@ -40,12 +42,6 @@ size_t EllpackPage::Size() const {
return 0; 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 { [[nodiscard]] common::HistogramCuts const& EllpackPage::Cuts() const {
LOG(FATAL) << "Internal Error: XGBoost is not compiled with CUDA but " LOG(FATAL) << "Internal Error: XGBoost is not compiled with CUDA but "
"EllpackPage is required"; "EllpackPage is required";

View File

@ -12,8 +12,8 @@
#include "../common/cuda_context.cuh" #include "../common/cuda_context.cuh"
#include "../common/hist_util.cuh" #include "../common/hist_util.cuh"
#include "../common/transform_iterator.h" // MakeIndexTransformIter #include "../common/transform_iterator.h" // MakeIndexTransformIter
#include "./ellpack_page.cuh" #include "device_adapter.cuh" // for NoInfInData
#include "device_adapter.cuh" // for NoInfInData #include "ellpack_page.cuh"
#include "ellpack_page.h" #include "ellpack_page.h"
#include "gradient_index.h" #include "gradient_index.h"
#include "xgboost/data.h" #include "xgboost/data.h"
@ -33,11 +33,6 @@ size_t EllpackPage::Size() const { return impl_->Size(); }
void EllpackPage::SetBaseRowId(std::size_t row_id) { impl_->SetBaseRowId(row_id); } 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 { [[nodiscard]] common::HistogramCuts const& EllpackPage::Cuts() const {
CHECK(impl_); CHECK(impl_);
return impl_->Cuts(); return impl_->Cuts();
@ -94,7 +89,8 @@ __global__ void CompressBinEllpackKernel(
} }
// Construct an ELLPACK matrix with the given number of empty rows. // Construct an ELLPACK matrix with the given number of empty rows.
EllpackPageImpl::EllpackPageImpl(DeviceOrd device, common::HistogramCuts cuts, bool is_dense, EllpackPageImpl::EllpackPageImpl(DeviceOrd device,
std::shared_ptr<common::HistogramCuts const> cuts, bool is_dense,
size_t row_stride, size_t n_rows) size_t row_stride, size_t n_rows)
: is_dense(is_dense), cuts_(std::move(cuts)), row_stride(row_stride), n_rows(n_rows) { : is_dense(is_dense), cuts_(std::move(cuts)), row_stride(row_stride), n_rows(n_rows) {
monitor_.Init("ellpack_page"); monitor_.Init("ellpack_page");
@ -105,12 +101,11 @@ EllpackPageImpl::EllpackPageImpl(DeviceOrd device, common::HistogramCuts cuts, b
monitor_.Stop("InitCompressedData"); monitor_.Stop("InitCompressedData");
} }
EllpackPageImpl::EllpackPageImpl(DeviceOrd device, common::HistogramCuts cuts, EllpackPageImpl::EllpackPageImpl(DeviceOrd device,
const SparsePage &page, bool is_dense, std::shared_ptr<common::HistogramCuts const> cuts,
size_t row_stride, const SparsePage& page, bool is_dense, size_t row_stride,
common::Span<FeatureType const> feature_types) common::Span<FeatureType const> feature_types)
: cuts_(std::move(cuts)), is_dense(is_dense), n_rows(page.Size()), : cuts_(std::move(cuts)), is_dense(is_dense), n_rows(page.Size()), row_stride(row_stride) {
row_stride(row_stride) {
this->InitCompressedData(device); this->InitCompressedData(device);
this->CreateHistIndices(device, page, feature_types); this->CreateHistIndices(device, page, feature_types);
} }
@ -127,9 +122,10 @@ EllpackPageImpl::EllpackPageImpl(Context const* ctx, DMatrix* dmat, const BatchP
// Create the quantile sketches for the dmatrix and initialize HistogramCuts. // Create the quantile sketches for the dmatrix and initialize HistogramCuts.
row_stride = GetRowStride(dmat); row_stride = GetRowStride(dmat);
if (!param.hess.empty()) { if (!param.hess.empty()) {
cuts_ = common::DeviceSketchWithHessian(ctx, dmat, param.max_bin, param.hess); cuts_ = std::make_shared<common::HistogramCuts>(
common::DeviceSketchWithHessian(ctx, dmat, param.max_bin, param.hess));
} else { } else {
cuts_ = common::DeviceSketch(ctx, dmat, param.max_bin); cuts_ = std::make_shared<common::HistogramCuts>(common::DeviceSketch(ctx, dmat, param.max_bin));
} }
monitor_.Stop("Quantiles"); monitor_.Stop("Quantiles");
@ -297,7 +293,7 @@ template <typename AdapterBatch>
EllpackPageImpl::EllpackPageImpl(AdapterBatch batch, float missing, DeviceOrd device, bool is_dense, EllpackPageImpl::EllpackPageImpl(AdapterBatch batch, float missing, DeviceOrd device, bool is_dense,
common::Span<size_t> row_counts_span, common::Span<size_t> row_counts_span,
common::Span<FeatureType const> feature_types, size_t row_stride, common::Span<FeatureType const> feature_types, size_t row_stride,
size_t n_rows, common::HistogramCuts const& cuts) { size_t n_rows, std::shared_ptr<common::HistogramCuts const> cuts) {
dh::safe_cuda(cudaSetDevice(device.ordinal)); dh::safe_cuda(cudaSetDevice(device.ordinal));
*this = EllpackPageImpl(device, cuts, is_dense, row_stride, n_rows); *this = EllpackPageImpl(device, cuts, is_dense, row_stride, n_rows);
@ -309,7 +305,7 @@ EllpackPageImpl::EllpackPageImpl(AdapterBatch batch, float missing, DeviceOrd de
template EllpackPageImpl::EllpackPageImpl( \ template EllpackPageImpl::EllpackPageImpl( \
__BATCH_T batch, float missing, DeviceOrd device, bool is_dense, \ __BATCH_T batch, float missing, DeviceOrd device, bool is_dense, \
common::Span<size_t> row_counts_span, common::Span<FeatureType const> feature_types, \ common::Span<size_t> row_counts_span, common::Span<FeatureType const> feature_types, \
size_t row_stride, size_t n_rows, common::HistogramCuts const& cuts); size_t row_stride, size_t n_rows, std::shared_ptr<common::HistogramCuts const> cuts);
ELLPACK_BATCH_SPECIALIZE(data::CudfAdapterBatch) ELLPACK_BATCH_SPECIALIZE(data::CudfAdapterBatch)
ELLPACK_BATCH_SPECIALIZE(data::CupyAdapterBatch) ELLPACK_BATCH_SPECIALIZE(data::CupyAdapterBatch)
@ -359,7 +355,11 @@ void CopyGHistToEllpack(GHistIndexMatrix const& page, common::Span<size_t const>
EllpackPageImpl::EllpackPageImpl(Context const* ctx, GHistIndexMatrix const& page, EllpackPageImpl::EllpackPageImpl(Context const* ctx, GHistIndexMatrix const& page,
common::Span<FeatureType const> ft) common::Span<FeatureType const> ft)
: is_dense{page.IsDense()}, base_rowid{page.base_rowid}, n_rows{page.Size()}, cuts_{page.cut} { : is_dense{page.IsDense()},
base_rowid{page.base_rowid},
n_rows{page.Size()},
// This makes a copy of the cut values.
cuts_{std::make_shared<common::HistogramCuts>(page.cut)} {
auto it = common::MakeIndexTransformIter( auto it = common::MakeIndexTransformIter(
[&](size_t i) { return page.row_ptr[i + 1] - page.row_ptr[i]; }); [&](size_t i) { return page.row_ptr[i + 1] - page.row_ptr[i]; });
row_stride = *std::max_element(it, it + page.Size()); row_stride = *std::max_element(it, it + page.Size());

View File

@ -23,20 +23,20 @@ struct EllpackDeviceAccessor {
bool is_dense; bool is_dense;
/*! \brief Row length for ELLPACK, equal to number of features. */ /*! \brief Row length for ELLPACK, equal to number of features. */
size_t row_stride; size_t row_stride;
size_t base_rowid{}; bst_idx_t base_rowid{0};
size_t n_rows{}; bst_idx_t n_rows{0};
common::CompressedIterator<uint32_t> gidx_iter; common::CompressedIterator<std::uint32_t> gidx_iter;
/*! \brief Minimum value for each feature. Size equals to number of features. */ /*! \brief Minimum value for each feature. Size equals to number of features. */
common::Span<const bst_float> min_fvalue; common::Span<const float> min_fvalue;
/*! \brief Histogram cut pointers. Size equals to (number of features + 1). */ /*! \brief Histogram cut pointers. Size equals to (number of features + 1). */
common::Span<const uint32_t> feature_segments; common::Span<const std::uint32_t> feature_segments;
/*! \brief Histogram cut values. Size equals to (bins per feature * number of features). */ /*! \brief Histogram cut values. Size equals to (bins per feature * number of features). */
common::Span<const bst_float> gidx_fvalue_map; common::Span<const float> gidx_fvalue_map;
common::Span<const FeatureType> feature_types; common::Span<const FeatureType> feature_types;
EllpackDeviceAccessor(DeviceOrd device, const common::HistogramCuts& cuts, bool is_dense, EllpackDeviceAccessor(DeviceOrd device, std::shared_ptr<const common::HistogramCuts> cuts,
size_t row_stride, size_t base_rowid, size_t n_rows, bool is_dense, size_t row_stride, size_t base_rowid, size_t n_rows,
common::CompressedIterator<uint32_t> gidx_iter, common::CompressedIterator<uint32_t> gidx_iter,
common::Span<FeatureType const> feature_types) common::Span<FeatureType const> feature_types)
: is_dense(is_dense), : is_dense(is_dense),
@ -46,16 +46,16 @@ struct EllpackDeviceAccessor {
gidx_iter(gidx_iter), gidx_iter(gidx_iter),
feature_types{feature_types} { feature_types{feature_types} {
if (device.IsCPU()) { if (device.IsCPU()) {
gidx_fvalue_map = cuts.cut_values_.ConstHostSpan(); gidx_fvalue_map = cuts->cut_values_.ConstHostSpan();
feature_segments = cuts.cut_ptrs_.ConstHostSpan(); feature_segments = cuts->cut_ptrs_.ConstHostSpan();
min_fvalue = cuts.min_vals_.ConstHostSpan(); min_fvalue = cuts->min_vals_.ConstHostSpan();
} else { } else {
cuts.cut_values_.SetDevice(device); cuts->cut_values_.SetDevice(device);
cuts.cut_ptrs_.SetDevice(device); cuts->cut_ptrs_.SetDevice(device);
cuts.min_vals_.SetDevice(device); cuts->min_vals_.SetDevice(device);
gidx_fvalue_map = cuts.cut_values_.ConstDeviceSpan(); gidx_fvalue_map = cuts->cut_values_.ConstDeviceSpan();
feature_segments = cuts.cut_ptrs_.ConstDeviceSpan(); feature_segments = cuts->cut_ptrs_.ConstDeviceSpan();
min_fvalue = cuts.min_vals_.ConstDeviceSpan(); min_fvalue = cuts->min_vals_.ConstDeviceSpan();
} }
} }
// Get a matrix element, uses binary search for look up Return NaN if missing // Get a matrix element, uses binary search for look up Return NaN if missing
@ -142,13 +142,14 @@ class EllpackPageImpl {
* This is used in the sampling case. The ELLPACK page is constructed from an existing EllpackInfo * This is used in the sampling case. The ELLPACK page is constructed from an existing EllpackInfo
* and the given number of rows. * and the given number of rows.
*/ */
EllpackPageImpl(DeviceOrd device, common::HistogramCuts cuts, bool is_dense, size_t row_stride, EllpackPageImpl(DeviceOrd device, std::shared_ptr<common::HistogramCuts const> cuts,
size_t n_rows); bool is_dense, size_t row_stride, size_t n_rows);
/*! /*!
* \brief Constructor used for external memory. * \brief Constructor used for external memory.
*/ */
EllpackPageImpl(DeviceOrd device, common::HistogramCuts cuts, const SparsePage& page, EllpackPageImpl(DeviceOrd device, std::shared_ptr<common::HistogramCuts const> cuts,
bool is_dense, size_t row_stride, common::Span<FeatureType const> feature_types); const SparsePage& page, bool is_dense, size_t row_stride,
common::Span<FeatureType const> feature_types);
/*! /*!
* \brief Constructor from an existing DMatrix. * \brief Constructor from an existing DMatrix.
@ -162,7 +163,7 @@ class EllpackPageImpl {
explicit EllpackPageImpl(AdapterBatch batch, float missing, DeviceOrd device, bool is_dense, explicit EllpackPageImpl(AdapterBatch batch, float missing, DeviceOrd device, bool is_dense,
common::Span<size_t> row_counts_span, common::Span<size_t> row_counts_span,
common::Span<FeatureType const> feature_types, size_t row_stride, common::Span<FeatureType const> feature_types, size_t row_stride,
size_t n_rows, common::HistogramCuts const& cuts); size_t n_rows, std::shared_ptr<common::HistogramCuts const> cuts);
/** /**
* \brief Constructor from an existing CPU gradient index. * \brief Constructor from an existing CPU gradient index.
*/ */
@ -194,8 +195,9 @@ class EllpackPageImpl {
base_rowid = row_id; base_rowid = row_id;
} }
[[nodiscard]] common::HistogramCuts& Cuts() { return cuts_; } [[nodiscard]] common::HistogramCuts const& Cuts() const { return *cuts_; }
[[nodiscard]] common::HistogramCuts const& Cuts() const { return cuts_; } [[nodiscard]] std::shared_ptr<common::HistogramCuts const> CutsShared() const { return cuts_; }
void SetCuts(std::shared_ptr<common::HistogramCuts const> cuts) { cuts_ = cuts; }
/*! \return Estimation of memory cost of this page. */ /*! \return Estimation of memory cost of this page. */
static size_t MemCostBytes(size_t num_rows, size_t row_stride, const common::HistogramCuts&cuts) ; static size_t MemCostBytes(size_t num_rows, size_t row_stride, const common::HistogramCuts&cuts) ;
@ -203,7 +205,7 @@ class EllpackPageImpl {
/*! \brief Return the total number of symbols (total number of bins plus 1 for /*! \brief Return the total number of symbols (total number of bins plus 1 for
* not found). */ * not found). */
[[nodiscard]] std::size_t NumSymbols() const { return cuts_.TotalBins() + 1; } [[nodiscard]] std::size_t NumSymbols() const { return cuts_->TotalBins() + 1; }
[[nodiscard]] EllpackDeviceAccessor GetDeviceAccessor( [[nodiscard]] EllpackDeviceAccessor GetDeviceAccessor(
DeviceOrd device, common::Span<FeatureType const> feature_types = {}) const; DeviceOrd device, common::Span<FeatureType const> feature_types = {}) const;
@ -225,19 +227,18 @@ class EllpackPageImpl {
*/ */
void InitCompressedData(DeviceOrd device); void InitCompressedData(DeviceOrd device);
public:
public:
/*! \brief Whether or not if the matrix is dense. */ /*! \brief Whether or not if the matrix is dense. */
bool is_dense; bool is_dense;
/*! \brief Row length for ELLPACK. */ /*! \brief Row length for ELLPACK. */
size_t row_stride; size_t row_stride;
size_t base_rowid{0}; bst_idx_t base_rowid{0};
size_t n_rows{}; bst_idx_t n_rows{};
/*! \brief global index of histogram, which is stored in ELLPACK format. */ /*! \brief global index of histogram, which is stored in ELLPACK format. */
HostDeviceVector<common::CompressedByteT> gidx_buffer; HostDeviceVector<common::CompressedByteT> gidx_buffer;
private: private:
common::HistogramCuts cuts_; std::shared_ptr<common::HistogramCuts const> cuts_;
common::Monitor monitor_; common::Monitor monitor_;
}; };

View File

@ -49,7 +49,6 @@ class EllpackPage {
[[nodiscard]] const EllpackPageImpl* Impl() const { return impl_.get(); } [[nodiscard]] const EllpackPageImpl* Impl() const { return impl_.get(); }
EllpackPageImpl* Impl() { return impl_.get(); } EllpackPageImpl* Impl() { return impl_.get(); }
[[nodiscard]] common::HistogramCuts& Cuts();
[[nodiscard]] common::HistogramCuts const& Cuts() const; [[nodiscard]] common::HistogramCuts const& Cuts() const;
private: private:

View File

@ -1,60 +1,78 @@
/** /**
* Copyright 2019-2023, XGBoost contributors * Copyright 2019-2024, XGBoost contributors
*/ */
#include <dmlc/registry.h> #include <dmlc/registry.h>
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <cstdint> // for uint64_t
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream #include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
#include "../common/ref_resource_view.h" // for ReadVec, WriteVec #include "../common/ref_resource_view.h" // for ReadVec, WriteVec
#include "ellpack_page.cuh" #include "ellpack_page.cuh" // for EllpackPage
#include "histogram_cut_format.h" // for ReadHistogramCuts, WriteHistogramCuts #include "ellpack_page_raw_format.h"
#include "sparse_page_writer.h" // for SparsePageFormat
namespace xgboost::data { namespace xgboost::data {
DMLC_REGISTRY_FILE_TAG(ellpack_page_raw_format); DMLC_REGISTRY_FILE_TAG(ellpack_page_raw_format);
class EllpackPageRawFormat : public SparsePageFormat<EllpackPage> { namespace {
public: template <typename T>
bool Read(EllpackPage* page, common::AlignedResourceReadStream* fi) override { [[nodiscard]] bool ReadDeviceVec(common::AlignedResourceReadStream* fi, HostDeviceVector<T>* vec) {
auto* impl = page->Impl(); std::uint64_t n{0};
if (!ReadHistogramCuts(&impl->Cuts(), fi)) { if (!fi->Read(&n)) {
return false; return false;
} }
if (!fi->Read(&impl->n_rows)) { if (n == 0) {
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;
}
dh::DefaultStream().Sync();
return true; return true;
} }
size_t Write(const EllpackPage& page, common::AlignedFileWriteStream* fo) override { auto expected_bytes = sizeof(T) * n;
std::size_t bytes{0};
auto* impl = page.Impl();
bytes += WriteHistogramCuts(impl->Cuts(), fo);
bytes += fo->Write(impl->n_rows);
bytes += fo->Write(impl->is_dense);
bytes += fo->Write(impl->row_stride);
CHECK(!impl->gidx_buffer.ConstHostVector().empty());
bytes += common::WriteVec(fo, impl->gidx_buffer.HostVector());
bytes += fo->Write(impl->base_rowid);
return bytes;
}
};
XGBOOST_REGISTER_ELLPACK_PAGE_FORMAT(raw) auto [ptr, n_bytes] = fi->Consume(expected_bytes);
.describe("Raw ELLPACK binary data format.") if (n_bytes != expected_bytes) {
.set_body([]() { return new EllpackPageRawFormat(); }); return false;
}
vec->SetDevice(DeviceOrd::CUDA(0));
vec->Resize(n);
auto d_vec = vec->DeviceSpan();
dh::safe_cuda(
cudaMemcpyAsync(d_vec.data(), ptr, n_bytes, cudaMemcpyDefault, dh::DefaultStream()));
return true;
}
} // namespace
[[nodiscard]] bool EllpackPageRawFormat::Read(EllpackPage* page,
common::AlignedResourceReadStream* fi) {
auto* impl = page->Impl();
impl->SetCuts(this->cuts_);
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 (!ReadDeviceVec(fi, &impl->gidx_buffer)) {
return false;
}
if (!fi->Read(&impl->base_rowid)) {
return false;
}
return true;
}
[[nodiscard]] std::size_t EllpackPageRawFormat::Write(const EllpackPage& page,
common::AlignedFileWriteStream* fo) {
std::size_t bytes{0};
auto* impl = page.Impl();
bytes += fo->Write(impl->n_rows);
bytes += fo->Write(impl->is_dense);
bytes += fo->Write(impl->row_stride);
CHECK(!impl->gidx_buffer.ConstHostVector().empty());
bytes += common::WriteVec(fo, impl->gidx_buffer.HostVector());
bytes += fo->Write(impl->base_rowid);
return bytes;
}
} // namespace xgboost::data } // namespace xgboost::data

View File

@ -0,0 +1,46 @@
/**
* Copyright 2019-2024, XGBoost contributors
*/
#pragma once
#include <cstddef> // for size_t
#include <memory> // for shared_ptr
#include <utility> // for move
#include "../common/io.h" // for AlignedResourceReadStream
#include "sparse_page_writer.h" // for SparsePageFormat
#include "xgboost/data.h" // for EllpackPage
#if !defined(XGBOOST_USE_CUDA)
#include "../common/common.h" // for AssertGPUSupport
#endif // !defined(XGBOOST_USE_CUDA)`
namespace xgboost::common {
class HistogramCuts;
}
namespace xgboost::data {
class EllpackPageRawFormat : public SparsePageFormat<EllpackPage> {
std::shared_ptr<common::HistogramCuts const> cuts_;
public:
explicit EllpackPageRawFormat(std::shared_ptr<common::HistogramCuts const> cuts)
: cuts_{std::move(cuts)} {}
[[nodiscard]] bool Read(EllpackPage* page, common::AlignedResourceReadStream* fi) override;
[[nodiscard]] std::size_t Write(const EllpackPage& page,
common::AlignedFileWriteStream* fo) override;
};
#if !defined(XGBOOST_USE_CUDA)
inline bool EllpackPageRawFormat::Read(EllpackPage*, common::AlignedResourceReadStream*) {
common::AssertGPUSupport();
return false;
}
inline std::size_t EllpackPageRawFormat::Write(const EllpackPage&,
common::AlignedFileWriteStream*) {
common::AssertGPUSupport();
return 0;
}
#endif // !defined(XGBOOST_USE_CUDA)
} // namespace xgboost::data

View File

@ -21,7 +21,7 @@ void EllpackPageSource::Fetch() {
auto const &csr = source_->Page(); auto const &csr = source_->Page();
this->page_.reset(new EllpackPage{}); this->page_.reset(new EllpackPage{});
auto *impl = this->page_->Impl(); auto *impl = this->page_->Impl();
*impl = EllpackPageImpl(device_, *cuts_, *csr, is_dense_, row_stride_, feature_types_); *impl = EllpackPageImpl(device_, cuts_, *csr, is_dense_, row_stride_, feature_types_);
page_->SetBaseRowId(csr->base_rowid); page_->SetBaseRowId(csr->base_rowid);
this->WriteCache(); this->WriteCache();
} }

View File

@ -1,35 +1,43 @@
/** /**
* Copyright 2019-2023, XGBoost Contributors * Copyright 2019-2024, XGBoost Contributors
*/ */
#ifndef XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_ #ifndef XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_
#define XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_ #define XGBOOST_DATA_ELLPACK_PAGE_SOURCE_H_
#include <xgboost/data.h> #include <cstdint> // for int32_t
#include <memory> // for shared_ptr
#include <utility> // for move
#include <memory> #include "../common/hist_util.h" // for HistogramCuts
#include <string> #include "ellpack_page.h" // for EllpackPage
#include <utility> #include "ellpack_page_raw_format.h" // for EllpackPageRawFormat
#include "sparse_page_source.h" // for PageSourceIncMixIn
#include "../common/common.h" #include "xgboost/base.h" // for bst_idx_t
#include "../common/hist_util.h" #include "xgboost/context.h" // for DeviceOrd
#include "ellpack_page.h" // for EllpackPage #include "xgboost/data.h" // for BatchParam
#include "sparse_page_source.h" #include "xgboost/span.h" // for Span
namespace xgboost::data { namespace xgboost::data {
class EllpackPageSource : public PageSourceIncMixIn<EllpackPage> { class EllpackPageSource : public PageSourceIncMixIn<EllpackPage> {
bool is_dense_; bool is_dense_;
size_t row_stride_; bst_idx_t row_stride_;
BatchParam param_; BatchParam param_;
common::Span<FeatureType const> feature_types_; common::Span<FeatureType const> feature_types_;
std::unique_ptr<common::HistogramCuts> cuts_; std::shared_ptr<common::HistogramCuts const> cuts_;
DeviceOrd device_; DeviceOrd device_;
protected:
[[nodiscard]] SparsePageFormat<EllpackPage>* CreatePageFormat() const override {
cuts_->SetDevice(this->device_);
return new EllpackPageRawFormat{cuts_};
}
public: public:
EllpackPageSource(float missing, int nthreads, bst_feature_t n_features, size_t n_batches, EllpackPageSource(float missing, std::int32_t nthreads, bst_feature_t n_features,
std::shared_ptr<Cache> cache, BatchParam param, size_t n_batches, std::shared_ptr<Cache> cache, BatchParam param,
std::unique_ptr<common::HistogramCuts> cuts, bool is_dense, size_t row_stride, std::shared_ptr<common::HistogramCuts const> cuts, bool is_dense,
common::Span<FeatureType const> feature_types, bst_idx_t row_stride, common::Span<FeatureType const> feature_types,
std::shared_ptr<SparsePageSource> source, DeviceOrd device) std::shared_ptr<SparsePageSource> source, DeviceOrd device)
: PageSourceIncMixIn(missing, nthreads, n_features, n_batches, cache, false), : PageSourceIncMixIn(missing, nthreads, n_features, n_batches, cache, false),
is_dense_{is_dense}, is_dense_{is_dense},

View File

@ -1,106 +1,97 @@
/** /**
* Copyright 2021-2024 XGBoost contributors * Copyright 2021-2024, XGBoost contributors
*/ */
#include <cstddef> // for size_t #include "gradient_index_format.h"
#include <cstdint> // for uint8_t
#include <type_traits> // for underlying_type_t
#include <vector> // for vector
#include <cstddef> // for size_t
#include <cstdint> // for uint8_t
#include <type_traits> // for underlying_type_t
#include <vector> // for vector
#include "../common/hist_util.h" // for HistogramCuts
#include "../common/io.h" // for AlignedResourceReadStream #include "../common/io.h" // for AlignedResourceReadStream
#include "../common/ref_resource_view.h" // for ReadVec, WriteVec #include "../common/ref_resource_view.h" // for ReadVec, WriteVec
#include "gradient_index.h" // for GHistIndexMatrix #include "gradient_index.h" // for GHistIndexMatrix
#include "histogram_cut_format.h" // for ReadHistogramCuts
#include "sparse_page_writer.h" // for SparsePageFormat
namespace xgboost::data { namespace xgboost::data {
class GHistIndexRawFormat : public SparsePageFormat<GHistIndexMatrix> { [[nodiscard]] bool GHistIndexRawFormat::Read(GHistIndexMatrix* page,
public: common::AlignedResourceReadStream* fi) {
bool Read(GHistIndexMatrix* page, common::AlignedResourceReadStream* fi) override { CHECK(fi);
CHECK(fi);
if (!ReadHistogramCuts(&page->cut, fi)) { page->Cuts() = this->cuts_;
return false; // indptr
} if (!common::ReadVec(fi, &page->row_ptr)) {
return false;
// indptr
if (!common::ReadVec(fi, &page->row_ptr)) {
return false;
}
// 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);
// - index buffer
if (!common::ReadVec(fi, &page->data)) {
return false;
}
// - index
page->index =
common::Index{common::Span{page->data.data(), static_cast<size_t>(page->data.size())},
size_type};
// hit count
if (!common::ReadVec(fi, &page->hit_count)) {
return false;
}
if (!fi->Read(&page->max_numeric_bins_per_feat)) {
return false;
}
if (!fi->Read(&page->base_rowid)) {
return false;
}
bool is_dense = false;
if (!fi->Read(&is_dense)) {
return false;
}
page->SetDense(is_dense);
if (is_dense) {
page->index.SetBinOffset(page->cut.Ptrs());
}
if (!page->ReadColumnPage(fi)) {
return false;
}
return true;
} }
std::size_t Write(GHistIndexMatrix const& page, common::AlignedFileWriteStream* fo) override { // data
std::size_t bytes = 0; // - bin type
bytes += WriteHistogramCuts(page.cut, fo); // Old gcc doesn't support reading from enum.
// indptr std::underlying_type_t<common::BinTypeSize> uint_bin_type{0};
bytes += common::WriteVec(fo, page.row_ptr); if (!fi->Read(&uint_bin_type)) {
return false;
// data
// - 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
bytes += common::WriteVec(fo, page.hit_count);
// max_bins, base row, is_dense
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;
} }
}; 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(), static_cast<size_t>(page->data.size())}, size_type};
// hit count
if (!common::ReadVec(fi, &page->hit_count)) {
return false;
}
if (!fi->Read(&page->max_numeric_bins_per_feat)) {
return false;
}
if (!fi->Read(&page->base_rowid)) {
return false;
}
bool is_dense = false;
if (!fi->Read(&is_dense)) {
return false;
}
page->SetDense(is_dense);
if (is_dense) {
page->index.SetBinOffset(page->cut.Ptrs());
}
if (!page->ReadColumnPage(fi)) {
return false;
}
return true;
}
[[nodiscard]] std::size_t GHistIndexRawFormat::Write(GHistIndexMatrix const& page,
common::AlignedFileWriteStream* fo) {
std::size_t bytes = 0;
// indptr
bytes += common::WriteVec(fo, page.row_ptr);
// data
// - 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
bytes += common::WriteVec(fo, page.hit_count);
// max_bins, base row, is_dense
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;
}
DMLC_REGISTRY_FILE_TAG(gradient_index_format); 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 xgboost::data } // namespace xgboost::data

View File

@ -0,0 +1,29 @@
/**
* Copyright 2021-2024, XGBoost contributors
*/
#pragma once
#include <cstddef> // for size_t
#include <utility> // for move
#include "../common/hist_util.h" // for HistogramCuts
#include "../common/io.h" // for AlignedFileWriteStream
#include "gradient_index.h" // for GHistIndexMatrix
#include "sparse_page_writer.h" // for SparsePageFormat
namespace xgboost::common {
class HistogramCuts;
}
namespace xgboost::data {
class GHistIndexRawFormat : public SparsePageFormat<GHistIndexMatrix> {
common::HistogramCuts cuts_;
public:
[[nodiscard]] bool Read(GHistIndexMatrix* page, common::AlignedResourceReadStream* fi) override;
[[nodiscard]] std::size_t Write(GHistIndexMatrix const& page,
common::AlignedFileWriteStream* fo) override;
explicit GHistIndexRawFormat(common::HistogramCuts cuts) : cuts_{std::move(cuts)} {}
};
} // namespace xgboost::data

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2021-2023, XGBoost Contributors * Copyright 2021-2024, XGBoost Contributors
*/ */
#include "gradient_index_page_source.h" #include "gradient_index_page_source.h"

View File

@ -1,27 +1,39 @@
/*! /**
* Copyright 2021-2022 by XGBoost Contributors * Copyright 2021-2024, XGBoost Contributors
*/ */
#ifndef XGBOOST_DATA_GRADIENT_INDEX_PAGE_SOURCE_H_ #ifndef XGBOOST_DATA_GRADIENT_INDEX_PAGE_SOURCE_H_
#define XGBOOST_DATA_GRADIENT_INDEX_PAGE_SOURCE_H_ #define XGBOOST_DATA_GRADIENT_INDEX_PAGE_SOURCE_H_
#include <memory> #include <cmath> // for isnan
#include <utility> #include <cstdint> // for int32_t
#include <memory> // for shared_ptr
#include <utility> // for move
#include "gradient_index.h" #include "../common/hist_util.h" // for HistogramCuts
#include "sparse_page_source.h" #include "gradient_index.h" // for GHistIndexMatrix
#include "gradient_index_format.h" // for GHistIndexRawFormat
#include "sparse_page_source.h" // for PageSourceIncMixIn
#include "xgboost/base.h" // for bst_feature_t
#include "xgboost/data.h" // for BatchParam, FeatureType
#include "xgboost/span.h" // for Span
namespace xgboost { namespace xgboost {
namespace data { namespace data {
class GradientIndexPageSource : public PageSourceIncMixIn<GHistIndexMatrix> { class GradientIndexPageSource : public PageSourceIncMixIn<GHistIndexMatrix> {
common::HistogramCuts cuts_; common::HistogramCuts cuts_;
bool is_dense_; bool is_dense_;
int32_t max_bin_per_feat_; std::int32_t max_bin_per_feat_;
common::Span<FeatureType const> feature_types_; common::Span<FeatureType const> feature_types_;
double sparse_thresh_; double sparse_thresh_;
protected:
[[nodiscard]] SparsePageFormat<GHistIndexMatrix>* CreatePageFormat() const override {
return new GHistIndexRawFormat{cuts_};
}
public: public:
GradientIndexPageSource(float missing, int nthreads, bst_feature_t n_features, size_t n_batches, GradientIndexPageSource(float missing, std::int32_t nthreads, bst_feature_t n_features,
std::shared_ptr<Cache> cache, BatchParam param, size_t n_batches, std::shared_ptr<Cache> cache, BatchParam param,
common::HistogramCuts cuts, bool is_dense, common::HistogramCuts cuts, bool is_dense,
common::Span<FeatureType const> feature_types, common::Span<FeatureType const> feature_types,
std::shared_ptr<SparsePageSource> source) std::shared_ptr<SparsePageSource> source)

View File

@ -54,7 +54,7 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
/** /**
* Generate quantiles * Generate quantiles
*/ */
common::HistogramCuts cuts; auto cuts = std::make_shared<common::HistogramCuts>();
do { do {
// We use do while here as the first batch is fetched in ctor // We use do while here as the first batch is fetched in ctor
CHECK_LT(ctx->Ordinal(), common::AllVisibleGPUs()); CHECK_LT(ctx->Ordinal(), common::AllVisibleGPUs());
@ -104,9 +104,9 @@ void IterativeDMatrix::InitFromCUDA(Context const* ctx, BatchParam const& p,
sketch_containers.clear(); sketch_containers.clear();
sketch_containers.shrink_to_fit(); sketch_containers.shrink_to_fit();
final_sketch.MakeCuts(ctx, &cuts, this->info_.IsColumnSplit()); final_sketch.MakeCuts(ctx, cuts.get(), this->info_.IsColumnSplit());
} else { } else {
GetCutsFromRef(ctx, ref, Info().num_col_, p, &cuts); GetCutsFromRef(ctx, ref, Info().num_col_, p, cuts.get());
} }
this->info_.num_row_ = accumulated_rows; this->info_.num_row_ = accumulated_rows;

View File

@ -1,7 +1,7 @@
/** /**
* Copyright 2021-2024, XGBoost contributors * Copyright 2021-2024, XGBoost contributors
*/ */
#include <memory> // for unique_ptr #include <memory> // for shared_ptr
#include "../common/hist_util.cuh" #include "../common/hist_util.cuh"
#include "../common/hist_util.h" // for HistogramCuts #include "../common/hist_util.h" // for HistogramCuts
@ -26,13 +26,13 @@ BatchSet<EllpackPage> SparsePageDMatrix::GetEllpackBatches(Context const* ctx,
// reinitialize the cache // reinitialize the cache
cache_info_.erase(id); cache_info_.erase(id);
MakeCache(this, ".ellpack.page", cache_prefix_, &cache_info_); MakeCache(this, ".ellpack.page", cache_prefix_, &cache_info_);
std::unique_ptr<common::HistogramCuts> cuts; std::shared_ptr<common::HistogramCuts> cuts;
if (!param.hess.empty()) { if (!param.hess.empty()) {
cuts = std::make_unique<common::HistogramCuts>( cuts = std::make_shared<common::HistogramCuts>(
common::DeviceSketchWithHessian(ctx, this, param.max_bin, param.hess)); common::DeviceSketchWithHessian(ctx, this, param.max_bin, param.hess));
} else { } else {
cuts = cuts =
std::make_unique<common::HistogramCuts>(common::DeviceSketch(ctx, this, param.max_bin)); std::make_shared<common::HistogramCuts>(common::DeviceSketch(ctx, this, param.max_bin));
} }
this->InitializeSparsePage(ctx); // reset after use. this->InitializeSparsePage(ctx); // reset after use.

View File

@ -7,6 +7,7 @@
#include <algorithm> // for min #include <algorithm> // for min
#include <atomic> // for atomic #include <atomic> // for atomic
#include <cstdint> // for uint64_t
#include <cstdio> // for remove #include <cstdio> // for remove
#include <future> // for future #include <future> // for future
#include <memory> // for unique_ptr #include <memory> // for unique_ptr
@ -72,9 +73,13 @@ struct Cache {
*/ */
[[nodiscard]] auto View(std::size_t i) const { [[nodiscard]] auto View(std::size_t i) const {
std::uint64_t off = offset.at(i); std::uint64_t off = offset.at(i);
std::uint64_t len = offset.at(i + 1) - offset[i]; std::uint64_t len = this->Bytes(i);
return std::pair{off, len}; return std::pair{off, len};
} }
/**
* @brief Get the number of bytes for the i^th page.
*/
[[nodiscard]] std::uint64_t Bytes(std::size_t i) const { return offset.at(i + 1) - offset[i]; }
/** /**
* @brief Call this once the write for the cache is complete. * @brief Call this once the write for the cache is complete.
*/ */
@ -174,6 +179,10 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
ExceHandler exce_; ExceHandler exce_;
common::Monitor monitor_; common::Monitor monitor_;
[[nodiscard]] virtual SparsePageFormat<S>* CreatePageFormat() const {
return ::xgboost::data::CreatePageFormat<S>("raw");
}
[[nodiscard]] bool ReadCache() { [[nodiscard]] bool ReadCache() {
CHECK(!at_end_); CHECK(!at_end_);
if (!cache_info_->written) { if (!cache_info_->written) {
@ -207,7 +216,7 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
*GlobalConfigThreadLocalStore::Get() = config; *GlobalConfigThreadLocalStore::Get() = config;
auto page = std::make_shared<S>(); auto page = std::make_shared<S>();
this->exce_.Run([&] { this->exce_.Run([&] {
std::unique_ptr<SparsePageFormat<S>> fmt{CreatePageFormat<S>("raw")}; std::unique_ptr<SparsePageFormat<S>> fmt{this->CreatePageFormat()};
auto name = self->cache_info_->ShardName(); auto name = self->cache_info_->ShardName();
auto [offset, length] = self->cache_info_->View(fetch_it); auto [offset, length] = self->cache_info_->View(fetch_it);
auto fi = std::make_unique<common::PrivateMmapConstStream>(name, offset, length); auto fi = std::make_unique<common::PrivateMmapConstStream>(name, offset, length);
@ -234,7 +243,7 @@ class SparsePageSourceImpl : public BatchIteratorImpl<S> {
CHECK(!cache_info_->written); CHECK(!cache_info_->written);
common::Timer timer; common::Timer timer;
timer.Start(); timer.Start();
std::unique_ptr<SparsePageFormat<S>> fmt{CreatePageFormat<S>("raw")}; std::unique_ptr<SparsePageFormat<S>> fmt{this->CreatePageFormat()};
auto name = cache_info_->ShardName(); auto name = cache_info_->ShardName();
std::unique_ptr<common::AlignedFileWriteStream> fo; std::unique_ptr<common::AlignedFileWriteStream> fo;
@ -404,8 +413,8 @@ class PageSourceIncMixIn : public SparsePageSourceImpl<S> {
bool sync_{true}; bool sync_{true};
public: public:
PageSourceIncMixIn(float missing, int nthreads, bst_feature_t n_features, std::uint32_t n_batches, PageSourceIncMixIn(float missing, std::int32_t nthreads, bst_feature_t n_features,
std::shared_ptr<Cache> cache, bool sync) std::uint32_t n_batches, std::shared_ptr<Cache> cache, bool sync)
: Super::SparsePageSourceImpl{missing, nthreads, n_features, n_batches, cache}, sync_{sync} {} : Super::SparsePageSourceImpl{missing, nthreads, n_features, n_batches, cache}, sync_{sync} {}
[[nodiscard]] PageSourceIncMixIn& operator++() final { [[nodiscard]] PageSourceIncMixIn& operator++() final {

View File

@ -10,7 +10,6 @@
#include <string> // for string #include <string> // for string
#include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream #include "../common/io.h" // for AlignedResourceReadStream, AlignedFileWriteStream
#include "dmlc/io.h" // for Stream
#include "dmlc/registry.h" // for Registry, FunctionRegEntryBase #include "dmlc/registry.h" // for Registry, FunctionRegEntryBase
#include "xgboost/data.h" // for SparsePage,CSCPage,SortedCSCPage,EllpackPage ... #include "xgboost/data.h" // for SparsePage,CSCPage,SortedCSCPage,EllpackPage ...

View File

@ -7,7 +7,6 @@
#include <dmlc/thread_local.h> #include <dmlc/thread_local.h>
#include "xgboost/global_config.h" #include "xgboost/global_config.h"
#include "xgboost/json.h"
namespace xgboost { namespace xgboost {
DMLC_REGISTER_PARAMETER(GlobalConfiguration); DMLC_REGISTER_PARAMETER(GlobalConfiguration);

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2020-2023 by XGBoost Contributors * Copyright 2020-2024, XGBoost Contributors
*/ */
#ifndef FEATURE_GROUPS_CUH_ #ifndef FEATURE_GROUPS_CUH_
#define FEATURE_GROUPS_CUH_ #define FEATURE_GROUPS_CUH_
@ -29,22 +29,23 @@ struct FeatureGroup {
/** The number of features in the group. */ /** The number of features in the group. */
int num_features; int num_features;
/** The first bin in the group. */ /** The first bin in the group. */
int start_bin; bst_bin_t start_bin;
/** The number of bins in the group. */ /** The number of bins in the group. */
int num_bins; bst_bin_t num_bins;
}; };
/** \brief FeatureGroupsAccessor is a non-owning accessor for FeatureGroups. */ /** \brief FeatureGroupsAccessor is a non-owning accessor for FeatureGroups. */
struct FeatureGroupsAccessor { struct FeatureGroupsAccessor {
FeatureGroupsAccessor(common::Span<const int> feature_segments_, FeatureGroupsAccessor(common::Span<const int> feature_segments_,
common::Span<const int> bin_segments_, int max_group_bins_) : common::Span<const int> bin_segments_, int max_group_bins_)
feature_segments(feature_segments_), bin_segments(bin_segments_), : feature_segments(feature_segments_),
max_group_bins(max_group_bins_) {} bin_segments(bin_segments_),
max_group_bins(max_group_bins_) {}
common::Span<const int> feature_segments; common::Span<const int> feature_segments;
common::Span<const int> bin_segments; common::Span<const int> bin_segments;
int max_group_bins; int max_group_bins;
/** \brief Gets the number of feature groups. */ /** \brief Gets the number of feature groups. */
__host__ __device__ int NumGroups() const { __host__ __device__ int NumGroups() const {
return feature_segments.size() - 1; return feature_segments.size() - 1;
@ -84,7 +85,7 @@ struct FeatureGroups {
/** Maximum number of bins in a group. Useful to compute the amount of dynamic /** Maximum number of bins in a group. Useful to compute the amount of dynamic
shared memory when launching a kernel. */ shared memory when launching a kernel. */
int max_group_bins; int max_group_bins;
/** Creates feature groups by splitting features into groups. /** Creates feature groups by splitting features into groups.
\param cuts Histogram cuts that given the number of bins per feature. \param cuts Histogram cuts that given the number of bins per feature.
\param is_dense Whether the data matrix is dense. \param is_dense Whether the data matrix is dense.
@ -110,7 +111,7 @@ struct FeatureGroups {
private: private:
void InitSingle(const common::HistogramCuts& cuts); void InitSingle(const common::HistogramCuts& cuts);
}; };
} // namespace tree } // namespace tree
} // namespace xgboost } // namespace xgboost

View File

@ -167,7 +167,7 @@ GradientBasedSample ExternalMemoryNoSampling::Sample(Context const* ctx,
for (auto& batch : dmat->GetBatches<EllpackPage>(ctx, batch_param_)) { for (auto& batch : dmat->GetBatches<EllpackPage>(ctx, batch_param_)) {
auto page = batch.Impl(); auto page = batch.Impl();
if (!page_) { if (!page_) {
page_ = std::make_unique<EllpackPageImpl>(ctx->Device(), page->Cuts(), page->is_dense, page_ = std::make_unique<EllpackPageImpl>(ctx->Device(), page->CutsShared(), page->is_dense,
page->row_stride, dmat->Info().num_row_); page->row_stride, dmat->Info().num_row_);
} }
size_t num_elements = page_->Copy(ctx->Device(), page, offset); size_t num_elements = page_->Copy(ctx->Device(), page, offset);
@ -228,7 +228,7 @@ GradientBasedSample ExternalMemoryUniformSampling::Sample(Context const* ctx,
auto first_page = (*batch_iterator.begin()).Impl(); auto first_page = (*batch_iterator.begin()).Impl();
// Create a new ELLPACK page with empty rows. // Create a new ELLPACK page with empty rows.
page_.reset(); // Release the device memory first before reallocating page_.reset(); // Release the device memory first before reallocating
page_.reset(new EllpackPageImpl(ctx->Device(), first_page->Cuts(), first_page->is_dense, page_.reset(new EllpackPageImpl(ctx->Device(), first_page->CutsShared(), first_page->is_dense,
first_page->row_stride, sample_rows)); first_page->row_stride, sample_rows));
// Compact the ELLPACK pages into the single sample page. // Compact the ELLPACK pages into the single sample page.
@ -306,7 +306,7 @@ GradientBasedSample ExternalMemoryGradientBasedSampling::Sample(Context const* c
auto first_page = (*batch_iterator.begin()).Impl(); auto first_page = (*batch_iterator.begin()).Impl();
// Create a new ELLPACK page with empty rows. // Create a new ELLPACK page with empty rows.
page_.reset(); // Release the device memory first before reallocating page_.reset(); // Release the device memory first before reallocating
page_.reset(new EllpackPageImpl(ctx->Device(), first_page->Cuts(), first_page->is_dense, page_.reset(new EllpackPageImpl(ctx->Device(), first_page->CutsShared(), first_page->is_dense,
first_page->row_stride, sample_rows)); first_page->row_stride, sample_rows));
// Compact the ELLPACK pages into the single sample page. // Compact the ELLPACK pages into the single sample page.

View File

@ -56,8 +56,7 @@ TEST(HistUtil, DeviceSketch) {
TEST(HistUtil, SketchBatchNumElements) { TEST(HistUtil, SketchBatchNumElements) {
#if defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1 #if defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1
LOG(WARNING) << "Test not runnable with RMM enabled."; GTEST_SKIP_("Test not runnable with RMM enabled.");
return;
#endif // defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1 #endif // defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1
size_t constexpr kCols = 10000; size_t constexpr kCols = 10000;
int device; int device;

View File

@ -152,7 +152,7 @@ TEST(EllpackPage, Copy) {
auto page = (*dmat->GetBatches<EllpackPage>(&ctx, param).begin()).Impl(); auto page = (*dmat->GetBatches<EllpackPage>(&ctx, param).begin()).Impl();
// Create an empty result page. // Create an empty result page.
EllpackPageImpl result(FstCU(), page->Cuts(), page->is_dense, page->row_stride, kRows); EllpackPageImpl result(FstCU(), page->CutsShared(), page->is_dense, page->row_stride, kRows);
// Copy batch pages into the result page. // Copy batch pages into the result page.
size_t offset = 0; size_t offset = 0;
@ -200,7 +200,8 @@ TEST(EllpackPage, Compact) {
auto page = (*dmat->GetBatches<EllpackPage>(&ctx, param).begin()).Impl(); auto page = (*dmat->GetBatches<EllpackPage>(&ctx, param).begin()).Impl();
// Create an empty result page. // Create an empty result page.
EllpackPageImpl result(FstCU(), page->Cuts(), page->is_dense, page->row_stride, kCompactedRows); EllpackPageImpl result(FstCU(), page->CutsShared(), page->is_dense, page->row_stride,
kCompactedRows);
// Compact batch pages into the result page. // Compact batch pages into the result page.
std::vector<size_t> row_indexes_h { std::vector<size_t> row_indexes_h {

View File

@ -1,14 +1,14 @@
/** /**
* Copyright 2021-2023, XGBoost contributors * Copyright 2021-2024, XGBoost contributors
*/ */
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <xgboost/data.h> #include <xgboost/data.h>
#include "../../../src/common/io.h" // for PrivateMmapConstStream, AlignedResourceReadStream... #include "../../../src/common/io.h" // for PrivateMmapConstStream, AlignedResourceReadStream...
#include "../../../src/data/ellpack_page.cuh" #include "../../../src/data/ellpack_page.cuh"
#include "../../../src/data/sparse_page_source.h" #include "../../../src/data/ellpack_page_raw_format.h" // for EllpackPageRawFormat
#include "../../../src/tree/param.h" // TrainParam #include "../../../src/tree/param.h" // TrainParam
#include "../filesystem.h" // dmlc::TemporaryDirectory #include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h" #include "../helpers.h"
namespace xgboost::data { namespace xgboost::data {
@ -16,12 +16,18 @@ TEST(EllpackPageRawFormat, IO) {
Context ctx{MakeCUDACtx(0)}; Context ctx{MakeCUDACtx(0)};
auto param = BatchParam{256, tree::TrainParam::DftSparseThreshold()}; auto param = BatchParam{256, tree::TrainParam::DftSparseThreshold()};
std::unique_ptr<SparsePageFormat<EllpackPage>> format{CreatePageFormat<EllpackPage>("raw")};
auto m = RandomDataGenerator{100, 14, 0.5}.GenerateDMatrix(); auto m = RandomDataGenerator{100, 14, 0.5}.GenerateDMatrix();
dmlc::TemporaryDirectory tmpdir; dmlc::TemporaryDirectory tmpdir;
std::string path = tmpdir.path + "/ellpack.page"; std::string path = tmpdir.path + "/ellpack.page";
std::shared_ptr<common::HistogramCuts const> cuts;
for (auto const& page : m->GetBatches<EllpackPage>(&ctx, param)) {
cuts = page.Impl()->CutsShared();
}
cuts->SetDevice(ctx.Device());
auto format = std::make_unique<EllpackPageRawFormat>(cuts);
std::size_t n_bytes{0}; std::size_t n_bytes{0};
{ {
auto fo = std::make_unique<common::AlignedFileWriteStream>(StringView{path}, "wb"); auto fo = std::make_unique<common::AlignedFileWriteStream>(StringView{path}, "wb");
@ -33,7 +39,7 @@ TEST(EllpackPageRawFormat, IO) {
EllpackPage page; EllpackPage page;
std::unique_ptr<common::AlignedResourceReadStream> fi{ std::unique_ptr<common::AlignedResourceReadStream> fi{
std::make_unique<common::PrivateMmapConstStream>(path.c_str(), 0, n_bytes)}; std::make_unique<common::PrivateMmapConstStream>(path.c_str(), 0, n_bytes)};
format->Read(&page, fi.get()); ASSERT_TRUE(format->Read(&page, fi.get()));
for (auto const &ellpack : m->GetBatches<EllpackPage>(&ctx, param)) { for (auto const &ellpack : m->GetBatches<EllpackPage>(&ctx, param)) {
auto loaded = page.Impl(); auto loaded = page.Impl();

View File

@ -7,23 +7,28 @@
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <memory> // for unique_ptr #include <memory> // for unique_ptr
#include "../../../src/common/column_matrix.h" #include "../../../src/common/column_matrix.h" // for common::ColumnMatrix
#include "../../../src/common/io.h" // for MmapResource, AlignedResourceReadStream... #include "../../../src/common/io.h" // for MmapResource, AlignedResourceReadStream...
#include "../../../src/data/gradient_index.h" // for GHistIndexMatrix #include "../../../src/data/gradient_index.h" // for GHistIndexMatrix
#include "../../../src/data/sparse_page_writer.h" // for CreatePageFormat #include "../../../src/data/gradient_index_format.h" // for GHistIndexRawFormat
#include "../helpers.h" // for RandomDataGenerator #include "../helpers.h" // for RandomDataGenerator
namespace xgboost::data { namespace xgboost::data {
TEST(GHistIndexPageRawFormat, IO) { TEST(GHistIndexPageRawFormat, IO) {
Context ctx; Context ctx;
std::unique_ptr<SparsePageFormat<GHistIndexMatrix>> format{
CreatePageFormat<GHistIndexMatrix>("raw")};
auto m = RandomDataGenerator{100, 14, 0.5}.GenerateDMatrix(); auto m = RandomDataGenerator{100, 14, 0.5}.GenerateDMatrix();
dmlc::TemporaryDirectory tmpdir; dmlc::TemporaryDirectory tmpdir;
std::string path = tmpdir.path + "/ghistindex.page"; std::string path = tmpdir.path + "/ghistindex.page";
auto batch = BatchParam{256, 0.5}; auto batch = BatchParam{256, 0.5};
common::HistogramCuts cuts;
for (auto const &index : m->GetBatches<GHistIndexMatrix>(&ctx, batch)) {
cuts = index.Cuts();
break;
}
auto format = std::make_unique<GHistIndexRawFormat>(std::move(cuts));
std::size_t bytes{0}; std::size_t bytes{0};
{ {
auto fo = std::make_unique<common::AlignedFileWriteStream>(StringView{path}, "wb"); auto fo = std::make_unique<common::AlignedFileWriteStream>(StringView{path}, "wb");
@ -36,7 +41,7 @@ TEST(GHistIndexPageRawFormat, IO) {
std::unique_ptr<common::AlignedResourceReadStream> fi{ std::unique_ptr<common::AlignedResourceReadStream> fi{
std::make_unique<common::PrivateMmapConstStream>(path, 0, bytes)}; std::make_unique<common::PrivateMmapConstStream>(path, 0, bytes)};
format->Read(&page, fi.get()); ASSERT_TRUE(format->Read(&page, fi.get()));
for (auto const &gidx : m->GetBatches<GHistIndexMatrix>(&ctx, batch)) { for (auto const &gidx : m->GetBatches<GHistIndexMatrix>(&ctx, batch)) {
auto const &loaded = gidx; auto const &loaded = gidx;

View File

@ -20,9 +20,8 @@ void TestEquivalent(float sparsity) {
std::numeric_limits<float>::quiet_NaN(), 0, 256); std::numeric_limits<float>::quiet_NaN(), 0, 256);
std::size_t offset = 0; std::size_t offset = 0;
auto first = (*m.GetEllpackBatches(&ctx, {}).begin()).Impl(); auto first = (*m.GetEllpackBatches(&ctx, {}).begin()).Impl();
std::unique_ptr<EllpackPageImpl> page_concatenated { std::unique_ptr<EllpackPageImpl> page_concatenated{new EllpackPageImpl(
new EllpackPageImpl(ctx.Device(), first->Cuts(), first->is_dense, ctx.Device(), first->CutsShared(), first->is_dense, first->row_stride, 1000 * 100)};
first->row_stride, 1000 * 100)};
for (auto& batch : m.GetBatches<EllpackPage>(&ctx, {})) { for (auto& batch : m.GetBatches<EllpackPage>(&ctx, {})) {
auto page = batch.Impl(); auto page = batch.Impl();
size_t num_elements = page_concatenated->Copy(ctx.Device(), page, offset); size_t num_elements = page_concatenated->Copy(ctx.Device(), page, offset);

View File

@ -171,6 +171,12 @@ TEST(SparsePageDMatrix, GHistIndexSkipSparsePage) {
// Restore the batch parameter by passing it in again through check_ghist // Restore the batch parameter by passing it in again through check_ghist
check_ghist(); check_ghist();
} }
// half the pages
auto it = Xy->GetBatches<SparsePage>(&ctx).begin();
for (std::int32_t i = 0; i < 3; ++i) {
++it;
}
check_ghist();
} }
TEST(SparsePageDMatrix, MetaInfo) { TEST(SparsePageDMatrix, MetaInfo) {

View File

@ -164,9 +164,9 @@ TEST(SparsePageDMatrix, EllpackPageContent) {
size_t offset = 0; size_t offset = 0;
for (auto& batch : dmat_ext->GetBatches<EllpackPage>(&ctx, param)) { for (auto& batch : dmat_ext->GetBatches<EllpackPage>(&ctx, param)) {
if (!impl_ext) { if (!impl_ext) {
impl_ext = std::make_unique<EllpackPageImpl>(batch.Impl()->gidx_buffer.Device(), impl_ext = std::make_unique<EllpackPageImpl>(
batch.Impl()->Cuts(), batch.Impl()->is_dense, batch.Impl()->gidx_buffer.Device(), batch.Impl()->CutsShared(), batch.Impl()->is_dense,
batch.Impl()->row_stride, kRows); batch.Impl()->row_stride, kRows);
} }
auto n_elems = impl_ext->Copy(ctx.Device(), batch.Impl(), offset); auto n_elems = impl_ext->Copy(ctx.Device(), batch.Impl(), offset);
offset += n_elems; offset += n_elems;

View File

@ -13,31 +13,25 @@
namespace xgboost { namespace xgboost {
#if defined(__CUDACC__) #if defined(__CUDACC__)
namespace { namespace detail {
class HistogramCutsWrapper : public common::HistogramCuts { class HistogramCutsWrapper : public common::HistogramCuts {
public: public:
using SuperT = common::HistogramCuts; using SuperT = common::HistogramCuts;
void SetValues(std::vector<float> cuts) { void SetValues(std::vector<float> cuts) { SuperT::cut_values_.HostVector() = std::move(cuts); }
SuperT::cut_values_.HostVector() = std::move(cuts); void SetPtrs(std::vector<uint32_t> ptrs) { SuperT::cut_ptrs_.HostVector() = std::move(ptrs); }
} void SetMins(std::vector<float> mins) { SuperT::min_vals_.HostVector() = std::move(mins); }
void SetPtrs(std::vector<uint32_t> ptrs) {
SuperT::cut_ptrs_.HostVector() = std::move(ptrs);
}
void SetMins(std::vector<float> mins) {
SuperT::min_vals_.HostVector() = std::move(mins);
}
}; };
} // anonymous namespace } // namespace detail
inline std::unique_ptr<EllpackPageImpl> BuildEllpackPage(int n_rows, int n_cols, inline std::unique_ptr<EllpackPageImpl> BuildEllpackPage(int n_rows, int n_cols,
bst_float sparsity = 0) { bst_float sparsity = 0) {
auto dmat = RandomDataGenerator(n_rows, n_cols, sparsity).Seed(3).GenerateDMatrix(); auto dmat = RandomDataGenerator(n_rows, n_cols, sparsity).Seed(3).GenerateDMatrix();
const SparsePage& batch = *dmat->GetBatches<xgboost::SparsePage>().begin(); const SparsePage& batch = *dmat->GetBatches<xgboost::SparsePage>().begin();
HistogramCutsWrapper cmat; auto cmat = std::make_shared<detail::HistogramCutsWrapper>();
cmat.SetPtrs({0, 3, 6, 9, 12, 15, 18, 21, 24}); cmat->SetPtrs({0, 3, 6, 9, 12, 15, 18, 21, 24});
// 24 cut fields, 3 cut fields for each feature (column). // 24 cut fields, 3 cut fields for each feature (column).
cmat.SetValues({0.30f, 0.67f, 1.64f, cmat->SetValues({0.30f, 0.67f, 1.64f,
0.32f, 0.77f, 1.95f, 0.32f, 0.77f, 1.95f,
0.29f, 0.70f, 1.80f, 0.29f, 0.70f, 1.80f,
0.32f, 0.75f, 1.85f, 0.32f, 0.75f, 1.85f,
@ -45,7 +39,7 @@ inline std::unique_ptr<EllpackPageImpl> BuildEllpackPage(int n_rows, int n_cols,
0.25f, 0.74f, 2.00f, 0.25f, 0.74f, 2.00f,
0.26f, 0.74f, 1.98f, 0.26f, 0.74f, 1.98f,
0.26f, 0.71f, 1.83f}); 0.26f, 0.71f, 1.83f});
cmat.SetMins({0.1f, 0.2f, 0.3f, 0.1f, 0.2f, 0.3f, 0.2f, 0.2f}); cmat->SetMins({0.1f, 0.2f, 0.3f, 0.1f, 0.2f, 0.3f, 0.2f, 0.2f});
bst_idx_t row_stride = 0; bst_idx_t row_stride = 0;
const auto &offset_vec = batch.offset.ConstHostVector(); const auto &offset_vec = batch.offset.ConstHostVector();

View File

@ -150,13 +150,13 @@ TEST(GpuHist, BuildHistSharedMem) {
TestBuildHist<GradientPairPrecise>(true); TestBuildHist<GradientPairPrecise>(true);
} }
HistogramCutsWrapper GetHostCutMatrix () { std::shared_ptr<detail::HistogramCutsWrapper> GetHostCutMatrix () {
HistogramCutsWrapper cmat; auto cmat = std::make_shared<detail::HistogramCutsWrapper>();
cmat.SetPtrs({0, 3, 6, 9, 12, 15, 18, 21, 24}); cmat->SetPtrs({0, 3, 6, 9, 12, 15, 18, 21, 24});
cmat.SetMins({0.1f, 0.2f, 0.3f, 0.1f, 0.2f, 0.3f, 0.2f, 0.2f}); cmat->SetMins({0.1f, 0.2f, 0.3f, 0.1f, 0.2f, 0.3f, 0.2f, 0.2f});
// 24 cut fields, 3 cut fields for each feature (column). // 24 cut fields, 3 cut fields for each feature (column).
// Each row of the cut represents the cuts for a data column. // Each row of the cut represents the cuts for a data column.
cmat.SetValues({0.30f, 0.67f, 1.64f, cmat->SetValues({0.30f, 0.67f, 1.64f,
0.32f, 0.77f, 1.95f, 0.32f, 0.77f, 1.95f,
0.29f, 0.70f, 1.80f, 0.29f, 0.70f, 1.80f,
0.32f, 0.75f, 1.85f, 0.32f, 0.75f, 1.85f,