Skip to content

Commit b3130a8

Browse files
authored
Remove reintroduced GPU sketch memory estimation (#12159)
1 parent cc451b6 commit b3130a8

3 files changed

Lines changed: 20 additions & 203 deletions

File tree

src/common/hist_util.cu

Lines changed: 2 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -34,99 +34,6 @@ size_t RequiredSampleCutsPerColumn(int max_bins, size_t num_rows) {
3434
return std::min(num_cuts, num_rows);
3535
}
3636

37-
size_t RequiredSampleCuts(bst_idx_t num_rows, bst_feature_t num_columns, size_t max_bins,
38-
bst_idx_t nnz) {
39-
auto per_column = RequiredSampleCutsPerColumn(max_bins, num_rows);
40-
auto if_dense = num_columns * per_column;
41-
auto result = std::min(nnz, if_dense);
42-
return result;
43-
}
44-
45-
size_t RequiredMemory(bst_idx_t num_rows, bst_feature_t num_columns, size_t nnz, size_t num_bins,
46-
bool with_weights) {
47-
size_t peak = 0;
48-
auto cuts_bytes = RequiredSampleCuts(num_rows, num_bins, num_bins, nnz) * sizeof(SketchEntry);
49-
// 0. Allocate cut pointer in quantile container by increasing: n_columns + 1
50-
size_t total = (num_columns + 1) * sizeof(SketchContainer::OffsetT);
51-
// 1. Copy and sort: 2 * bytes_per_element * shape
52-
total += BytesPerElement(with_weights) * num_rows * num_columns;
53-
peak = std::max(peak, total);
54-
// 2. Deallocate bytes_per_element * shape due to reusing memory in sort.
55-
total -= BytesPerElement(with_weights) * num_rows * num_columns / 2;
56-
// 3. Allocate colomn size scan by increasing: n_columns + 1
57-
total += (num_columns + 1) * sizeof(SketchContainer::OffsetT);
58-
// 4. Allocate cut pointer by increasing: n_columns + 1
59-
total += (num_columns + 1) * sizeof(SketchContainer::OffsetT);
60-
// 5. Allocate cuts: assuming rows is greater than bins: n_columns * limit_size
61-
total += cuts_bytes;
62-
// 6. Install the first batch summary into the resident sketch while the temporary pruned
63-
// summary is still live.
64-
total += cuts_bytes;
65-
// 7. Deallocate copied entries by reducing: bytes_per_element * shape.
66-
peak = std::max(peak, total);
67-
total -= (BytesPerElement(with_weights) * num_rows * num_columns) / 2;
68-
// 8. Deallocate the temporary pruned batch summary after merge/prune commit.
69-
peak = std::max(peak, total);
70-
total -= cuts_bytes;
71-
// 9. Deallocate column size scan.
72-
peak = std::max(peak, total);
73-
total -= (num_columns + 1) * sizeof(SketchContainer::OffsetT);
74-
// 10. Deallocate cut size scan.
75-
total -= (num_columns + 1) * sizeof(SketchContainer::OffsetT);
76-
// 11. Allocate final cut values and cut ptrs: std::min(rows, bins + 1) * n_columns +
77-
// n_columns + 1
78-
total += std::min(num_rows, num_bins) * num_columns * sizeof(float);
79-
total +=
80-
(num_columns + 1) *
81-
sizeof(std::remove_reference_t<decltype(std::declval<HistogramCuts>().Ptrs())>::value_type);
82-
peak = std::max(peak, total);
83-
84-
return peak;
85-
}
86-
87-
bst_idx_t SketchBatchNumElements(bst_idx_t sketch_batch_num_elements, SketchShape shape, int device,
88-
size_t num_cuts, bool has_weight, std::size_t container_bytes) {
89-
auto constexpr kIntMax = static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max());
90-
91-
// Device available memory is not accurate when a memory pool is used.
92-
auto avoid_estimation_with_pool = [&] {
93-
(void)device;
94-
double total_mem = curt::TotalMemory() - container_bytes;
95-
double total_f32 = total_mem / sizeof(float);
96-
double n_max_used_f32 = std::max(total_f32 / 8.0, 1.0);
97-
if (shape.nnz > shape.Size()) {
98-
// Unknown nnz
99-
shape.nnz = shape.Size();
100-
}
101-
return std::min(static_cast<bst_idx_t>(n_max_used_f32), shape.nnz);
102-
};
103-
104-
#if defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1
105-
// Early exit with RMM pool
106-
return avoid_estimation_with_pool();
107-
#endif // defined(XGBOOST_USE_RMM) && XGBOOST_USE_RMM == 1
108-
// Early exit with CUDA async pool
109-
if (GlobalConfigThreadLocalStore::Get()->use_cuda_async_pool) {
110-
return avoid_estimation_with_pool();
111-
}
112-
113-
(void)container_bytes; // We known the remaining size when RMM is not used.
114-
if (sketch_batch_num_elements == detail::UnknownSketchNumElements()) {
115-
auto required_memory =
116-
RequiredMemory(shape.n_samples, shape.n_features, shape.nnz, num_cuts, has_weight);
117-
// use up to 80% of available space
118-
auto avail = dh::AvailableMemory(device) * 0.8;
119-
CHECK_GT(avail, 0) << error::ZeroCudaMemory();
120-
if (required_memory > avail) {
121-
sketch_batch_num_elements = avail / BytesPerElement(has_weight);
122-
} else {
123-
sketch_batch_num_elements = std::min(shape.Size(), shape.nnz);
124-
}
125-
}
126-
127-
return std::min(sketch_batch_num_elements, kIntMax);
128-
}
129-
13037
void SortByWeight(Context const* ctx, dh::device_vector<float>* weights,
13138
dh::device_vector<Entry>* sorted_entries) {
13239
// Sort both entries and wegihts.
@@ -360,21 +267,16 @@ void ProcessWeightedBatch(Context const* ctx, const SparsePage& page, MetaInfo c
360267
}
361268

362269
HistogramCuts DeviceSketchWithHessian(Context const* ctx, DMatrix* p_fmat, bst_bin_t max_bin,
363-
Span<float const> hessian,
364-
std::size_t sketch_batch_num_elements) {
270+
Span<float const> hessian) {
365271
auto const& info = p_fmat->Info();
366272
bool has_weight = !info.weights_.Empty();
367273
info.feature_types.SetDevice(ctx->Device());
368274

369275
HostDeviceVector<float> weight;
370276
weight.SetDevice(ctx->Device());
371277

372-
// Configure batch size based on available memory
373278
std::size_t num_cuts_per_feature = detail::RequiredSampleCutsPerColumn(max_bin, info.num_row_);
374-
sketch_batch_num_elements = detail::SketchBatchNumElements(
375-
sketch_batch_num_elements,
376-
detail::SketchShape{info.num_row_, info.num_col_, info.num_nonzero_}, ctx->Ordinal(),
377-
num_cuts_per_feature, has_weight, 0);
279+
auto sketch_batch_num_elements = detail::kSketchBatchNumElements;
378280

379281
CUDAContext const* cuctx = ctx->CUDACtx();
380282

src/common/hist_util.cuh

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -160,53 +160,12 @@ void GetColumnSizesScan(CUDAContext const* cuctx, DeviceOrd device, size_t num_c
160160
column_sizes_scan->begin());
161161
}
162162

163-
inline size_t constexpr BytesPerElement(bool has_weight) {
164-
// Double the memory usage for sorting. We need to assign weight for each element, so
165-
// sizeof(float) is added to all elements.
166-
return (has_weight ? sizeof(Entry) + sizeof(float) : sizeof(Entry)) * 2;
167-
}
168-
169-
struct SketchShape {
170-
bst_idx_t n_samples;
171-
bst_feature_t n_features;
172-
bst_idx_t nnz;
173-
174-
template <typename F, std::enable_if_t<std::is_integral_v<F>>* = nullptr>
175-
SketchShape(bst_idx_t n_samples, F n_features, bst_idx_t nnz)
176-
: n_samples{n_samples}, n_features{static_cast<bst_feature_t>(n_features)}, nnz{nnz} {}
177-
178-
[[nodiscard]] bst_idx_t Size() const { return n_samples * n_features; }
179-
};
180-
181-
/**
182-
* @brief Calcuate the length of sliding window. Returns `sketch_batch_num_elements`
183-
* directly if it's not 0.
184-
*/
185-
bst_idx_t SketchBatchNumElements(bst_idx_t sketch_batch_num_elements, SketchShape shape, int device,
186-
size_t num_cuts, bool has_weight, std::size_t container_bytes);
163+
constexpr bst_idx_t kSketchBatchNumElements = bst_idx_t{1} << 26; // 64M
187164

188165
// Compute number of sample cuts needed on local node to maintain accuracy
189166
// We take more cuts than needed and then reduce them later
190167
size_t RequiredSampleCutsPerColumn(int max_bins, size_t num_rows);
191168

192-
/* \brief Estimate required memory for each sliding window.
193-
*
194-
* It's not precise as to obtain exact memory usage for sparse dataset we need to walk
195-
* through the whole dataset first. Also if data is from host DMatrix, we copy the
196-
* weight, group and offset on first batch, which is not considered in the function.
197-
*
198-
* \param num_rows Number of rows in this worker.
199-
* \param num_columns Number of columns for this dataset.
200-
* \param nnz Number of non-zero element. Put in something greater than rows *
201-
* cols if nnz is unknown.
202-
* \param num_bins Number of histogram bins.
203-
* \param with_weights Whether weight is used, works the same for ranking and other models.
204-
*
205-
* \return The estimated bytes
206-
*/
207-
size_t RequiredMemory(bst_idx_t num_rows, bst_feature_t num_columns, size_t nnz,
208-
size_t num_bins, bool with_weights);
209-
210169
// Count the valid entries in each column and copy them out.
211170
template <typename AdapterBatch, typename BatchIter>
212171
void MakeEntriesFromAdapter(CUDAContext const* cuctx, AdapterBatch const& batch,
@@ -241,7 +200,6 @@ void RemoveDuplicatedCategories(Context const* ctx, MetaInfo const& info,
241200
dh::device_vector<float>* p_sorted_weights,
242201
dh::caching_device_vector<size_t>* p_column_sizes_scan);
243202

244-
constexpr bst_idx_t UnknownSketchNumElements() { return 0; }
245203
} // namespace detail
246204

247205
/**
@@ -251,28 +209,23 @@ constexpr bst_idx_t UnknownSketchNumElements() { return 0; }
251209
* @param p_fmat Training feature matrix
252210
* @param max_bin Maximum number of bins for each feature
253211
* @param hessian Hessian vector.
254-
* @param sketch_batch_num_elements 0 means autodetect. Only modify this for testing.
255212
*
256213
* @return Quantile cuts
257214
*/
258215
HistogramCuts DeviceSketchWithHessian(Context const* ctx, DMatrix* p_fmat, bst_bin_t max_bin,
259-
Span<float const> hessian,
260-
std::size_t sketch_batch_num_elements = detail::UnknownSketchNumElements());
216+
Span<float const> hessian);
261217

