Skip to content

Commit

Permalink
allow build with cmake; fix test; fix bug with mingw
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitry Razdoburdin committed Oct 27, 2023
1 parent d3772fb commit 26f9dea
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 163 deletions.
13 changes: 9 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,6 @@ if (USE_CUDA)
find_package(CUDAToolkit REQUIRED)
endif (USE_CUDA)

if (PLUGIN_UPDATER_ONEAPI)
target_compile_definitions(xgboost PRIVATE -DXGBOOST_USE_ONEAPI=1)
endif (PLUGIN_UPDATER_ONEAPI)

if (FORCE_COLORED_OUTPUT AND (CMAKE_GENERATOR STREQUAL "Ninja") AND
((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")))
Expand Down Expand Up @@ -268,6 +264,15 @@ if (PLUGIN_RMM)
get_target_property(rmm_link_libs rmm::rmm INTERFACE_LINK_LIBRARIES)
endif (PLUGIN_RMM)

if (PLUGIN_UPDATER_ONEAPI)
set(CMAKE_CXX_LINK_EXECUTABLE
"icpx <FLAGS> <CMAKE_CXX_LINK_FLAGS> -qopenmp <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_CXX_CREATE_SHARED_LIBRARY
"icpx <CMAKE_SHARED_LIBRARY_CXX_FLAGS> -qopenmp <LANGUAGE_COMPILE_FLAGS> \
<CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <SONAME_FLAG>,<TARGET_SONAME> \
-o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
endif (PLUGIN_UPDATER_ONEAPI)

#-- library
if (BUILD_STATIC_LIB)
add_library(xgboost STATIC)
Expand Down
4 changes: 3 additions & 1 deletion plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ if (PLUGIN_DENSE_PARSER)
endif (PLUGIN_DENSE_PARSER)

if (PLUGIN_UPDATER_ONEAPI)
set(CMAKE_CXX_COMPILER "icpx")
add_library(oneapi_plugin OBJECT
${xgboost_SOURCE_DIR}/plugin/updater_oneapi/hist_util_oneapi.cc
${xgboost_SOURCE_DIR}/plugin/updater_oneapi/regression_obj_oneapi.cc
Expand All @@ -24,7 +25,8 @@ if (PLUGIN_UPDATER_ONEAPI)
POSITION_INDEPENDENT_CODE ON)
if (USE_OPENMP)
find_package(OpenMP REQUIRED)
target_link_libraries(oneapi_plugin PUBLIC OpenMP::OpenMP_CXX)
set_target_properties(oneapi_plugin PROPERTIES
COMPILE_FLAGS "-fsycl -qopenmp")
endif (USE_OPENMP)
# Get compilation and link flags of oneapi_plugin and propagate to objxgboost
target_link_libraries(objxgboost PUBLIC oneapi_plugin)
Expand Down
32 changes: 21 additions & 11 deletions plugin/updater_oneapi/device_manager_oneapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
namespace xgboost {

sycl::device DeviceManagerOneAPI::GetDevice(const DeviceOrd& device_spec) const {
if (!device_spec.IsSycl()) {
LOG(WARNING) << "Sycl kernel is executed with non-sycl context. "
<< "Default sycl device_selector will be used.";
}

bool not_use_default_selector = (device_spec.ordinal != kDefaultOrdinal) ||
(rabit::IsDistributed());
if (not_use_default_selector) {
Expand All @@ -28,22 +33,27 @@ sycl::device DeviceManagerOneAPI::GetDevice(const DeviceOrd& device_spec) const
return gpu_devices[device_idx];
}
} else {
if (device_spec.IsSyclDefault()) {
return sycl::device(sycl::default_selector_v);
} else if(device_spec.IsSyclCPU()) {
return sycl::device(sycl::cpu_selector_v);
} else {
return sycl::device(sycl::gpu_selector_v);
}
if(device_spec.IsSyclCPU()) {
return sycl::device(sycl::cpu_selector_v);
} else if(device_spec.IsSyclGPU()) {
return sycl::device(sycl::gpu_selector_v);
} else {
return sycl::device(sycl::default_selector_v);
}
}
}

sycl::queue DeviceManagerOneAPI::GetQueue(const DeviceOrd& device_spec) const {
if (!device_spec.IsSycl()) {
LOG(WARNING) << "Sycl kernel is executed with non-sycl context. "
<< "Default sycl device_selector will be used.";
}

QueueRegister_t& queue_register = GetQueueRegister();
if (queue_register.count(device_spec.Name()) > 0) {
return queue_register.at(device_spec.Name());
}

bool not_use_default_selector = (device_spec.ordinal != kDefaultOrdinal) ||
(rabit::IsDistributed());
std::lock_guard<std::mutex> guard(queue_registering_mutex);
Expand All @@ -64,12 +74,12 @@ sycl::queue DeviceManagerOneAPI::GetQueue(const DeviceOrd& device_spec) const {
queue_register[device_spec.Name()] = sycl::queue(gpu_devices[device_idx]);
}
} else {
if (device_spec.IsSyclDefault()) {
queue_register[device_spec.Name()] = sycl::queue(sycl::default_selector_v);
} else if (device_spec.IsSyclCPU()) {
if (device_spec.IsSyclCPU()) {
queue_register[device_spec.Name()] = sycl::queue(sycl::cpu_selector_v);
} else if (device_spec.IsSyclGPU()) {
queue_register[device_spec.Name()] = sycl::queue(sycl::gpu_selector_v);
} else {
queue_register[device_spec.Name()] = sycl::queue(sycl::default_selector_v);
}
}
return queue_register.at(device_spec.Name());
Expand Down
18 changes: 11 additions & 7 deletions plugin/updater_oneapi/predictor_oneapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ namespace predictor {
DMLC_REGISTRY_FILE_TAG(predictor_oneapi);

class PredictorOneAPI : public Predictor {
public:
explicit PredictorOneAPI(Context const* context) :
Predictor::Predictor{context} {}

void Configure(const std::vector<std::pair<std::string, std::string>>& args) override {
void SetupBackend() {
const DeviceOrd device_spec = ctx_->Device();

bool is_cpu;
Expand All @@ -42,14 +38,22 @@ class PredictorOneAPI : public Predictor {
} else {
is_cpu = true;
}

LOG(INFO) << "device = " << device_spec.Name() << ", is_cpu = " << int(is_cpu);

if (is_cpu) {
predictor_backend_.reset(Predictor::Create("cpu_predictor", ctx_));
} else{
predictor_backend_.reset(Predictor::Create("oneapi_predictor_backend", ctx_));
}
}

public:
explicit PredictorOneAPI(Context const* context) :
Predictor::Predictor{context} {
SetupBackend();
}

void Configure(const std::vector<std::pair<std::string, std::string>>& args) override {
SetupBackend();
predictor_backend_->Configure(args);
}

Expand Down
15 changes: 13 additions & 2 deletions src/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,23 @@ DeviceOrd CUDAOrdinal(DeviceOrd device, bool) {
#endif // defined(__MINGW32__)

// handle alias
#if defined(__MINGW32__)
// mingw hangs on regex using rtools 430. Basic checks only.
bool is_sycl = (substr == "syc");
#else
bool is_sycl = std::regex_match(input, std::regex("sycl(:cpu|:gpu)?(:-1|:[0-9]+)?"));
#endif // defined(__MINGW32__)

std::string s_device = input;
if (!std::regex_match(s_device, std::regex("sycl(:cpu|:gpu)?(:-1|:[0-9]+)?")))
if (!is_sycl) {
s_device = std::regex_replace(s_device, std::regex{"gpu"}, DeviceSym::CUDA());

auto split_it = std::find(s_device.cbegin(), s_device.cend(), ':');
if (std::regex_match(s_device, std::regex("sycl:(cpu|gpu)?"))) split_it = s_device.cend();
// For these cases we need to move iterator to the end, not to look for a ordinal.
if ((s_device == "sycl:cpu") ||
(s_device == "sycl:gpu")) {
split_it = s_device.cend();
}

// For s_device like "sycl:gpu:1"
if (split_it != s_device.cend()) {
Expand Down
142 changes: 45 additions & 97 deletions tests/cpp/plugin/test_predictor_oneapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,57 @@
#include <xgboost/predictor.h>

#include "../../../src/data/adapter.h"
#include "../../../src/data/proxy_dmatrix.h"
#include "../../../src/gbm/gbtree_model.h"
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h"
#include "../predictor/test_predictor.h"

namespace xgboost {
TEST(Plugin, OneAPIPredictorBasic) {
auto lparam = MakeCUDACtx(0);
namespace {
void TestBasic(DMatrix* dmat) {
Context ctx;
ctx.UpdateAllowUnknown(Args{{"device", "sycl"}});
std::unique_ptr<Predictor> oneapi_predictor =
std::unique_ptr<Predictor>(Predictor::Create("oneapi_predictor", &lparam));
std::unique_ptr<Predictor>(Predictor::Create("oneapi_predictor", &ctx));

int kRows = 5;
int kCols = 5;
size_t const kRows = dmat->Info().num_row_;
size_t const kCols = dmat->Info().num_col_;

LearnerModelParam param;
param.num_feature = kCols;
param.base_score = 0.0;
param.num_output_group = 1;

gbm::GBTreeModel model = CreateTestModel(&param);

auto dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatrix();
LearnerModelParam param(MakeMP(kCols, .0, 1));
gbm::GBTreeModel model = CreateTestModel(&param, &ctx);

// Test predict batch
PredictionCacheEntry out_predictions;
oneapi_predictor->PredictBatch(dmat.get(), &out_predictions, model, 0);
ASSERT_EQ(model.trees.size(), out_predictions.version);
oneapi_predictor->InitOutPredictions(dmat->Info(), &out_predictions.predictions, model);
oneapi_predictor->PredictBatch(dmat, &out_predictions, model, 0);

std::vector<float>& out_predictions_h = out_predictions.predictions.HostVector();
for (size_t i = 0; i < out_predictions.predictions.Size(); i++) {
ASSERT_EQ(out_predictions_h[i], 1.5);
}

// Test predict instance
auto const &batch = *dmat->GetBatches<xgboost::SparsePage>().begin();
auto page = batch.GetView();
for (size_t i = 0; i < batch.Size(); i++) {
std::vector<float> instance_out_predictions;
oneapi_predictor->PredictInstance(batch[i], &instance_out_predictions, model);
oneapi_predictor->PredictInstance(page[i], &instance_out_predictions, model);
ASSERT_EQ(instance_out_predictions[0], 1.5);
}

// Test predict leaf
std::vector<float> leaf_out_predictions;
oneapi_predictor->PredictLeaf(dmat.get(), &leaf_out_predictions, model);
for (auto v : leaf_out_predictions) {
HostDeviceVector<float> leaf_out_predictions;
oneapi_predictor->PredictLeaf(dmat, &leaf_out_predictions, model);
auto const& h_leaf_out_predictions = leaf_out_predictions.ConstHostVector();
for (auto v : h_leaf_out_predictions) {
ASSERT_EQ(v, 0);
}

// Test predict contribution
std::vector<float> out_contribution;
oneapi_predictor->PredictContribution(dmat.get(), &out_contribution, model);
HostDeviceVector<float> out_contribution_hdv;
auto& out_contribution = out_contribution_hdv.HostVector();
oneapi_predictor->PredictContribution(dmat, &out_contribution_hdv, model);
ASSERT_EQ(out_contribution.size(), kRows * (kCols + 1));
for (size_t i = 0; i < out_contribution.size(); ++i) {
auto const& contri = out_contribution[i];
Expand All @@ -65,8 +66,9 @@ TEST(Plugin, OneAPIPredictorBasic) {
ASSERT_EQ(contri, 0);
}
}

// Test predict contribution (approximate method)
oneapi_predictor->PredictContribution(dmat.get(), &out_contribution, model, 0, nullptr, true);
oneapi_predictor->PredictContribution(dmat, &out_contribution_hdv, model, 0, nullptr, true);
for (size_t i = 0; i < out_contribution.size(); ++i) {
auto const& contri = out_contribution[i];
// shift 1 for bias, as test tree is a decision dump, only global bias is filled with LeafValue().
Expand All @@ -77,92 +79,38 @@ TEST(Plugin, OneAPIPredictorBasic) {
}
}
}
} // anonymous namespace

TEST(Plugin, OneAPIPredictorExternalMemory) {
dmlc::TemporaryDirectory tmpdir;
std::string filename = tmpdir.path + "/big.libsvm";
std::unique_ptr<DMatrix> dmat = CreateSparsePageDMatrix(12, 64, filename);
auto lparam = MakeCUDACtx(0);

std::unique_ptr<Predictor> oneapi_predictor =
std::unique_ptr<Predictor>(Predictor::Create("oneapi_predictor", &lparam));

LearnerModelParam param;
param.base_score = 0;
param.num_feature = dmat->Info().num_col_;
param.num_output_group = 1;

gbm::GBTreeModel model = CreateTestModel(&param);

// Test predict batch
PredictionCacheEntry out_predictions;
oneapi_predictor->PredictBatch(dmat.get(), &out_predictions, model, 0);
std::vector<float> &out_predictions_h = out_predictions.predictions.HostVector();
ASSERT_EQ(out_predictions.predictions.Size(), dmat->Info().num_row_);
for (const auto& v : out_predictions_h) {
ASSERT_EQ(v, 1.5);
}

// Test predict leaf
std::vector<float> leaf_out_predictions;
oneapi_predictor->PredictLeaf(dmat.get(), &leaf_out_predictions, model);
ASSERT_EQ(leaf_out_predictions.size(), dmat->Info().num_row_);
for (const auto& v : leaf_out_predictions) {
ASSERT_EQ(v, 0);
}

// Test predict contribution
std::vector<float> out_contribution;
oneapi_predictor->PredictContribution(dmat.get(), &out_contribution, model);
ASSERT_EQ(out_contribution.size(), dmat->Info().num_row_ * (dmat->Info().num_col_ + 1));
for (size_t i = 0; i < out_contribution.size(); ++i) {
auto const& contri = out_contribution[i];
// shift 1 for bias, as test tree is a decision dump, only global bias is filled with LeafValue().
if ((i + 1) % (dmat->Info().num_col_ + 1) == 0) {
ASSERT_EQ(out_contribution.back(), 1.5f);
} else {
ASSERT_EQ(contri, 0);
}
}
TEST(SyclPredictor, Basic) {
size_t constexpr kRows = 5;
size_t constexpr kCols = 5;
auto dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatrix();
TestBasic(dmat.get());
}

// Test predict contribution (approximate method)
std::vector<float> out_contribution_approximate;
oneapi_predictor->PredictContribution(dmat.get(), &out_contribution_approximate, model, 0, nullptr, true);
ASSERT_EQ(out_contribution_approximate.size(),
dmat->Info().num_row_ * (dmat->Info().num_col_ + 1));
for (size_t i = 0; i < out_contribution.size(); ++i) {
auto const& contri = out_contribution[i];
// shift 1 for bias, as test tree is a decision dump, only global bias is filled with LeafValue().
if ((i + 1) % (dmat->Info().num_col_ + 1) == 0) {
ASSERT_EQ(out_contribution.back(), 1.5f);
} else {
ASSERT_EQ(contri, 0);
}
}
TEST(SyclPredictor, ExternalMemory) {
size_t constexpr kPageSize = 64, kEntriesPerCol = 3;
size_t constexpr kEntries = kPageSize * kEntriesPerCol * 2;
std::unique_ptr<DMatrix> dmat = CreateSparsePageDMatrix(kEntries);
TestBasic(dmat.get());
}

TEST(Plugin, OneAPIPredictorInplacePredict) {
TEST(SyclPredictor, InplacePredict) {
bst_row_t constexpr kRows{128};
bst_feature_t constexpr kCols{64};
auto gen = RandomDataGenerator{kRows, kCols, 0.5}.Device(-1);
{
HostDeviceVector<float> data;
gen.GenerateDense(&data);
ASSERT_EQ(data.Size(), kRows * kCols);
std::shared_ptr<data::DenseAdapter> x{
new data::DenseAdapter(data.HostPointer(), kRows, kCols)};
TestInplacePrediction(x, "oneapi_predictor", kRows, kCols, -1);
}

{
HostDeviceVector<float> data;
HostDeviceVector<bst_row_t> rptrs;
HostDeviceVector<bst_feature_t> columns;
gen.GenerateCSR(&data, &rptrs, &columns);
std::shared_ptr<data::CSRAdapter> x{new data::CSRAdapter(
rptrs.HostPointer(), columns.HostPointer(), data.HostPointer(), kRows,
data.Size(), kCols)};
TestInplacePrediction(x, "oneapi_predictor", kRows, kCols, -1);
Context ctx;
ctx.UpdateAllowUnknown(Args{{"device", "sycl"}});
std::shared_ptr<data::DMatrixProxy> x{new data::DMatrixProxy{}};
auto array_interface = GetArrayInterface(&data, kRows, kCols);
std::string arr_str;
Json::Dump(array_interface, &arr_str);
x->SetArrayData(arr_str.data());
TestInplacePrediction(&ctx, x, kRows, kCols);
}
}
} // namespace xgboost
Loading

0 comments on commit 26f9dea

Please sign in to comment.