Skip to content

Commit c07a69b

Browse files
committed
[EM] Check whether memory policy is set.
1 parent 9c0efce commit c07a69b

File tree

7 files changed

+389
-41
lines changed

7 files changed

+389
-41
lines changed

src/common/error_msg.cc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
/**
2-
* Copyright 2023 by XGBoost contributors
2+
* Copyright 2023-2025, XGBoost contributors
33
*/
44
#include "error_msg.h"
55

6-
#include <mutex> // for call_once, once_flag
7-
#include <sstream> // for stringstream
6+
#include <mutex> // for call_once, once_flag
7+
#include <sstream> // for stringstream
8+
#include <system_error> // for error_code, system_category
89

910
#include "../collective/communicator-inl.h" // for GetRank
11+
#include "xgboost/collective/socket.h" // for LastError
1012
#include "xgboost/context.h" // for Context
1113
#include "xgboost/logging.h"
1214

@@ -76,4 +78,10 @@ void CheckOldNccl(std::int32_t major, std::int32_t minor, std::int32_t patch) {
7678
LOG(WARNING) << msg();
7779
}
7880
}
81+
82+
[[nodiscard]] std::error_code SystemError() {
83+
std::int32_t errsv = system::LastError();
84+
auto err = std::error_code{errsv, std::system_category()};
85+
return err;
86+
}
7987
} // namespace xgboost::error

src/common/error_msg.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
#ifndef XGBOOST_COMMON_ERROR_MSG_H_
77
#define XGBOOST_COMMON_ERROR_MSG_H_
88

9-
#include <cstdint> // for uint64_t
10-
#include <limits> // for numeric_limits
11-
#include <string> // for string
9+
#include <cstdint> // for uint64_t
10+
#include <limits> // for numeric_limits
11+
#include <string> // for string
12+
#include <system_error> // for error_code
1213

1314
#include "xgboost/base.h" // for bst_feature_t
1415
#include "xgboost/context.h" // for Context
@@ -140,5 +141,7 @@ constexpr StringView CacheHostRatioNotImpl() {
140141
constexpr StringView CacheHostRatioInvalid() {
141142
return "`cache_host_ratio` must be in range [0, 1].";
142143
}
144+
145+
[[nodiscard]] std::error_code SystemError();
143146
} // namespace xgboost::error
144147
#endif // XGBOOST_COMMON_ERROR_MSG_H_

src/common/io.cc

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Copyright 2019-2025, by XGBoost Contributors
33
*/
4+
#include "error_msg.h"
45
#if defined(__unix__) || defined(__APPLE__)
56

67
#include <fcntl.h> // for open, O_RDONLY, posix_fadvise
@@ -29,12 +30,10 @@
2930
#include <iterator> // for distance
3031
#include <memory> // for unique_ptr, make_unique
3132
#include <string> // for string
32-
#include <system_error> // for error_code, system_category
3333
#include <utility> // for move
3434
#include <vector> // for vector
3535

3636
#include "io.h"
37-
#include "xgboost/collective/socket.h" // for LastError
3837
#include "xgboost/logging.h" // for CHECK_LE
3938
#include "xgboost/string_view.h" // for StringView
4039

@@ -134,19 +133,13 @@ std::size_t GetMmapAlignment() {
134133
return getpagesize();
135134
#endif
136135
}
137-
138-
auto SystemErrorMsg() {
139-
std::int32_t errsv = system::LastError();
140-
auto err = std::error_code{errsv, std::system_category()};
141-
return err.message();
142-
}
143136
} // anonymous namespace
144137

