Files
2026-07-13 13:27:18 +08:00

368 lines
14 KiB
C++

/*!
* Copyright (c) 2016-2026 Microsoft Corporation. All rights reserved.
* Copyright (c) 2016-2026 The LightGBM developers. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*/
#ifndef LIGHTGBM_INCLUDE_LIGHTGBM_TRAIN_SHARE_STATES_H_
#define LIGHTGBM_INCLUDE_LIGHTGBM_TRAIN_SHARE_STATES_H_
#include <LightGBM/bin.h>
#include <LightGBM/feature_group.h>
#include <LightGBM/meta.h>
#include <LightGBM/utils/threading.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <vector>
namespace LightGBM {
class MultiValBinWrapper {
public:
MultiValBinWrapper(MultiValBin* bin, data_size_t num_data,
const std::vector<int>& feature_groups_contained, const int num_grad_quant_bins);
bool IsSparse() const {
if (multi_val_bin_ != nullptr) {
return multi_val_bin_->IsSparse();
}
return false;
}
void InitTrain(const std::vector<int>& group_feature_start,
const std::vector<std::unique_ptr<FeatureGroup>>& feature_groups,
const std::vector<int8_t>& is_feature_used,
const data_size_t* bagging_use_indices,
data_size_t bagging_indices_cnt);
template <bool USE_QUANT_GRAD, int HIST_BITS, int INNER_HIST_BITS>
void HistMove(const std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>>& hist_buf);
template <bool USE_QUANT_GRAD, int HIST_BITS, int INNER_HIST_BITS>
void HistMerge(std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>>* hist_buf);
void ResizeHistBuf(std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>>* hist_buf,
MultiValBin* sub_multi_val_bin,
hist_t* origin_hist_data);
template <bool USE_INDICES, bool ORDERED, bool USE_QUANT_GRAD, int HIST_BITS>
void ConstructHistograms(const data_size_t* data_indices,
data_size_t num_data,
const score_t* gradients,
const score_t* hessians,
std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>>* hist_buf,
hist_t* origin_hist_data) {
const auto cur_multi_val_bin = (is_use_subcol_ || is_use_subrow_)
? multi_val_bin_subset_.get()
: multi_val_bin_.get();
if (cur_multi_val_bin != nullptr) {
global_timer.Start("Dataset::sparse_bin_histogram");
n_data_block_ = 1;
data_block_size_ = num_data;
Threading::BlockInfo<data_size_t>(num_threads_, num_data, min_block_size_,
&n_data_block_, &data_block_size_);
ResizeHistBuf(hist_buf, cur_multi_val_bin, origin_hist_data);
const int inner_hist_bits = (data_block_size_ * num_grad_quant_bins_ < 256 && HIST_BITS == 16) ? 8 : HIST_BITS;
OMP_INIT_EX();
#pragma omp parallel for schedule(static) num_threads(num_threads_)
for (int block_id = 0; block_id < n_data_block_; ++block_id) {
OMP_LOOP_EX_BEGIN();
data_size_t start = block_id * data_block_size_;
data_size_t end = std::min<data_size_t>(start + data_block_size_, num_data);
if (inner_hist_bits == 8) {
ConstructHistogramsForBlock<USE_INDICES, ORDERED, USE_QUANT_GRAD, 8>(
cur_multi_val_bin, start, end, data_indices, gradients, hessians,
block_id, hist_buf);
} else {
ConstructHistogramsForBlock<USE_INDICES, ORDERED, USE_QUANT_GRAD, HIST_BITS>(
cur_multi_val_bin, start, end, data_indices, gradients, hessians,
block_id, hist_buf);
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
global_timer.Stop("Dataset::sparse_bin_histogram");
global_timer.Start("Dataset::sparse_bin_histogram_merge");
if (inner_hist_bits == 8) {
HistMerge<USE_QUANT_GRAD, HIST_BITS, 8>(hist_buf);
} else {
HistMerge<USE_QUANT_GRAD, HIST_BITS, HIST_BITS>(hist_buf);
}
global_timer.Stop("Dataset::sparse_bin_histogram_merge");
global_timer.Start("Dataset::sparse_bin_histogram_move");
if (inner_hist_bits == 8) {
HistMove<USE_QUANT_GRAD, HIST_BITS, 8>(*hist_buf);
} else {
HistMove<USE_QUANT_GRAD, HIST_BITS, HIST_BITS>(*hist_buf);
}
global_timer.Stop("Dataset::sparse_bin_histogram_move");
}
}
template <bool USE_INDICES, bool ORDERED, bool USE_QUANT_GRAD, int HIST_BITS>
void ConstructHistogramsForBlock(const MultiValBin* sub_multi_val_bin,
data_size_t start, data_size_t end, const data_size_t* data_indices,
const score_t* gradients, const score_t* hessians, int block_id,
std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>>* hist_buf) {
if (USE_QUANT_GRAD) {
if (HIST_BITS == 8) {
int8_t* hist_buf_ptr = reinterpret_cast<int8_t*>(hist_buf->data());
int8_t* data_ptr = hist_buf_ptr +
static_cast<size_t>(num_bin_aligned_) * block_id * 2;
std::memset(reinterpret_cast<void*>(data_ptr), 0, num_bin_ * kInt8HistBufferEntrySize);
if (USE_INDICES) {
if (ORDERED) {
sub_multi_val_bin->ConstructHistogramOrderedInt8(data_indices, start, end,
gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
} else {
sub_multi_val_bin->ConstructHistogramInt8(data_indices, start, end, gradients,
hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
} else {
sub_multi_val_bin->ConstructHistogramInt8(start, end, gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
} else if (HIST_BITS == 16) {
int16_t* data_ptr = reinterpret_cast<int16_t*>(origin_hist_data_);
int16_t* hist_buf_ptr = reinterpret_cast<int16_t*>(hist_buf->data());
if (block_id == 0) {
if (is_use_subcol_) {
data_ptr = hist_buf_ptr + hist_buf->size() - 2 * static_cast<size_t>(num_bin_aligned_);
}
} else {
data_ptr = hist_buf_ptr +
static_cast<size_t>(num_bin_aligned_) * (block_id - 1) * 2;
}
std::memset(reinterpret_cast<void*>(data_ptr), 0, num_bin_ * kInt16HistBufferEntrySize);
if (USE_INDICES) {
if (ORDERED) {
sub_multi_val_bin->ConstructHistogramOrderedInt16(data_indices, start, end,
gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
} else {
sub_multi_val_bin->ConstructHistogramInt16(data_indices, start, end, gradients,
hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
} else {
sub_multi_val_bin->ConstructHistogramInt16(start, end, gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
} else {
int32_t* data_ptr = reinterpret_cast<int32_t*>(origin_hist_data_);
int32_t* hist_buf_ptr = reinterpret_cast<int32_t*>(hist_buf->data());
if (block_id == 0) {
if (is_use_subcol_) {
data_ptr = hist_buf_ptr + hist_buf->size() - 2 * static_cast<size_t>(num_bin_aligned_);
}
} else {
data_ptr = hist_buf_ptr +
static_cast<size_t>(num_bin_aligned_) * (block_id - 1) * 2;
}
std::memset(reinterpret_cast<void*>(data_ptr), 0, num_bin_ * kInt32HistBufferEntrySize);
if (USE_INDICES) {
if (ORDERED) {
sub_multi_val_bin->ConstructHistogramOrderedInt32(data_indices, start, end,
gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
} else {
sub_multi_val_bin->ConstructHistogramInt32(data_indices, start, end, gradients,
hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
} else {
sub_multi_val_bin->ConstructHistogramInt32(start, end, gradients, hessians,
reinterpret_cast<hist_t*>(data_ptr));
}
}
} else {
hist_t* data_ptr = origin_hist_data_;
if (block_id == 0) {
if (is_use_subcol_) {
data_ptr = hist_buf->data() + hist_buf->size() - 2 * static_cast<size_t>(num_bin_aligned_);
}
} else {
data_ptr = hist_buf->data() +
static_cast<size_t>(num_bin_aligned_) * (block_id - 1) * 2;
}
std::memset(reinterpret_cast<void*>(data_ptr), 0, num_bin_ * kHistBufferEntrySize);
if (USE_INDICES) {
if (ORDERED) {
sub_multi_val_bin->ConstructHistogramOrdered(data_indices, start, end,
gradients, hessians, data_ptr);
} else {
sub_multi_val_bin->ConstructHistogram(data_indices, start, end, gradients,
hessians, data_ptr);
}
} else {
sub_multi_val_bin->ConstructHistogram(start, end, gradients, hessians,
data_ptr);
}
}
}
void CopyMultiValBinSubset(const std::vector<int>& group_feature_start,
const std::vector<std::unique_ptr<FeatureGroup>>& feature_groups,
const std::vector<int8_t>& is_feature_used,
const data_size_t* bagging_use_indices,
data_size_t bagging_indices_cnt);
void SetUseSubrow(bool is_use_subrow) {
is_use_subrow_ = is_use_subrow;
}
void SetSubrowCopied(bool is_subrow_copied) {
is_subrow_copied_ = is_subrow_copied;
}
#ifdef USE_CUDA
const void* GetRowWiseData(
uint8_t* bit_type,
size_t* total_size,
bool* is_sparse,
const void** out_data_ptr,
uint8_t* data_ptr_bit_type) const {
if (multi_val_bin_ == nullptr) {
*bit_type = 0;
*total_size = 0;
*is_sparse = false;
return nullptr;
} else {
return multi_val_bin_->GetRowWiseData(bit_type, total_size, is_sparse, out_data_ptr, data_ptr_bit_type);
}
}
#endif // USE_CUDA
private:
bool is_use_subcol_ = false;
bool is_use_subrow_ = false;
bool is_subrow_copied_ = false;
std::unique_ptr<MultiValBin> multi_val_bin_;
std::unique_ptr<MultiValBin> multi_val_bin_subset_;
std::vector<uint32_t> hist_move_src_;
std::vector<uint32_t> hist_move_dest_;
std::vector<uint32_t> hist_move_size_;
const std::vector<int> feature_groups_contained_;
int num_threads_;
int num_bin_;
int num_bin_aligned_;
int n_data_block_;
int data_block_size_;
int min_block_size_;
int num_data_;
int num_grad_quant_bins_;
hist_t* origin_hist_data_;
const size_t kHistBufferEntrySize = 2 * sizeof(hist_t);
const size_t kInt32HistBufferEntrySize = 2 * sizeof(int32_t);
const size_t kInt16HistBufferEntrySize = 2 * sizeof(int16_t);
const size_t kInt8HistBufferEntrySize = 2 * sizeof(int8_t);
};
struct TrainingShareStates {
int num_threads = 0;
bool is_col_wise = true;
bool is_constant_hessian = true;
const data_size_t* bagging_use_indices;
data_size_t bagging_indices_cnt;
TrainingShareStates() {
multi_val_bin_wrapper_.reset(nullptr);
}
int num_hist_total_bin() const { return num_hist_total_bin_; }
const std::vector<uint32_t>& feature_hist_offsets() const { return feature_hist_offsets_; }
#ifdef USE_CUDA
const std::vector<uint32_t>& column_hist_offsets() const { return column_hist_offsets_; }
#endif // USE_CUDA
bool IsSparseRowwise() const {
return (multi_val_bin_wrapper_ != nullptr && multi_val_bin_wrapper_->IsSparse());
}
void SetMultiValBin(MultiValBin* bin, data_size_t num_data,
const std::vector<std::unique_ptr<FeatureGroup>>& feature_groups,
bool dense_only, bool sparse_only, const int num_grad_quant_bins);
void CalcBinOffsets(const std::vector<std::unique_ptr<FeatureGroup>>& feature_groups,
std::vector<uint32_t>* offsets, bool is_col_wise);
void InitTrain(const std::vector<int>& group_feature_start,
const std::vector<std::unique_ptr<FeatureGroup>>& feature_groups,
const std::vector<int8_t>& is_feature_used) {
if (multi_val_bin_wrapper_ != nullptr) {
multi_val_bin_wrapper_->InitTrain(group_feature_start,
feature_groups,
is_feature_used,
bagging_use_indices,
bagging_indices_cnt);
}
}
template <bool USE_INDICES, bool ORDERED, bool USE_QUANT_GRAD, int HIST_BITS>
void ConstructHistograms(const data_size_t* data_indices,
data_size_t num_data,
const score_t* gradients,
const score_t* hessians,
hist_t* hist_data) {
if (multi_val_bin_wrapper_ != nullptr) {
multi_val_bin_wrapper_->ConstructHistograms<USE_INDICES, ORDERED, USE_QUANT_GRAD, HIST_BITS>(
data_indices, num_data, gradients, hessians, &hist_buf_, hist_data);
}
}
void SetUseSubrow(bool is_use_subrow) {
if (multi_val_bin_wrapper_ != nullptr) {
multi_val_bin_wrapper_->SetUseSubrow(is_use_subrow);
}
}
void SetSubrowCopied(bool is_subrow_copied) {
if (multi_val_bin_wrapper_ != nullptr) {
multi_val_bin_wrapper_->SetSubrowCopied(is_subrow_copied);
}
}
#ifdef USE_CUDA
const void* GetRowWiseData(uint8_t* bit_type,
size_t* total_size,
bool* is_sparse,
const void** out_data_ptr,
uint8_t* data_ptr_bit_type) {
if (multi_val_bin_wrapper_ != nullptr) {
return multi_val_bin_wrapper_->GetRowWiseData(bit_type, total_size, is_sparse, out_data_ptr, data_ptr_bit_type);
} else {
*bit_type = 0;
*total_size = 0;
*is_sparse = false;
return nullptr;
}
}
#endif // USE_CUDA
private:
std::vector<uint32_t> feature_hist_offsets_;
#ifdef USE_CUDA
std::vector<uint32_t> column_hist_offsets_;
#endif // USE_CUDA
int num_hist_total_bin_ = 0;
std::unique_ptr<MultiValBinWrapper> multi_val_bin_wrapper_;
std::vector<hist_t, Common::AlignmentAllocator<hist_t, kAlignedSize>> hist_buf_;
int num_total_bin_ = 0;
double num_elements_per_row_ = 0.0f;
};
} // namespace LightGBM
#endif // LIGHTGBM_INCLUDE_LIGHTGBM_TRAIN_SHARE_STATES_H_