From d8059f472e05f5fcd77e4ce115d2ec170525a400 Mon Sep 17 00:00:00 2001 From: fis Date: Tue, 20 Jul 2021 21:03:13 +0800 Subject: [PATCH] Empty dataset handling. --- src/data/data.cu | 14 ++++++++++++-- src/data/device_adapter.cuh | 6 +++--- src/data/ellpack_page_source.cu | 1 + src/data/proxy_dmatrix.cu | 6 ++++++ src/data/simple_dmatrix.cu | 9 ++++++--- src/data/sparse_page_source.cu | 5 +++++ tests/python/test_data_iterator.py | 6 +++--- 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/data/data.cu b/src/data/data.cu index a9397803ce1f..de8a8c248083 100644 --- a/src/data/data.cu +++ b/src/data/data.cu @@ -19,11 +19,16 @@ void CopyInfoImpl(ArrayInterface column, HostDeviceVector* out) { cudaPointerAttributes attr; dh::safe_cuda(cudaPointerGetAttributes(&attr, ptr)); int32_t ptr_device = attr.device; - dh::safe_cuda(cudaSetDevice(ptr_device)); + if (ptr_device >= 0) { + dh::safe_cuda(cudaSetDevice(ptr_device)); + } return ptr_device; }; auto ptr_device = SetDeviceToPtr(column.data); + if (column.num_rows == 0) { + return; + } out->SetDevice(ptr_device); out->Resize(column.num_rows); @@ -123,7 +128,12 @@ void MetaInfo::SetInfo(const char * c_key, std::string const& interface_str) { << "MetaInfo: " << c_key << ". " << ArrayInterfaceErrors::Dimension(1); ArrayInterface array_interface(interface_str); std::string key{c_key}; - array_interface.AsColumnVector(); + if (!((array_interface.num_cols == 1 && array_interface.num_rows == 0) || + (array_interface.num_cols == 0 && array_interface.num_rows == 1))) { + // Not an empty column, transform it. + array_interface.AsColumnVector(); + } + CHECK(!array_interface.valid.Data()) << "Meta info " << key << " should be dense, found validity mask"; if (array_interface.num_rows == 0) { diff --git a/src/data/device_adapter.cuh b/src/data/device_adapter.cuh index a772a064f9b7..2da786969fcf 100644 --- a/src/data/device_adapter.cuh +++ b/src/data/device_adapter.cuh @@ -154,7 +154,7 @@ class CudfAdapter : public detail::SingleBatchDataIter { size_t NumRows() const { return num_rows_; } size_t NumColumns() const { return columns_.size(); } - size_t DeviceIdx() const { return device_idx_; } + int32_t DeviceIdx() const { return device_idx_; } private: CudfAdapterBatch batch_; @@ -202,12 +202,12 @@ class CupyAdapter : public detail::SingleBatchDataIter { size_t NumRows() const { return array_interface_.num_rows; } size_t NumColumns() const { return array_interface_.num_cols; } - size_t DeviceIdx() const { return device_idx_; } + int32_t DeviceIdx() const { return device_idx_; } private: ArrayInterface array_interface_; CupyAdapterBatch batch_; - int device_idx_; + int32_t device_idx_ {-1}; }; // Returns maximum row length diff --git a/src/data/ellpack_page_source.cu b/src/data/ellpack_page_source.cu index 115d593e126c..6d79250a0a63 100644 --- a/src/data/ellpack_page_source.cu +++ b/src/data/ellpack_page_source.cu @@ -10,6 +10,7 @@ namespace xgboost { namespace data { void EllpackPageSource::Fetch() { + dh::safe_cuda(cudaSetDevice(param_.gpu_id)); if (!this->ReadCache()) { auto const &csr = source_->Page(); this->page_.reset(new EllpackPage{}); diff --git a/src/data/proxy_dmatrix.cu b/src/data/proxy_dmatrix.cu index adad6f4c4d79..6fbd721007d0 100644 --- a/src/data/proxy_dmatrix.cu +++ b/src/data/proxy_dmatrix.cu @@ -14,6 +14,9 @@ void DMatrixProxy::FromCudaColumnar(std::string interface_str) { device_ = adapter->DeviceIdx(); this->Info().num_col_ = adapter->NumColumns(); this->Info().num_row_ = adapter->NumRows(); + if (device_ < 0) { + CHECK_EQ(this->Info().num_row_, 0); + } } void DMatrixProxy::FromCudaArray(std::string interface_str) { @@ -22,6 +25,9 @@ void DMatrixProxy::FromCudaArray(std::string interface_str) { device_ = adapter->DeviceIdx(); this->Info().num_col_ = adapter->NumColumns(); this->Info().num_row_ = adapter->NumRows(); + if (device_ < 0) { + CHECK_EQ(this->Info().num_row_, 0); + } } } // namespace data diff --git a/src/data/simple_dmatrix.cu b/src/data/simple_dmatrix.cu index 80de6706c3a1..e770d4aa2849 100644 --- a/src/data/simple_dmatrix.cu +++ b/src/data/simple_dmatrix.cu @@ -16,7 +16,10 @@ namespace data { // be supported in future. Does not currently support inferring row/column size template SimpleDMatrix::SimpleDMatrix(AdapterT* adapter, float missing, int nthread) { - dh::safe_cuda(cudaSetDevice(adapter->DeviceIdx())); + auto device = + adapter->DeviceIdx() < 0 ? dh::CurrentDevice() : adapter->DeviceIdx(); + CHECK_GE(device, 0); + dh::safe_cuda(cudaSetDevice(device)); CHECK(adapter->NumRows() != kAdapterUnknownSize); CHECK(adapter->NumColumns() != kAdapterUnknownSize); @@ -27,8 +30,8 @@ SimpleDMatrix::SimpleDMatrix(AdapterT* adapter, float missing, int nthread) { // Enforce single batch CHECK(!adapter->Next()); - info_.num_nonzero_ = CopyToSparsePage(adapter->Value(), adapter->DeviceIdx(), - missing, sparse_page_.get()); + info_.num_nonzero_ = + CopyToSparsePage(adapter->Value(), device, missing, sparse_page_.get()); info_.num_col_ = adapter->NumColumns(); info_.num_row_ = adapter->NumRows(); // Synchronise worker columns diff --git a/src/data/sparse_page_source.cu b/src/data/sparse_page_source.cu index 9edaae797c22..bcadffaff41f 100644 --- a/src/data/sparse_page_source.cu +++ b/src/data/sparse_page_source.cu @@ -20,6 +20,11 @@ size_t NFeaturesDevice(DMatrixProxy *proxy) { 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); }); diff --git a/tests/python/test_data_iterator.py b/tests/python/test_data_iterator.py index f6ce46c21f62..58df8eb93f2a 100644 --- a/tests/python/test_data_iterator.py +++ b/tests/python/test_data_iterator.py @@ -57,12 +57,12 @@ def run_data_iterator( n_features: int, n_batches: int, tree_method: str, - cupy: bool, + use_cupy: bool, ) -> None: n_rounds = 2 it = IteratorForTest( - *make_batches(n_samples_per_batch, n_features, n_batches, cupy) + *make_batches(n_samples_per_batch, n_features, n_batches, use_cupy) ) if n_batches == 0: with pytest.raises(ValueError, match="1 batch"): @@ -103,8 +103,8 @@ def run_data_iterator( if tree_method != "gpu_hist": rtol = 1e-1 # flaky else: + np.testing.assert_allclose(it_predt, arr_predt, rtol=1e-3) rtol = 1e-6 - np.testing.assert_allclose(it_predt, arr_predt) np.testing.assert_allclose( results_from_it["Train"]["rmse"],