145138
std::vector<char> LoadSequentialFile(std::string uri) {
146139
auto OpenErr = [&uri]() {
147140
std::string msg;
148141
msg = "Opening " + uri + " failed: ";
149-
msg += SystemErrorMsg();
142+
msg += error::SystemError().message();
150143
LOG(FATAL) << msg;
151144
};
152145

@@ -193,10 +186,11 @@ MMAPFile* detail::OpenMmap(std::string path, std::size_t offset, std::size_t len
193186
#if defined(xgboost_IS_WIN)
194187
HANDLE fd = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
195188
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr);
196-
CHECK_NE(fd, INVALID_HANDLE_VALUE) << "Failed to open:" << path << ". " << SystemErrorMsg();
189+
CHECK_NE(fd, INVALID_HANDLE_VALUE)
190+
<< "Failed to open:" << path << ". " << SystemError().message();
197191
#else
198192
auto fd = open(path.c_str(), O_RDONLY);
199-
CHECK_GE(fd, 0) << "Failed to open:" << path << ". " << SystemErrorMsg();
193+
CHECK_GE(fd, 0) << "Failed to open:" << path << ". " << error::SystemError().message();
200194
#endif
201195

202196
std::byte* ptr{nullptr};
@@ -207,7 +201,7 @@ MMAPFile* detail::OpenMmap(std::string path, std::size_t offset, std::size_t len
207201
#if defined(__linux__) || defined(__GLIBC__)
208202
int prot{PROT_READ};
209203
ptr = reinterpret_cast<std::byte*>(mmap(nullptr, view_size, prot, MAP_PRIVATE, fd, view_start));
210-
CHECK_NE(ptr, MAP_FAILED) << "Failed to map: " << path << ". " << SystemErrorMsg();
204+
CHECK_NE(ptr, MAP_FAILED) << "Failed to map: " << path << ". " << error::SystemError().message();
211205
auto handle = new MMAPFile{fd, ptr, view_size, offset - view_start, std::move(path)};
212206
#elif defined(xgboost_IS_WIN)
213207
auto file_size = GetFileSize(fd, nullptr);
@@ -216,16 +210,16 @@ MMAPFile* detail::OpenMmap(std::string path, std::size_t offset, std::size_t len
216210
access = FILE_MAP_READ;
217211
std::uint32_t loff = static_cast<std::uint32_t>(view_start);
218212
std::uint32_t hoff = view_start >> 32;
219-
CHECK(map_file) << "Failed to map: " << path << ". " << SystemErrorMsg();
213+
CHECK(map_file) << "Failed to map: " << path << ". " << error::SystemError().message();
220214
ptr = reinterpret_cast<std::byte*>(MapViewOfFile(map_file, access, hoff, loff, view_size));
221-
CHECK_NE(ptr, nullptr) << "Failed to map: " << path << ". " << SystemErrorMsg();
215+
CHECK_NE(ptr, nullptr) << "Failed to map: " << path << ". " << error::SystemError().message();
222216
auto handle = new MMAPFile{fd, map_file, ptr, view_size, offset - view_start, std::move(path)};
223217
#else
224218
CHECK_LE(offset, std::numeric_limits<off_t>::max())
225219
<< "File size has exceeded the limit on the current system.";
226220
int prot{PROT_READ};
227221
ptr = reinterpret_cast<std::byte*>(mmap(nullptr, view_size, prot, MAP_PRIVATE, fd, view_start));
228-
CHECK_NE(ptr, MAP_FAILED) << "Failed to map: " << path << ". " << SystemErrorMsg();
222+
CHECK_NE(ptr, MAP_FAILED) << "Failed to map: " << path << ". " << error::SystemError().message();
229223
auto handle = new MMAPFile{fd, ptr, view_size, offset - view_start, std::move(path)};
230224
#endif // defined(__linux__) || defined(__GLIBC__)
231225

@@ -238,22 +232,24 @@ void detail::CloseMmap(MMAPFile* handle) {
238232
}
239233
#if defined(xgboost_IS_WIN)
240234
if (handle->base_ptr) {
241-
CHECK(UnmapViewOfFile(handle->base_ptr)) << "Failed to call munmap: " << SystemErrorMsg();
235+
CHECK(UnmapViewOfFile(handle->base_ptr))
236+
<< "Failed to call munmap: " << error::SystemError().message();
242237
}
243238
if (handle->fd != INVALID_HANDLE_VALUE) {
244-
CHECK(CloseHandle(handle->fd)) << "Failed to close handle: " << SystemErrorMsg();
239+
CHECK(CloseHandle(handle->fd)) << "Failed to close handle: " << error::SystemError().message();
245240
}
246241
if (handle->file_map != INVALID_HANDLE_VALUE) {
247-
CHECK(CloseHandle(handle->file_map)) << "Failed to close mapping object: " << SystemErrorMsg();
242+
CHECK(CloseHandle(handle->file_map))
243+
<< "Failed to close mapping object: " << error::SystemError().message();
248244
}
249245
#else
250246
if (handle->base_ptr) {
251247
CHECK_NE(munmap(handle->base_ptr, handle->base_size), -1)
252-
<< "Failed to call munmap: `" << handle->path << "`. " << SystemErrorMsg();
248+
<< "Failed to call munmap: `" << handle->path << "`. " << error::SystemError().message();
253249
}
254250
if (handle->fd != 0) {
255251
CHECK_NE(close(handle->fd), -1)
256-
<< "Failed to close: `" << handle->path << "`. " << SystemErrorMsg();
252+
<< "Failed to close: `" << handle->path << "`. " << error::SystemError().message();
257253
}
258254
#endif
259255
delete handle;
@@ -293,7 +289,7 @@ std::shared_ptr<MallocResource> MemBufFileReadStream::ReadFileIntoBuffer(StringV
293289
std::unique_ptr<FILE, std::function<int(FILE*)>> fp{fopen(path.c_str(), "rb"), fclose};
294290

295291
auto err = [&] {
296-
auto e = SystemErrorMsg();
292+
auto e = error::SystemError().message();
297293
LOG(FATAL) << "Failed to read file `" << path << "`. System error message: " << e;
298294
};
299295
#if defined(__linux__)
@@ -302,7 +298,7 @@ std::shared_ptr<MallocResource> MemBufFileReadStream::ReadFileIntoBuffer(StringV
302298
err();
303299
}
304300
if (posix_fadvise(fd, offset, length, POSIX_FADV_SEQUENTIAL) != 0) {
305-
LOG(FATAL) << SystemErrorMsg();
301+
LOG(FATAL) << error::SystemError().message();
306302
}
307303
#endif // defined(__linux__)
308304

@@ -358,12 +354,12 @@ AlignedMemWriteStream::~AlignedMemWriteStream() = default;
358354
[[nodiscard]] std::size_t TotalMemory() {
359355
#if defined(__linux__)
360356
struct sysinfo info;
361-
CHECK_EQ(sysinfo(&info), 0) << SystemErrorMsg();
357+
CHECK_EQ(sysinfo(&info), 0) << error::SystemError().message();
362358
return info.totalram * info.mem_unit;
363359
#elif defined(xgboost_IS_WIN)
364360
MEMORYSTATUSEX status;
365361
status.dwLength = sizeof(status);
366-
CHECK(GlobalMemoryStatusEx(&status)) << SystemErrorMsg();
362+
CHECK(GlobalMemoryStatusEx(&status)) << error::SystemError().message();
367363
return static_cast<std::size_t>(status.ullTotalPhys);
368364
#else
369365
LOG(FATAL) << "Not implemented";

src/common/numa_utils.cc

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* Copyright 2025, XGBoost Contributors
3+
*/
4+
#include "numa_utils.h"
5+
6+
#if defined(__linux__)
7+
8+
#include <linux/mempolicy.h> // for MPOL_BIND
9+
#include <sys/syscall.h> // for SYS_get_mempolicy
10+
#include <unistd.h> // for syscall
11+
12+
#endif // defined(__linux__)
13+
14+
#include <cctype> // for isalnum
15+
#include <cstddef> // for size_t
16+
#include <cstdint> // for int32_t
17+
#include <filesystem> // for path
18+
#include <fstream> // for ifstream
19+
#include <string> // for string, stoi
20+
#include <vector> // for vector
21+
22+
#include "common.h" // for TrimLast, TrimFirst
23+
#include "error_msg.h" // for SystemError
24+
#include "xgboost/logging.h"
25+
26+
namespace xgboost::common {
27+
28+
namespace {
29+
namespace fs = std::filesystem;
30+
#if defined(__linux__)
31+
// Wrapper for the system call
32+
auto GetMemPolicy(int *mode, unsigned long *nodemask, unsigned long maxnode, void *addr, // NOLINT
33+
unsigned long flags) { // NOLINT
34+
return syscall(SYS_get_mempolicy, mode, nodemask, maxnode, addr, flags);
35+
}
36+
37+
auto GetMemPolicy(int *policy, unsigned long *nmask, unsigned long maxnode) { // NOLINT
38+
return GetMemPolicy(policy, nmask, maxnode, nullptr, 0);
39+
}
40+
#endif // defined(__linux__)
41+
} // namespace
42+
43+
void ReadCpuList(fs::path const &path, std::vector<std::int32_t> *p_cpus) {
44+
auto &cpus = *p_cpus;
45+
cpus.clear();
46+
47+
std::ifstream fin{path};
48+
std::string buff;
49+
fin >> buff;
50+
CHECK(!buff.empty());
51+
buff = common::TrimFirst(common::TrimLast(buff));
52+
53+
std::int32_t k = 0;
54+
CHECK(std::isalnum(buff[k]));
55+
while (static_cast<std::size_t>(k) < buff.size()) {
56+
std::int32_t val0 = -1, val1 = -1;
57+
std::size_t idx = 0;
58+
CHECK(std::isalnum(buff[k])) << k << " " << buff;
59+
val0 = std::stoi(buff.data() + k, &idx);
60+
auto last = k + idx;
61+
CHECK_LE(last, buff.size());
62+
k = last + 1; // new begin
63+
if (last == buff.size() || buff[last] != '-') {
64+
cpus.push_back(val0);
65+
continue;
66+
}
67+
CHECK_EQ(buff[last], '-') << last;
68+
69+
idx = -1;
70+
CHECK_LT(k, buff.size());
71+
val1 = std::stoi(buff.data() + k, &idx);
72+
CHECK_GE(idx, 1);
73+
// Parse range
74+
for (auto i = val0; i <= val1; ++i) {
75+
cpus.push_back(i);
76+
}
77+
k += (idx + 1);
78+
}
79+
}
80+
81+
void GetNumaNodeCpus(std::int32_t node_id, std::vector<std::int32_t> *p_cpus) {
82+
p_cpus->clear();
83+
#if defined(__linux__)
84+
std::string nodename = "node" + std::to_string(node_id);
85+
auto p_cpulist = fs::path{"/sys/devices/system/node"} / fs::path{nodename} / fs::path{"cpulist"};
86+
87+
if (!fs::exists(p_cpulist)) {
88+
return;
89+
}
90+
ReadCpuList(p_cpulist, p_cpus);
91+
#endif // defined(__linux__)
92+
}
93+
94+
[[nodiscard]] std::int32_t GetNumaMaxNumNodes() {
95+
#if defined(__linux__)
96+
auto p_possible = fs::path{"/sys/devices/system/node/possible"};
97+
std::int32_t max_n_nodes = -1;
98+
if (fs::exists(p_possible)) {
99+
std::vector<std::int32_t> cpus;
100+
ReadCpuList(p_possible, &cpus);
101+
auto it = std::max_element(cpus.cbegin(), cpus.cend());
102+
if (it != cpus.cend()) {
103+
max_n_nodes = *it;
104+
}
105+
}
106+
107+
// Just in case if it keeps getting into error
108+
constexpr decltype(max_n_nodes) kThresh = 16384;
109+
// Estimate the size of the CPU set based on the error returned from get mempolicy.
110+
// Strategy used by hwloc and libnuma.
111+
while (true) {
112+
std::vector<std::uint64_t> mask(max_n_nodes, 0);
113+
114+
std::int32_t mode = -1;
115+
auto err = GetMemPolicy(&mode, mask.data(), max_n_nodes);
116+
if (!err || errno != EINVAL) {
117+
return max_n_nodes;
118+
}
119+
max_n_nodes *= 2;
120+
121+
if (max_n_nodes > kThresh) {
122+
break;
123+
}
124+
}
125+
return -1;
126+
#else
127+
return -1;
128+
#endif // defined(__linux__)
129+
}
130+
131+
[[nodiscard]] bool GetNumaMemBind() {
132+
#if defined(__linux__)
133+
std::int32_t mode = -1;
134+
auto max_n_nodes = GetNumaMaxNumNodes();
135+
if (max_n_nodes <= 0) {
136+
return false;
137+
}
138+
std::vector<std::uint64_t> mask(max_n_nodes / 8);
139+
CHECK_GE(GetMemPolicy(&mode, mask.data(), max_n_nodes), 0) << error::SystemError().message();
140+
return mode == MPOL_BIND;
141+
#else
142+
return false;
143+
#endif // defined(__linux__)
144+
}
145+
146+
[[nodiscard]] std::int32_t GetNumaNumNodes() {
147+
#if defined(__linux__)
148+
fs::path p_node{"/sys/devices/system/node"};
149+
if (!fs::exists(p_node)) {
150+
return -1;
151+
}
152+
std::int32_t n_nodes{0};
153+
for (auto const &entry : fs::directory_iterator{p_node}) {
154+
auto name = entry.path().filename().string();
155+
if (name.find("node") == 0) { // starts with `node`
156+
n_nodes += 1;
157+
}
158+
}
159+
if (n_nodes == 0) {
160+
return -1;
161+
}
162+
return n_nodes;
163+
#else
164+
return -1;
165+
#endif // defined(__linux__)
166+
}
167+
} // namespace xgboost::common

0 commit comments

Comments
 (0)