Optimize adapter element counting on GPU. (#9209)
- Implement a simple `IterSpan` for passing iterators with size. - Use shared memory for column size counts. - Use one thread for each sample in row count to reduce atomic operations.
This commit is contained in:
@@ -483,6 +483,73 @@ TEST(HistUtil, AdapterDeviceSketchBatches) {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
auto MakeData(Context const* ctx, std::size_t n_samples, bst_feature_t n_features) {
|
||||
dh::safe_cuda(cudaSetDevice(ctx->gpu_id));
|
||||
auto n = n_samples * n_features;
|
||||
std::vector<float> x;
|
||||
x.resize(n);
|
||||
|
||||
std::iota(x.begin(), x.end(), 0);
|
||||
std::int32_t c{0};
|
||||
float missing = n_samples * n_features;
|
||||
for (std::size_t i = 0; i < x.size(); ++i) {
|
||||
if (i % 5 == 0) {
|
||||
x[i] = missing;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
thrust::device_vector<float> d_x;
|
||||
d_x = x;
|
||||
|
||||
auto n_invalids = n / 10 * 2 + 1;
|
||||
auto is_valid = data::IsValidFunctor{missing};
|
||||
return std::tuple{x, d_x, n_invalids, is_valid};
|
||||
}
|
||||
|
||||
void TestGetColumnSize(std::size_t n_samples) {
|
||||
auto ctx = MakeCUDACtx(0);
|
||||
bst_feature_t n_features = 12;
|
||||
[[maybe_unused]] auto [x, d_x, n_invalids, is_valid] = MakeData(&ctx, n_samples, n_features);
|
||||
|
||||
auto adapter = AdapterFromData(d_x, n_samples, n_features);
|
||||
auto batch = adapter.Value();
|
||||
|
||||
auto batch_iter = dh::MakeTransformIterator<data::COOTuple>(
|
||||
thrust::make_counting_iterator(0llu),
|
||||
[=] __device__(std::size_t idx) { return batch.GetElement(idx); });
|
||||
|
||||
dh::caching_device_vector<std::size_t> column_sizes_scan;
|
||||
column_sizes_scan.resize(n_features + 1);
|
||||
std::vector<std::size_t> h_column_size(column_sizes_scan.size());
|
||||
std::vector<std::size_t> h_column_size_1(column_sizes_scan.size());
|
||||
|
||||
detail::LaunchGetColumnSizeKernel<decltype(batch_iter), true, true>(
|
||||
ctx.gpu_id, IterSpan{batch_iter, batch.Size()}, is_valid, dh::ToSpan(column_sizes_scan));
|
||||
thrust::copy(column_sizes_scan.begin(), column_sizes_scan.end(), h_column_size.begin());
|
||||
|
||||
detail::LaunchGetColumnSizeKernel<decltype(batch_iter), true, false>(
|
||||
ctx.gpu_id, IterSpan{batch_iter, batch.Size()}, is_valid, dh::ToSpan(column_sizes_scan));
|
||||
thrust::copy(column_sizes_scan.begin(), column_sizes_scan.end(), h_column_size_1.begin());
|
||||
ASSERT_EQ(h_column_size, h_column_size_1);
|
||||
|
||||
detail::LaunchGetColumnSizeKernel<decltype(batch_iter), false, true>(
|
||||
ctx.gpu_id, IterSpan{batch_iter, batch.Size()}, is_valid, dh::ToSpan(column_sizes_scan));
|
||||
thrust::copy(column_sizes_scan.begin(), column_sizes_scan.end(), h_column_size_1.begin());
|
||||
ASSERT_EQ(h_column_size, h_column_size_1);
|
||||
|
||||
detail::LaunchGetColumnSizeKernel<decltype(batch_iter), false, false>(
|
||||
ctx.gpu_id, IterSpan{batch_iter, batch.Size()}, is_valid, dh::ToSpan(column_sizes_scan));
|
||||
thrust::copy(column_sizes_scan.begin(), column_sizes_scan.end(), h_column_size_1.begin());
|
||||
ASSERT_EQ(h_column_size, h_column_size_1);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(HistUtil, GetColumnSize) {
|
||||
bst_row_t n_samples = 4096;
|
||||
TestGetColumnSize(n_samples);
|
||||
}
|
||||
|
||||
// Check sketching from adapter or DMatrix results in the same answer
|
||||
// Consistency here is useful for testing and user experience
|
||||
TEST(HistUtil, SketchingEquivalent) {
|
||||
|
||||
@@ -50,7 +50,7 @@ void TestSketchUnique(float sparsity) {
|
||||
thrust::make_counting_iterator(0llu),
|
||||
[=] __device__(size_t idx) { return batch.GetElement(idx); });
|
||||
auto end = kCols * kRows;
|
||||
detail::GetColumnSizesScan(0, kCols, n_cuts, batch_iter, is_valid, 0, end,
|
||||
detail::GetColumnSizesScan(0, kCols, n_cuts, IterSpan{batch_iter, end}, is_valid,
|
||||
&cut_sizes_scan, &column_sizes_scan);
|
||||
auto const& cut_sizes = cut_sizes_scan.HostVector();
|
||||
ASSERT_LE(sketch.Data().size(), cut_sizes.back());
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/*!
|
||||
* Copyright 2018 XGBoost contributors
|
||||
/**
|
||||
* Copyright 2018-2023, XGBoost contributors
|
||||
*/
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
|
||||
#include <xgboost/span.h>
|
||||
#include "test_span.h"
|
||||
|
||||
namespace xgboost {
|
||||
namespace common {
|
||||
#include <gtest/gtest.h>
|
||||
#include <xgboost/span.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../../../src/common/transform_iterator.h" // for MakeIndexTransformIter
|
||||
|
||||
namespace xgboost::common {
|
||||
TEST(Span, TestStatus) {
|
||||
int status = 1;
|
||||
TestTestStatus {&status}();
|
||||
@@ -526,5 +527,17 @@ TEST(SpanDeathTest, Empty) {
|
||||
Span<float> s{data.data(), static_cast<Span<float>::index_type>(0)};
|
||||
EXPECT_DEATH(s[0], ""); // not ok to use it.
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace xgboost
|
||||
|
||||
TEST(IterSpan, Basic) {
|
||||
auto iter = common::MakeIndexTransformIter([](std::size_t i) { return i; });
|
||||
std::size_t n = 13;
|
||||
auto span = IterSpan{iter, n};
|
||||
ASSERT_EQ(span.size(), n);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
ASSERT_EQ(span[i], i);
|
||||
}
|
||||
ASSERT_EQ(span.subspan(1).size(), n - 1);
|
||||
ASSERT_EQ(span.subspan(1)[0], 1);
|
||||
ASSERT_EQ(span.subspan(1, 2)[1], 2);
|
||||
}
|
||||
} // namespace xgboost::common
|
||||
|
||||
@@ -51,3 +51,22 @@ void TestCudfAdapter()
|
||||
TEST(DeviceAdapter, CudfAdapter) {
|
||||
TestCudfAdapter();
|
||||
}
|
||||
|
||||
namespace xgboost::data {
|
||||
TEST(DeviceAdapter, GetRowCounts) {
|
||||
auto ctx = MakeCUDACtx(0);
|
||||
|
||||
for (bst_feature_t n_features : {1, 2, 4, 64, 128, 256}) {
|
||||
HostDeviceVector<float> storage;
|
||||
auto str_arr = RandomDataGenerator{8192, n_features, 0.0}
|
||||
.Device(ctx.gpu_id)
|
||||
.GenerateArrayInterface(&storage);
|
||||
auto adapter = CupyAdapter{str_arr};
|
||||
HostDeviceVector<bst_row_t> offset(adapter.NumRows() + 1, 0);
|
||||
offset.SetDevice(ctx.gpu_id);
|
||||
auto rstride = GetRowCounts(adapter.Value(), offset.DeviceSpan(), ctx.gpu_id,
|
||||
std::numeric_limits<float>::quiet_NaN());
|
||||
ASSERT_EQ(rstride, n_features);
|
||||
}
|
||||
}
|
||||
} // namespace xgboost::data
|
||||
|
||||
Reference in New Issue
Block a user