262218
/**
263219
* @brief Compute sketch on DMatrix with GPU.
264220
*
265221
* @param ctx Runtime context
266222
* @param p_fmat Training feature matrix
267223
* @param max_bin Maximum number of bins for each feature
268-
* @param sketch_batch_num_elements 0 means autodetect. Only modify this for testing.
269224
*
270225
* @return Quantile cuts
271226
*/
272-
inline HistogramCuts DeviceSketch(
273-
Context const* ctx, DMatrix* p_fmat, bst_bin_t max_bin,
274-
std::size_t sketch_batch_num_elements = detail::UnknownSketchNumElements()) {
275-
return DeviceSketchWithHessian(ctx, p_fmat, max_bin, {}, sketch_batch_num_elements);
227+
inline HistogramCuts DeviceSketch(Context const* ctx, DMatrix* p_fmat, bst_bin_t max_bin) {
228+
return DeviceSketchWithHessian(ctx, p_fmat, max_bin, {});
276229
}
277230

278231
template <typename AdapterBatch>
@@ -393,13 +346,10 @@ void ProcessWeightedSlidingWindow(Context const* ctx, Batch batch, MetaInfo cons
393346
* @param info Metainfo used for sketching.
394347
* @param missing Floating point value that represents invalid value.
395348
* @param sketch_container Container for output sketch.
396-
* @param sketch_batch_num_elements Number of element per-sliding window, use it only for
397-
* testing.
398349
*/
399350
template <typename Batch>
400351
void AdapterDeviceSketch(Context const* ctx, Batch batch, bst_bin_t num_bins, MetaInfo const& info,
401-
float missing, SketchContainer* sketch_container,
402-
bst_idx_t sketch_batch_num_elements = detail::UnknownSketchNumElements()) {
352+
float missing, SketchContainer* sketch_container) {
403353
bst_idx_t num_rows = batch.NumRows();
404354
size_t num_cols = batch.NumCols();
405355

@@ -408,16 +358,10 @@ void AdapterDeviceSketch(Context const* ctx, Batch batch, bst_bin_t num_bins, Me
408358
bst_idx_t const kRemaining = batch.Size();
409359
bst_idx_t begin = 0;
410360

411-
auto shape = detail::SketchShape{num_rows, num_cols, std::numeric_limits<bst_idx_t>::max()};
412-
413361
while (begin < kRemaining) {
414-
// Use total number of samples to estimate the needed cuts first, this doesn't hurt
415-
// accuracy as total number of samples is larger.
416362
auto num_cuts_per_feature = detail::RequiredSampleCutsPerColumn(num_bins, num_rows);
417-
// Estimate the memory usage based on the current available memory.
418-
sketch_batch_num_elements = detail::SketchBatchNumElements(
419-
sketch_batch_num_elements, shape, ctx->Ordinal(), num_cuts_per_feature, weighted,
420-
sketch_container->MemCostBytes());
363+
auto remaining = kRemaining - begin;
364+
auto sketch_batch_num_elements = std::min(detail::kSketchBatchNumElements, remaining);
421365
// Re-estimate the needed number of cuts based on the size of the sub-batch.
422366
//
423367
// The estimation of `sketch_batch_num_elements` assumes dense input, so the

tests/cpp/common/test_hist_util.cu

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -276,29 +276,6 @@ TEST(HistUitl, DeviceSketchWeights) {
276276
}
277277
}
278278

279-
TEST(HistUtil, DeviceSketchBatches) {
280-
auto ctx = MakeCUDACtx(0);
281-
int num_bins = 256;
282-
int num_rows = 5000;
283-
auto batch_sizes = {0, 100, 1500, 6000};
284-
int num_columns = 5;
285-
for (auto batch_size : batch_sizes) {
286-
auto x = GenerateRandom(num_rows, num_columns);
287-
auto dmat = GetDMatrixFromData(x, num_rows, num_columns);
288-
auto cuts = DeviceSketch(&ctx, dmat.get(), num_bins, batch_size);
289-
ValidateCuts(cuts, dmat.get(), num_bins);
290-
}
291-
292-
num_rows = 1000;
293-
size_t batches = 16;
294-
auto x = GenerateRandom(num_rows * batches, num_columns);
295-
auto dmat = GetDMatrixFromData(x, num_rows * batches, num_columns);
296-
auto cuts_with_batches = DeviceSketch(&ctx, dmat.get(), num_bins, num_rows);
297-
auto cuts = DeviceSketch(&ctx, dmat.get(), num_bins, 0);
298-
ValidateCuts(cuts_with_batches, dmat.get(), num_bins);
299-
ValidateCuts(cuts, dmat.get(), num_bins);
300-
}
301-
302279
TEST(HistUtil, DeviceSketchMultipleColumnsExternal) {
303280
auto ctx = MakeCUDACtx(0);
304281
auto bin_sizes = {2, 16, 256, 512};
@@ -682,14 +659,13 @@ class DeviceSketchWithHessianTest
682659
}
683660

684661
void CheckReg(Context const* ctx, std::shared_ptr<DMatrix> p_fmat, bst_bin_t n_bins,
685-
HostDeviceVector<float> const& hessian, std::vector<float> const& w,
686-
std::size_t n_elements) const {
662+
HostDeviceVector<float> const& hessian, std::vector<float> const& w) const {
687663
auto const& h_hess = hessian.ConstHostVector();
688664
auto& h_weight = p_fmat->Info().weights_.HostVector();
689665
h_weight = w;
690666

691667
HistogramCuts cuts_hess =
692-
DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan(), n_elements);
668+
DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan());
693669

694670
// merge hessian
695671
ASSERT_EQ(h_weight.size(), h_hess.size());
@@ -698,7 +674,7 @@ class DeviceSketchWithHessianTest
698674
}
699675
ValidateCuts(cuts_hess, p_fmat.get(), n_bins, kMaxWeightedNormalizedRankError);
700676

701-
HistogramCuts cuts_wh = DeviceSketch(ctx, p_fmat.get(), n_bins, n_elements);
677+
HistogramCuts cuts_wh = DeviceSketch(ctx, p_fmat.get(), n_bins);
702678
ValidateCuts(cuts_wh, p_fmat.get(), n_bins, kMaxWeightedNormalizedRankError);
703679
ASSERT_EQ(cuts_hess.Values().size(), cuts_wh.Values().size());
704680
for (std::size_t i = 0; i < cuts_hess.Values().size(); ++i) {
@@ -711,8 +687,7 @@ class DeviceSketchWithHessianTest
711687
protected:
712688
Context ctx_ = MakeCUDACtx(0);
713689

714-
void TestLTR(Context const* ctx, bst_idx_t n_samples, bst_bin_t n_bins,
715-
std::size_t n_elements) const {
690+
void TestLTR(Context const* ctx, bst_idx_t n_samples, bst_bin_t n_bins) const {
716691
auto x = GenerateRandom(n_samples, n_features_);
717692

718693
std::vector<bst_group_t> gptr;
@@ -730,7 +705,7 @@ class DeviceSketchWithHessianTest
730705
std::vector<float> w(n_groups_, 1.0f);
731706
p_fmat->Info().weights_.HostVector() = w;
732707
HistogramCuts cuts_hess =
733-
DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan(), n_elements);
708+
DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan());
734709
// make validation easier by converting it into sample weight.
735710
p_fmat->Info().weights_.HostVector() = h_hess;
736711
p_fmat->Info().group_ptr_.clear();
@@ -742,8 +717,7 @@ class DeviceSketchWithHessianTest
742717
// test with random group weight
743718
w = GenerateRandomWeights(n_groups_);
744719
p_fmat->Info().weights_.HostVector() = w;
745-
cuts_hess =
746-
DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan(), n_elements);
720+
cuts_hess = DeviceSketchWithHessian(ctx, p_fmat.get(), n_bins, hessian.ConstDeviceSpan());
747721
// make validation easier by converting it into sample weight.
748722
p_fmat->Info().weights_.Resize(n_samples);
749723
for (std::size_t i = 0; i < h_hess.size(); ++i) {
@@ -760,23 +734,22 @@ class DeviceSketchWithHessianTest
760734
auto gidx = dh::SegmentId(Span{gptr.data(), gptr.size()}, i);
761735
p_fmat->Info().weights_.HostVector()[i] = w[gidx] * h_hess[i];
762736
}
763-
auto cuts = DeviceSketch(ctx, p_fmat.get(), n_bins, n_elements);
737+
auto cuts = DeviceSketch(ctx, p_fmat.get(), n_bins);
764738
ValidateCuts(cuts, p_fmat.get(), n_bins, kMaxWeightedNormalizedRankError);
765739
ASSERT_EQ(cuts.Values().size(), cuts_hess.Values().size());
766740
for (std::size_t i = 0; i < cuts.Values().size(); ++i) {
767741
EXPECT_NEAR(cuts.Values()[i], cuts_hess.Values()[i], 1e-4f);
768742
}
769743
}
770744

771-
void TestRegression(Context const* ctx, bst_idx_t n_samples, bst_bin_t n_bins,
772-
std::size_t n_elements) const {
745+
void TestRegression(Context const* ctx, bst_idx_t n_samples, bst_bin_t n_bins) const {
773746
auto x = GenerateRandom(n_samples, n_features_);
774747
auto p_fmat = GetDMatrixFromData(x, n_samples, n_features_);
775748
std::vector<float> w = GenerateRandomWeights(n_samples);
776749

777750
auto hessian = this->GenerateHessian(ctx, n_samples);
778751

779-
this->CheckReg(ctx, p_fmat, n_bins, hessian, w, n_elements);
752+
this->CheckReg(ctx, p_fmat, n_bins, hessian, w);
780753
}
781754
};
782755

@@ -799,11 +772,9 @@ TEST_P(DeviceSketchWithHessianTest, DeviceSketchWithHessian) {
799772
auto n_samples = std::get<1>(param);
800773
auto n_bins = std::get<2>(param);
801774
if (std::get<0>(param)) {
802-
this->TestLTR(&ctx_, n_samples, n_bins, 0);
803-
this->TestLTR(&ctx_, n_samples, n_bins, 512);
775+
this->TestLTR(&ctx_, n_samples, n_bins);
804776
} else {
805-
this->TestRegression(&ctx_, n_samples, n_bins, 0);
806-
this->TestRegression(&ctx_, n_samples, n_bins, 512);
777+
this->TestRegression(&ctx_, n_samples, n_bins);
807778
}
808779
}
809780

0 commit comments

Comments
 (0)