Files
lightgbm-org--lightgbm/src/io/cuda/cuda_row_data.cpp
T
2026-07-13 13:27:18 +08:00

439 lines
20 KiB
C++

/*!
* Copyright (c) 2021-2026 Microsoft Corporation. All rights reserved.
* Copyright (c) 2021-2026 The LightGBM developers. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*/
#ifdef USE_CUDA
#include <LightGBM/cuda/cuda_row_data.hpp>
#include <vector>
namespace LightGBM {
CUDARowData::CUDARowData(const Dataset* train_data,
const TrainingShareStates* train_share_state,
const int gpu_device_id,
const bool gpu_use_dp):
gpu_device_id_(gpu_device_id),
gpu_use_dp_(gpu_use_dp) {
num_threads_ = OMP_NUM_THREADS();
num_data_ = train_data->num_data();
const auto& feature_hist_offsets = train_share_state->feature_hist_offsets();
if (gpu_use_dp_) {
shared_hist_size_ = DP_SHARED_HIST_SIZE;
} else {
shared_hist_size_ = SP_SHARED_HIST_SIZE;
}
if (feature_hist_offsets.empty()) {
num_total_bin_ = 0;
} else {
num_total_bin_ = static_cast<int>(feature_hist_offsets.back());
}
num_feature_group_ = train_data->num_feature_groups();
num_feature_ = train_data->num_features();
if (gpu_device_id >= 0) {
SetCUDADevice(gpu_device_id, __FILE__, __LINE__);
} else {
SetCUDADevice(0, __FILE__, __LINE__);
}
}
CUDARowData::~CUDARowData() {}
void CUDARowData::Init(const Dataset* train_data, TrainingShareStates* train_share_state) {
if (num_feature_ == 0) {
return;
}
DivideCUDAFeatureGroups(train_data, train_share_state);
bit_type_ = 0;
size_t total_size = 0;
const void* host_row_ptr = nullptr;
row_ptr_bit_type_ = 0;
const void* host_data = train_share_state->GetRowWiseData(&bit_type_, &total_size, &is_sparse_, &host_row_ptr, &row_ptr_bit_type_);
if (bit_type_ == 8) {
if (!is_sparse_) {
std::vector<uint8_t> partitioned_data;
GetDenseDataPartitioned<uint8_t>(reinterpret_cast<const uint8_t*>(host_data), &partitioned_data);
cuda_data_uint8_t_.InitFromHostVector(partitioned_data);
} else {
if (row_ptr_bit_type_ == 16) {
InitSparseData<uint8_t, uint16_t>(
reinterpret_cast<const uint8_t*>(host_data),
reinterpret_cast<const uint16_t*>(host_row_ptr),
&cuda_data_uint8_t_,
&cuda_row_ptr_uint16_t_,
&cuda_partition_ptr_uint16_t_);
} else if (row_ptr_bit_type_ == 32) {
InitSparseData<uint8_t, uint32_t>(
reinterpret_cast<const uint8_t*>(host_data),
reinterpret_cast<const uint32_t*>(host_row_ptr),
&cuda_data_uint8_t_,
&cuda_row_ptr_uint32_t_,
&cuda_partition_ptr_uint32_t_);
} else if (row_ptr_bit_type_ == 64) {
InitSparseData<uint8_t, uint64_t>(
reinterpret_cast<const uint8_t*>(host_data),
reinterpret_cast<const uint64_t*>(host_row_ptr),
&cuda_data_uint8_t_,
&cuda_row_ptr_uint64_t_,
&cuda_partition_ptr_uint64_t_);
} else {
Log::Fatal("Unknown data ptr bit type %d", row_ptr_bit_type_);
}
}
} else if (bit_type_ == 16) {
if (!is_sparse_) {
std::vector<uint16_t> partitioned_data;
GetDenseDataPartitioned<uint16_t>(reinterpret_cast<const uint16_t*>(host_data), &partitioned_data);
cuda_data_uint16_t_.InitFromHostVector(partitioned_data);
} else {
if (row_ptr_bit_type_ == 16) {
InitSparseData<uint16_t, uint16_t>(
reinterpret_cast<const uint16_t*>(host_data),
reinterpret_cast<const uint16_t*>(host_row_ptr),
&cuda_data_uint16_t_,
&cuda_row_ptr_uint16_t_,
&cuda_partition_ptr_uint16_t_);
} else if (row_ptr_bit_type_ == 32) {
InitSparseData<uint16_t, uint32_t>(
reinterpret_cast<const uint16_t*>(host_data),
reinterpret_cast<const uint32_t*>(host_row_ptr),
&cuda_data_uint16_t_,
&cuda_row_ptr_uint32_t_,
&cuda_partition_ptr_uint32_t_);
} else if (row_ptr_bit_type_ == 64) {
InitSparseData<uint16_t, uint64_t>(
reinterpret_cast<const uint16_t*>(host_data),
reinterpret_cast<const uint64_t*>(host_row_ptr),
&cuda_data_uint16_t_,
&cuda_row_ptr_uint64_t_,
&cuda_partition_ptr_uint64_t_);
} else {
Log::Fatal("Unknown data ptr bit type %d", row_ptr_bit_type_);
}
}
} else if (bit_type_ == 32) {
if (!is_sparse_) {
std::vector<uint32_t> partitioned_data;
GetDenseDataPartitioned<uint32_t>(reinterpret_cast<const uint32_t*>(host_data), &partitioned_data);
cuda_data_uint32_t_.InitFromHostVector(partitioned_data);
} else {
if (row_ptr_bit_type_ == 16) {
InitSparseData<uint32_t, uint16_t>(
reinterpret_cast<const uint32_t*>(host_data),
reinterpret_cast<const uint16_t*>(host_row_ptr),
&cuda_data_uint32_t_,
&cuda_row_ptr_uint16_t_,
&cuda_partition_ptr_uint16_t_);
} else if (row_ptr_bit_type_ == 32) {
InitSparseData<uint32_t, uint32_t>(
reinterpret_cast<const uint32_t*>(host_data),
reinterpret_cast<const uint32_t*>(host_row_ptr),
&cuda_data_uint32_t_,
&cuda_row_ptr_uint32_t_,
&cuda_partition_ptr_uint32_t_);
} else if (row_ptr_bit_type_ == 64) {
InitSparseData<uint32_t, uint64_t>(
reinterpret_cast<const uint32_t*>(host_data),
reinterpret_cast<const uint64_t*>(host_row_ptr),
&cuda_data_uint32_t_,
&cuda_row_ptr_uint64_t_,
&cuda_partition_ptr_uint64_t_);
} else {
Log::Fatal("Unknown data ptr bit type %d", row_ptr_bit_type_);
}
}
} else {
Log::Fatal("Unknown bit type = %d", bit_type_);
}
SynchronizeCUDADevice(__FILE__, __LINE__);
}
void CUDARowData::DivideCUDAFeatureGroups(const Dataset* train_data, TrainingShareStates* share_state) {
const uint32_t max_num_bin_per_partition = shared_hist_size_ / 2;
const std::vector<uint32_t>& column_hist_offsets = share_state->column_hist_offsets();
std::vector<int> feature_group_num_feature_offsets;
int offsets = 0;
int prev_group_index = -1;
for (int feature_index = 0; feature_index < num_feature_; ++feature_index) {
const int feature_group_index = train_data->Feature2Group(feature_index);
if (prev_group_index == -1 || feature_group_index != prev_group_index) {
feature_group_num_feature_offsets.emplace_back(offsets);
prev_group_index = feature_group_index;
}
++offsets;
}
CHECK_EQ(offsets, num_feature_);
feature_group_num_feature_offsets.emplace_back(offsets);
uint32_t start_hist_offset = 0;
feature_partition_column_index_offsets_.clear();
column_hist_offsets_.clear();
partition_hist_offsets_.clear();
feature_partition_column_index_offsets_.emplace_back(0);
partition_hist_offsets_.emplace_back(0);
const int num_feature_groups = train_data->num_feature_groups();
int column_index = 0;
num_feature_partitions_ = 0;
large_bin_partitions_.clear();
small_bin_partitions_.clear();
for (int feature_group_index = 0; feature_group_index < num_feature_groups; ++feature_group_index) {
if (!train_data->IsMultiGroup(feature_group_index)) {
const uint32_t column_feature_hist_start = column_hist_offsets[column_index];
const uint32_t column_feature_hist_end = column_hist_offsets[column_index + 1];
const uint32_t num_bin_in_dense_group = column_feature_hist_end - column_feature_hist_start;
// if one column has too many bins, use a separate partition for that column
if (num_bin_in_dense_group > max_num_bin_per_partition) {
feature_partition_column_index_offsets_.emplace_back(column_index + 1);
start_hist_offset = column_feature_hist_end;
partition_hist_offsets_.emplace_back(start_hist_offset);
large_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
column_hist_offsets_.emplace_back(0);
++column_index;
continue;
}
// try if adding this column exceed the maximum number per partition
const uint32_t cur_hist_num_bin = column_feature_hist_end - start_hist_offset;
if (cur_hist_num_bin > max_num_bin_per_partition) {
feature_partition_column_index_offsets_.emplace_back(column_index);
start_hist_offset = column_feature_hist_start;
partition_hist_offsets_.emplace_back(start_hist_offset);
small_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
}
column_hist_offsets_.emplace_back(column_hist_offsets[column_index] - start_hist_offset);
if (feature_group_index == num_feature_groups - 1) {
feature_partition_column_index_offsets_.emplace_back(column_index + 1);
partition_hist_offsets_.emplace_back(column_hist_offsets.back());
small_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
}
++column_index;
} else {
const int group_feature_index_start = feature_group_num_feature_offsets[feature_group_index];
const int num_feature_in_group = feature_group_num_feature_offsets[feature_group_index + 1] - group_feature_index_start;
for (int sub_feature_index = 0; sub_feature_index < num_feature_in_group; ++sub_feature_index) {
const int feature_index = group_feature_index_start + sub_feature_index;
const uint32_t column_feature_hist_start = column_hist_offsets[column_index];
const uint32_t column_feature_hist_end = column_hist_offsets[column_index + 1];
const uint32_t num_bin_in_dense_group = column_feature_hist_end - column_feature_hist_start;
// if one column has too many bins, use a separate partition for that column
if (num_bin_in_dense_group > max_num_bin_per_partition) {
feature_partition_column_index_offsets_.emplace_back(column_index + 1);
start_hist_offset = column_feature_hist_end;
partition_hist_offsets_.emplace_back(start_hist_offset);
large_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
column_hist_offsets_.emplace_back(0);
++column_index;
continue;
}
// try if adding this column exceed the maximum number per partition
const uint32_t cur_hist_num_bin = column_feature_hist_end - start_hist_offset;
if (cur_hist_num_bin > max_num_bin_per_partition) {
feature_partition_column_index_offsets_.emplace_back(column_index);
start_hist_offset = column_feature_hist_start;
partition_hist_offsets_.emplace_back(start_hist_offset);
small_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
}
column_hist_offsets_.emplace_back(column_hist_offsets[column_index] - start_hist_offset);
if (feature_group_index == num_feature_groups - 1 && sub_feature_index == num_feature_in_group - 1) {
CHECK_EQ(feature_index, num_feature_ - 1);
feature_partition_column_index_offsets_.emplace_back(column_index + 1);
partition_hist_offsets_.emplace_back(column_hist_offsets.back());
small_bin_partitions_.emplace_back(num_feature_partitions_);
++num_feature_partitions_;
}
++column_index;
}
}
}
column_hist_offsets_.emplace_back(column_hist_offsets.back() - start_hist_offset);
max_num_column_per_partition_ = 0;
for (size_t i = 0; i < feature_partition_column_index_offsets_.size() - 1; ++i) {
const int num_column = feature_partition_column_index_offsets_[i + 1] - feature_partition_column_index_offsets_[i];
if (num_column > max_num_column_per_partition_) {
max_num_column_per_partition_ = num_column;
}
}
cuda_feature_partition_column_index_offsets_.InitFromHostVector(feature_partition_column_index_offsets_);
cuda_column_hist_offsets_.InitFromHostVector(column_hist_offsets_);
cuda_partition_hist_offsets_.InitFromHostVector(partition_hist_offsets_);
}
template <typename BIN_TYPE>
void CUDARowData::GetDenseDataPartitioned(const BIN_TYPE* row_wise_data, std::vector<BIN_TYPE>* partitioned_data) {
const int num_total_columns = feature_partition_column_index_offsets_.back();
partitioned_data->resize(static_cast<size_t>(num_total_columns) * static_cast<size_t>(num_data_), 0);
BIN_TYPE* out_data = partitioned_data->data();
Threading::For<data_size_t>(0, num_data_, 512,
[this, num_total_columns, row_wise_data, out_data] (int /*thread_index*/, data_size_t start, data_size_t end) {
for (size_t i = 0; i < feature_partition_column_index_offsets_.size() - 1; ++i) {
const int num_prev_columns = static_cast<int>(feature_partition_column_index_offsets_[i]);
const size_t offset = static_cast<size_t>(num_data_) * static_cast<size_t>(num_prev_columns);
const int partition_column_start = feature_partition_column_index_offsets_[i];
const int partition_column_end = feature_partition_column_index_offsets_[i + 1];
const int num_columns_in_cur_partition = partition_column_end - partition_column_start;
for (data_size_t data_index = start; data_index < end; ++data_index) {
const size_t data_offset = offset + static_cast<size_t>(data_index) * num_columns_in_cur_partition;
const size_t read_data_offset = static_cast<size_t>(data_index) * num_total_columns;
for (int column_index = 0; column_index < num_columns_in_cur_partition; ++column_index) {
const size_t true_column_index = read_data_offset + column_index + partition_column_start;
const BIN_TYPE bin = row_wise_data[true_column_index];
out_data[data_offset + column_index] = bin;
}
}
}
});
}
template <typename BIN_TYPE, typename DATA_PTR_TYPE>
void CUDARowData::GetSparseDataPartitioned(
const BIN_TYPE* row_wise_data,
const DATA_PTR_TYPE* row_ptr,
std::vector<std::vector<BIN_TYPE>>* partitioned_data,
std::vector<std::vector<DATA_PTR_TYPE>>* partitioned_row_ptr,
std::vector<DATA_PTR_TYPE>* partition_ptr) {
const int num_partitions = static_cast<int>(feature_partition_column_index_offsets_.size()) - 1;
partitioned_data->resize(num_partitions);
partitioned_row_ptr->resize(num_partitions);
std::vector<int> thread_max_elements_per_row(num_threads_, 0);
Threading::For<int>(0, num_partitions, 1,
[partitioned_data, partitioned_row_ptr, row_ptr, row_wise_data, &thread_max_elements_per_row, this] (int thread_index, int start, int end) {
for (int partition_index = start; partition_index < end; ++partition_index) {
std::vector<BIN_TYPE>& data_for_this_partition = partitioned_data->at(partition_index);
std::vector<DATA_PTR_TYPE>& row_ptr_for_this_partition = partitioned_row_ptr->at(partition_index);
const int partition_hist_start = partition_hist_offsets_[partition_index];
const int partition_hist_end = partition_hist_offsets_[partition_index + 1];
DATA_PTR_TYPE offset = 0;
row_ptr_for_this_partition.clear();
data_for_this_partition.clear();
row_ptr_for_this_partition.emplace_back(offset);
for (data_size_t data_index = 0; data_index < num_data_; ++data_index) {
const DATA_PTR_TYPE row_start = row_ptr[data_index];
const DATA_PTR_TYPE row_end = row_ptr[data_index + 1];
const BIN_TYPE* row_data_start = row_wise_data + row_start;
const BIN_TYPE* row_data_end = row_wise_data + row_end;
const size_t partition_start_in_row = std::lower_bound(row_data_start, row_data_end, partition_hist_start) - row_data_start;
const size_t partition_end_in_row = std::lower_bound(row_data_start, row_data_end, partition_hist_end) - row_data_start;
for (size_t pos = partition_start_in_row; pos < partition_end_in_row; ++pos) {
const BIN_TYPE bin = row_data_start[pos];
CHECK_GE(bin, static_cast<BIN_TYPE>(partition_hist_start));
data_for_this_partition.emplace_back(bin - partition_hist_start);
}
CHECK_GE(partition_end_in_row, partition_start_in_row);
const data_size_t num_elements_in_row = partition_end_in_row - partition_start_in_row;
offset += static_cast<DATA_PTR_TYPE>(num_elements_in_row);
row_ptr_for_this_partition.emplace_back(offset);
if (num_elements_in_row > thread_max_elements_per_row[thread_index]) {
thread_max_elements_per_row[thread_index] = num_elements_in_row;
}
}
}
});
partition_ptr->clear();
DATA_PTR_TYPE offset = 0;
partition_ptr->emplace_back(offset);
for (size_t i = 0; i < partitioned_row_ptr->size(); ++i) {
offset += partitioned_row_ptr->at(i).back();
partition_ptr->emplace_back(offset);
}
max_num_column_per_partition_ = 0;
for (int thread_index = 0; thread_index < num_threads_; ++thread_index) {
if (thread_max_elements_per_row[thread_index] > max_num_column_per_partition_) {
max_num_column_per_partition_ = thread_max_elements_per_row[thread_index];
}
}
}
template <typename BIN_TYPE, typename ROW_PTR_TYPE>
void CUDARowData::InitSparseData(const BIN_TYPE* host_data,
const ROW_PTR_TYPE* host_row_ptr,
CUDAVector<BIN_TYPE>* cuda_data,
CUDAVector<ROW_PTR_TYPE>* cuda_row_ptr,
CUDAVector<ROW_PTR_TYPE>* cuda_partition_ptr) {
std::vector<std::vector<BIN_TYPE>> partitioned_data;
std::vector<std::vector<ROW_PTR_TYPE>> partitioned_data_ptr;
std::vector<ROW_PTR_TYPE> partition_ptr;
GetSparseDataPartitioned<BIN_TYPE, ROW_PTR_TYPE>(host_data, host_row_ptr, &partitioned_data, &partitioned_data_ptr, &partition_ptr);
cuda_partition_ptr->InitFromHostVector(partition_ptr);
cuda_data->Resize(partition_ptr.back());
cuda_row_ptr->Resize((num_data_ + 1) * partitioned_data_ptr.size());
for (size_t i = 0; i < partitioned_data.size(); ++i) {
const std::vector<ROW_PTR_TYPE>& data_ptr_for_this_partition = partitioned_data_ptr[i];
const std::vector<BIN_TYPE>& data_for_this_partition = partitioned_data[i];
CopyFromHostToCUDADevice<BIN_TYPE>(cuda_data->RawData() + partition_ptr[i], data_for_this_partition.data(), data_for_this_partition.size(), __FILE__, __LINE__);
CopyFromHostToCUDADevice<ROW_PTR_TYPE>(cuda_row_ptr->RawData() + i * (num_data_ + 1), data_ptr_for_this_partition.data(), data_ptr_for_this_partition.size(), __FILE__, __LINE__);
}
}
template <typename BIN_TYPE>
const BIN_TYPE* CUDARowData::GetBin() const {
if (bit_type_ == 8) {
return reinterpret_cast<const BIN_TYPE*>(cuda_data_uint8_t_.RawData());
} else if (bit_type_ == 16) {
return reinterpret_cast<const BIN_TYPE*>(cuda_data_uint16_t_.RawData());
} else if (bit_type_ == 32) {
return reinterpret_cast<const BIN_TYPE*>(cuda_data_uint32_t_.RawData());
} else {
Log::Fatal("Unknown bit_type %d for GetBin.", bit_type_);
}
}
template const uint8_t* CUDARowData::GetBin<uint8_t>() const;
template const uint16_t* CUDARowData::GetBin<uint16_t>() const;
template const uint32_t* CUDARowData::GetBin<uint32_t>() const;
template <typename PTR_TYPE>
const PTR_TYPE* CUDARowData::GetRowPtr() const {
if (row_ptr_bit_type_ == 16) {
return reinterpret_cast<const PTR_TYPE*>(cuda_row_ptr_uint16_t_.RawData());
} else if (row_ptr_bit_type_ == 32) {
return reinterpret_cast<const PTR_TYPE*>(cuda_row_ptr_uint32_t_.RawData());
} else if (row_ptr_bit_type_ == 64) {
return reinterpret_cast<const PTR_TYPE*>(cuda_row_ptr_uint64_t_.RawData());
} else {
Log::Fatal("Unknown row_ptr_bit_type = %d for GetRowPtr.", row_ptr_bit_type_);
}
}
template const uint16_t* CUDARowData::GetRowPtr<uint16_t>() const;
template const uint32_t* CUDARowData::GetRowPtr<uint32_t>() const;
template const uint64_t* CUDARowData::GetRowPtr<uint64_t>() const;
template <typename PTR_TYPE>
const PTR_TYPE* CUDARowData::GetPartitionPtr() const {
if (row_ptr_bit_type_ == 16) {
return reinterpret_cast<const PTR_TYPE*>(cuda_partition_ptr_uint16_t_.RawData());
} else if (row_ptr_bit_type_ == 32) {
return reinterpret_cast<const PTR_TYPE*>(cuda_partition_ptr_uint32_t_.RawData());
} else if (row_ptr_bit_type_ == 64) {
return reinterpret_cast<const PTR_TYPE*>(cuda_partition_ptr_uint64_t_.RawData());
} else {
Log::Fatal("Unknown row_ptr_bit_type = %d for GetPartitionPtr.", row_ptr_bit_type_);
}
}
template const uint16_t* CUDARowData::GetPartitionPtr<uint16_t>() const;
template const uint32_t* CUDARowData::GetPartitionPtr<uint32_t>() const;
template const uint64_t* CUDARowData::GetPartitionPtr<uint64_t>() const;
} // namespace LightGBM
#endif // USE_CUDA