Skip to content

Commit 4cc5656

Browse files
[feat] Support numa node bind.
1 parent af9eca4 commit 4cc5656

9 files changed

Lines changed: 300 additions & 1 deletion

File tree

src/cache/dingo_cache.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "common/options/cache.h"
3232
#include "common/options/common.h"
3333
#include "utils/daemonize.h"
34+
#include "utils/numa_manager.h"
3435

3536
namespace dingofs {
3637
namespace cache {
@@ -134,6 +135,13 @@ int DingoCache::Run(int argc, char** argv) {
134135
}
135136
}
136137

138+
// numa bind to node to avoid performance degradation caused by
139+
// cross-node memory access
140+
auto& numa_manager = dingofs::utils::NumaManager::GetInstance();
141+
if (dingofs::FLAGS_numa_bind_enable && numa_manager.Available()) {
142+
numa_manager.BindNode(dingofs::FLAGS_numa_bind_node);
143+
}
144+
137145
return StartServer();
138146
}
139147

src/client/main.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
#include <gflags/gflags.h>
1718
#include <glog/logging.h>
1819

1920
#include <csignal>
@@ -32,8 +33,8 @@
3233
#include "common/options/common.h"
3334
#include "common/types.h"
3435
#include "fmt/format.h"
35-
#include "gflags/gflags.h"
3636
#include "utils/daemonize.h"
37+
#include "utils/numa_manager.h"
3738
#include "utils/scoped_cleanup.h"
3839

3940
using FuseServer = dingofs::client::fuse::FuseServer;
@@ -181,6 +182,13 @@ int main(int argc, char* argv[]) {
181182
return EXIT_FAILURE;
182183
}
183184

185+
// numa bind to node to avoid performance degradation caused by
186+
// cross-node memory access
187+
auto& numa_manager = dingofs::utils::NumaManager::GetInstance();
188+
if (dingofs::FLAGS_numa_bind_enable && numa_manager.Available()) {
189+
numa_manager.BindNode(dingofs::FLAGS_numa_bind_node);
190+
}
191+
184192
struct MountOption mount_option{
185193
.mount_point = mountpoint,
186194
.fs_name = fs_name,

src/common/options/client.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ DECLARE_bool(vfs_use_fake_block_store);
133133
DECLARE_bool(vfs_block_store_access_log_enable);
134134
DECLARE_int64(vfs_block_store_access_log_threshold_us);
135135

136+
// numa
137+
DECLARE_bool(numa_bind_enable);
138+
DECLARE_int32(numa_bind_node);
139+
136140
} // namespace client
137141
} // namespace dingofs
138142

src/common/options/common.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ DEFINE_validator(log_clean_filter_pattern,
3737
return !value.empty();
3838
});
3939

40+
// numa
41+
DEFINE_bool(numa_bind_enable, false, "enable numa bind");
42+
DEFINE_int32(numa_bind_node, 0, "numa bind node");
43+
4044
} // namespace dingofs

src/common/options/common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ DECLARE_bool(log_clean_enable);
3333
DECLARE_int32(log_retention_seconds);
3434
DECLARE_string(log_clean_filter_pattern);
3535

36+
// numa
37+
DECLARE_bool(numa_bind_enable);
38+
DECLARE_int32(numa_bind_node);
39+
3640
} // namespace dingofs
3741

3842
#endif // DINGOFS_COMMON_OPTIONS_COMMON_OPTION_H_

src/mds/main.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "mds/common/helper.h"
3030
#include "mds/server.h"
3131
#include "utils/daemonize.h"
32+
#include "utils/numa_manager.h"
3233

3334
DEFINE_string(storage_url, "file://./conf/coor_list", "storage url, e.g. file://<path> or list://<addr1>");
3435

@@ -298,6 +299,13 @@ int main(int argc, char* argv[]) {
298299
return 1;
299300
}
300301

302+
// numa bind to node to avoid performance degradation caused by
303+
// cross-node memory access
304+
auto& numa_manager = dingofs::utils::NumaManager::GetInstance();
305+
if (dingofs::FLAGS_numa_bind_enable && numa_manager.Available()) {
306+
numa_manager.BindNode(dingofs::FLAGS_numa_bind_node);
307+
}
308+
301309
// print config info
302310
dingofs::Helper::PrintConfigInfo(GenConfigs());
303311

