Skip to content

Commit 5006fe7

Browse files
authored
Optimize device regex, test reusing a ProxyDMatrix. (dmlc#11273)
1 parent 73e0df6 commit 5006fe7

File tree

3 files changed

+126
-7
lines changed

3 files changed

+126
-7
lines changed

demo/c-api/inference/inference.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
/*!
2-
* Copyright 2021 XGBoost contributors
1+
/**
2+
* Copyright 2021-2025, XGBoost contributors
33
*
4-
* \brief A simple example of using prediction functions.
4+
* @brief A simple example of using prediction functions.
5+
*
6+
* See more examples in test_c_api.cc on how to reuse a ProxyDMatrix object for reducing
7+
* the latency of DMatrix creation.
58
*/
69
#include <stddef.h>
710
#include <stdlib.h>

src/context.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2014-2024, XGBoost Contributors
2+
* Copyright 2014-2025, XGBoost Contributors
33
*
44
* \brief Context object used for controlling runtime parameters.
55
*/
@@ -11,12 +11,17 @@
1111
#include <optional> // for optional
1212
#include <regex> // for regex_replace, regex_match
1313

14-
#include "common/common.h" // AssertGPUSupport
1514
#include "common/cuda_rt_utils.h" // for AllVisibleGPUs
1615
#include "common/error_msg.h" // WarnDeprecatedGPUId
1716
#include "common/threading_utils.h"
1817
#include "xgboost/string_view.h"
1918

19+
#if !defined(XGBOOST_USE_CUDA)
20+
21+
#include "common/common.h" // for AssertGPUSupport
22+
23+
#endif // !defined(XGBOOST_USE_CUDA)
24+
2025
namespace xgboost {
2126

2227
DMLC_REGISTER_PARAMETER(Context);
@@ -108,7 +113,8 @@ DeviceOrd CUDAOrdinal(DeviceOrd device, bool) {
108113
bool valid = substr == "cpu" || substr == "cud" || substr == "gpu" || substr == "syc";
109114
CHECK(valid) << msg;
110115
#else
111-
std::regex pattern{"gpu(:[0-9]+)?|cuda(:[0-9]+)?|cpu|sycl(:cpu|:gpu)?(:-1|:[0-9]+)?"};
116+
thread_local static std::regex pattern{
117+
"gpu(:[0-9]+)?|cuda(:[0-9]+)?|cpu|sycl(:cpu|:gpu)?(:-1|:[0-9]+)?"};
112118
if (!std::regex_match(input, pattern)) {
113119
fatal();
114120
}

tests/cpp/c_api/test_c_api.cc

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2019-2024 XGBoost contributors
2+
* Copyright 2019-2025, XGBoost contributors
33
*/
44
#include <gtest/gtest.h>
55
#include <xgboost/c_api.h>
@@ -8,6 +8,7 @@
88
#include <xgboost/learner.h>
99
#include <xgboost/version_config.h>
1010

11+
#include <algorithm> // for copy_n
1112
#include <array> // for array
1213
#include <cstddef> // std::size_t
1314
#include <filesystem> // std::filesystem
@@ -22,6 +23,7 @@
2223
#include "../../../src/data/batch_utils.h" // for MatchingPageBytes
2324
#include "../../../src/data/gradient_index.h" // for GHistIndexMatrix
2425
#include "../../../src/data/iterative_dmatrix.h" // for IterativeDMatrix
26+
#include "../../../src/data/proxy_dmatrix.h" // for DMatrixProxy
2527
#include "../../../src/data/sparse_page_dmatrix.h" // for SparsePageDMatrix
2628
#include "../helpers.h"
2729

@@ -599,4 +601,112 @@ TEST(CAPI, GPUXGDMatrixGetQuantileCut) {
599601
TestXGDMatrixGetQuantileCut(&ctx);
600602
}
601603
#endif // defined(XGBOOST_USE_CUDA)
604+
605+
TEST(CAPI, PredictReuseProxy) {
606+
// Configuration for creating DMatrix
607+
Json fmat_cfg{Object{}};
608+
fmat_cfg["missing"] = std::numeric_limits<float>::quiet_NaN();
609+
auto sfmat_cfg = Json::Dump(fmat_cfg);
610+
611+
// Configuration for prediction
612+
Json config{Object{}};
613+
config["type"] = Integer{0};
614+
config["iteration_begin"] = config["iteration_end"] = Integer{0};
615+
config["missing"] = Number{std::numeric_limits<float>::quiet_NaN()};
616+
config["strict_shape"] = Boolean{true};
617+
config["training"] = Boolean{false};
618+
auto scfg = Json::Dump(config);
619+
620+
HostDeviceVector<float> storage;
621+
bst_idx_t n_samples = 1024;
622+
auto inf = RandomDataGenerator{n_samples, 256, 0.0}.GenerateArrayInterface(&storage);
623+
HostDeviceVector<float> storage_y;
624+
auto y_inf = RandomDataGenerator{n_samples, 1, 0.0}.GenerateArrayInterface(&storage_y);
625+
626+
// Create a DMatrix for training
627+
DMatrixHandle fmat_hdl{nullptr};
628+
ASSERT_EQ(XGDMatrixCreateFromDense(inf.c_str(), sfmat_cfg.c_str(), &fmat_hdl), 0);
629+
ASSERT_EQ(XGDMatrixSetInfoFromInterface(fmat_hdl, "label", y_inf.c_str()), 0);
630+
631+
// Create booster and train.
632+
std::array<DMatrixHandle, 1> mats{fmat_hdl};
633+
BoosterHandle booster_hdl;
634+
ASSERT_EQ(XGBoosterCreate(mats.data(), 1, &booster_hdl), 0);
635+
636+
for (std::int32_t i = 0; i < 3; ++i) {
637+
ASSERT_EQ(XGBoosterUpdateOneIter(booster_hdl, i, fmat_hdl), 0);
638+
}
639+
640+
// Create a proxy that can be reused.
641+
DMatrixHandle proxy_hdl{nullptr};
642+
ASSERT_EQ(XGProxyDMatrixCreate(&proxy_hdl), 0);
643+
644+
bst_ulong const *outshape{nullptr};
645+
bst_ulong outdim{0};
646+
float const *result{nullptr};
647+
648+
{
649+
// Prediction with DMatrix
650+
ASSERT_EQ(XGBoosterPredictFromDMatrix(booster_hdl, fmat_hdl, scfg.c_str(), &outshape, &outdim,
651+
&result),
652+
0);
653+
bst_ulong n_samples_ret = 0;
654+
ASSERT_EQ(XGDMatrixNumRow(fmat_hdl, &n_samples_ret), 0);
655+
std::vector<float> vec_0(n_samples_ret);
656+
ASSERT_EQ(vec_0.size(), n_samples);
657+
ASSERT_EQ(outdim, 2);
658+
std::copy_n(result, vec_0.size(), vec_0.begin());
659+
660+
// In-place predict
661+
ASSERT_EQ(XGBoosterPredictFromDense(booster_hdl, inf.c_str(), scfg.c_str(), proxy_hdl,
662+
&outshape, &outdim, &result),
663+
0);
664+
ASSERT_EQ(XGDMatrixNumRow(proxy_hdl, &n_samples_ret), 0);
665+
std::vector<float> vec_1(n_samples_ret);
666+
ASSERT_EQ(vec_1.size(), n_samples);
667+
ASSERT_EQ(outdim, 2);
668+
std::copy_n(result, vec_1.size(), vec_1.begin());
669+
670+
// Same result
671+
ASSERT_EQ(vec_0, vec_1);
672+
}
673+
674+
{
675+
bst_idx_t n_samples = 512;
676+
677+
// Prediction with DMatrix
678+
auto inf = RandomDataGenerator{n_samples, 256, 0.0}.GenerateArrayInterface(&storage);
679+
DMatrixHandle fmat_hdl{nullptr};
680+
ASSERT_EQ(XGDMatrixCreateFromDense(inf.c_str(), sfmat_cfg.c_str(), &fmat_hdl), 0);
681+
682+
ASSERT_EQ(XGBoosterPredictFromDMatrix(booster_hdl, fmat_hdl, scfg.c_str(), &outshape, &outdim,
683+
&result),
684+
0);
685+
bst_ulong n_samples_ret = 0;
686+
ASSERT_EQ(XGDMatrixNumRow(fmat_hdl, &n_samples_ret), 0);
687+
std::vector<float> vec_0(n_samples_ret);
688+
ASSERT_EQ(vec_0.size(), n_samples);
689+
ASSERT_EQ(outdim, 2);
690+
std::copy_n(result, vec_0.size(), vec_0.begin());
691+
692+
// In-place predict, same proxy as before
693+
ASSERT_EQ(XGBoosterPredictFromDense(booster_hdl, inf.c_str(), scfg.c_str(), proxy_hdl,
694+
&outshape, &outdim, &result),
695+
0);
696+
ASSERT_EQ(XGDMatrixNumRow(proxy_hdl, &n_samples_ret), 0);
697+
std::vector<float> vec_1(n_samples_ret);
698+
ASSERT_EQ(vec_1.size(), n_samples);
699+
ASSERT_EQ(outdim, 2);
700+
std::copy_n(result, vec_1.size(), vec_1.begin());
701+
702+
// Same result
703+
ASSERT_EQ(vec_0, vec_1);
704+
705+
ASSERT_EQ(XGDMatrixFree(fmat_hdl), 0);
706+
}
707+
708+
ASSERT_EQ(XGDMatrixFree(fmat_hdl), 0);
709+
ASSERT_EQ(XGBoosterFree(booster_hdl), 0);
710+
ASSERT_EQ(XGDMatrixFree(proxy_hdl), 0);
711+
}
602712
} // namespace xgboost

0 commit comments

Comments
 (0)