src/utils/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(dingofs_utils
1919
throttle.cc
2020
uuid.cc
2121
logclean_manager.cc
22+
numa_manager.cc
2223
)
2324

2425
target_link_libraries(dingofs_utils
@@ -27,4 +28,5 @@ target_link_libraries(dingofs_utils
2728
brpc::brpc
2829
glog::glog
2930
gflags::gflags
31+
numa
3032
)

src/utils/numa_manager.cc

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright (c) 2025 dingodb.com, Inc. All Rights Reserved
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "utils/numa_manager.h"
16+
17+
#include <numa.h>
18+
#include <numaif.h>
19+
#include <sched.h>
20+
#include <unistd.h>
21+
22+
#include <sstream>
23+
#include <thread>
24+
25+
#include "glog/logging.h"
26+
27+
namespace dingofs {
28+
namespace utils {
29+
30+
NumaManager& NumaManager::GetInstance() {
31+
static NumaManager instance;
32+
return instance;
33+
}
34+
35+
NumaManager::NumaManager() {
36+
available_ = numa_available() >= 0;
37+
38+
if (!available_) {
39+
nodes_ = 1;
40+
cpus_ = std::thread::hardware_concurrency();
41+
return;
42+
}
43+
44+
nodes_ = numa_max_node() + 1;
45+
cpus_ = numa_num_configured_cpus();
46+
}
47+
48+
void NumaManager::CheckNode(int node) const {
49+
CHECK(node >= 0 && node < nodes_) << "invalid numa node: " << node;
50+
}
51+
52+
std::vector<int> NumaManager::GetNodeCpus(const int node) const {
53+
CheckNode(node);
54+
55+
std::vector<int> cpulist;
56+
57+
struct bitmask* mask = numa_allocate_cpumask();
58+
numa_node_to_cpus(node, mask);
59+
60+
for (int i = 0; i < cpus_; ++i) {
61+
if (numa_bitmask_isbitset(mask, i)) {
62+
cpulist.push_back(i);
63+
}
64+
}
65+
66+
numa_free_cpumask(mask);
67+
return cpulist;
68+
}
69+
70+
void NumaManager::BindCpuNode(const int node) { BindCpuNodes({node}); }
71+
72+
void NumaManager::BindCpuNodes(const std::vector<int>& nodes) {
73+
CHECK(!nodes.empty());
74+
75+
struct bitmask* cpumask = numa_allocate_cpumask();
76+
struct bitmask* tmpmask = numa_allocate_cpumask();
77+
78+
numa_bitmask_clearall(cpumask);
79+
80+
for (int node : nodes) {
81+
CheckNode(node);
82+
83+
numa_bitmask_clearall(tmpmask);
84+
numa_node_to_cpus(node, tmpmask);
85+
86+
for (int cpu = 0; cpu < cpus_; ++cpu) {
87+
if (numa_bitmask_isbitset(tmpmask, cpu)) {
88+
numa_bitmask_setbit(cpumask, cpu);
89+
}
90+
}
91+
}
92+
93+
CHECK(numa_sched_setaffinity(0, cpumask) == 0)
94+
<< "numa_sched_setaffinity failed";
95+
96+
numa_free_cpumask(tmpmask);
97+
numa_free_cpumask(cpumask);
98+
}
99+
100+
void NumaManager::BindMemPreferredNode(int node) {
101+
CheckNode(node);
102+
numa_set_preferred(node);
103+
}
104+
105+
void NumaManager::BindMemNode(const int node) { BindMemNodes({node}); }
106+
107+
void NumaManager::BindMemNodes(const std::vector<int>& nodes) {
108+
CHECK(!nodes.empty()) << "nodes cannot be empty";
109+
110+
struct bitmask* mask = numa_allocate_nodemask();
111+
numa_bitmask_clearall(mask);
112+
113+
for (int node : nodes) {
114+
CheckNode(node);
115+
numa_bitmask_setbit(mask, node);
116+
}
117+
118+
numa_set_membind(mask);
119+
120+
numa_free_nodemask(mask);
121+
}
122+
123+
void NumaManager::BindNode(int node) {
124+
BindCpuNode(node);
125+
BindMemNode(node);
126+
}
127+
128+
void NumaManager::BindNodes(const std::vector<int>& nodes) {
129+
CHECK(!nodes.empty()) << "nodes cannot be empty";
130+
131+
BindCpuNodes(nodes);
132+
BindMemNodes(nodes);
133+
}
134+
135+
int NumaManager::CpuToNode(int cpu) const { return numa_node_of_cpu(cpu); }
136+
137+
void NumaManager::InterleaveAll() const {
138+
struct bitmask* mask = numa_allocate_nodemask();
139+
140+
for (int i = 0; i < nodes_; ++i) {
141+
numa_bitmask_setbit(mask, i);
142+
}
143+
144+
numa_set_interleave_mask(mask);
145+
numa_free_nodemask(mask);
146+
}
147+
148+
void NumaManager::BindThreadToCpu(const int id, const int cpu) const {
149+
CHECK(cpu >= 0 && cpu < cpus_);
150+
151+
cpu_set_t cpuset;
152+
CPU_ZERO(&cpuset);
153+
CPU_SET(cpu, &cpuset);
154+
155+
CHECK(sched_setaffinity(id, sizeof(cpuset), &cpuset) == 0)
156+
<< "sched_setaffinity failed";
157+
}
158+
159+
// 0 means current thread
160+
void NumaManager::BindCurrentThreadToCpu(const int cpu) const {
161+
BindThreadToCpu(0, cpu);
162+
}
163+
164+
void NumaManager::PrintTopology() const {
165+
std::stringstream ss;
166+
167+
ss << "NUMA nodes: " << nodes_ << "\n";
168+
ss << "CPU count : " << cpus_ << "\n";
169+
170+
for (int i = 0; i < nodes_; ++i) {
171+
auto cpus = GetNodeCpus(i);
172+
173+
ss << "node " << i << " cpus: ";
174+
for (auto c : cpus) {
175+
ss << c << " ";
176+
}
177+
ss << "\n";
178+
}
179+
180+
LOG(INFO) << ss.str();
181+
}
182+
183+
} // namespace utils
184+
} // namespace dingofs

src/utils/numa_manager.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2025 dingodb.com, Inc. All Rights Reserved
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef SRC_UTILS_NUMA_MANAGER_H_
16+
#define SRC_UTILS_NUMA_MANAGER_H_
17+
18+
#include <vector>
19+
20+
namespace dingofs {
21+
namespace utils {
22+
23+
class NumaManager {
24+
public:
25+
static NumaManager& GetInstance();
26+
27+
bool Available() const { return available_; }
28+
29+
int NodeCount() const { return nodes_; }
30+
31+
int CpuCount() const { return cpus_; }
32+
33+
std::vector<int> GetNodeCpus(int node) const;
34+
35+
void BindCpuNode(int node);
36+
37+
void BindCpuNodes(const std::vector<int>& nodes);
38+
39+
void BindMemPreferredNode(int node);
40+
41+
void BindMemNode(int node);
42+
43+
void BindMemNodes(const std::vector<int>& nodes);
44+
45+
void BindNode(int node);
46+
47+
void BindNodes(const std::vector<int>& nodes);
48+
49+
int CpuToNode(int cpu) const;
50+
51+
void InterleaveAll() const;
52+
53+
void BindThreadToCpu(int id, int cpu) const;
54+
55+
void BindCurrentThreadToCpu(int cpu) const;
56+
57+
void PrintTopology() const;
58+
59+
public:
60+
NumaManager(const NumaManager&) = delete;
61+
NumaManager& operator=(const NumaManager&) = delete;
62+
63+
private:
64+
NumaManager();
65+
66+
void CheckNode(int node) const;
67+
68+
private:
69+
bool available_;
70+
int nodes_;
71+
int cpus_;
72+
};
73+
74+
} // namespace utils
75+
} // namespace dingofs
76+
77+
#endif // SRC_UTILS_NUMA_MANAGER_H_

0 commit comments

Comments
 (0)