forked from max0x7ba/atomic_queue
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpu_base_frequency.cc
More file actions
165 lines (135 loc) · 5.56 KB
/
cpu_base_frequency.cc
File metadata and controls
165 lines (135 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
// Copyright (c) 2019 Maxim Egorushkin. MIT License. See the full licence in file LICENSE.
#include "cpu_base_frequency.h"
#include <fstream>
#include <tuple>
#include <regex>
#include <string>
#include <thread>
#include <system_error>
#include <pthread.h>
#include <dlfcn.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double atomic_queue::cpu_base_frequency() {
std::regex const re("model name\\s*:[^@]+@\\s*([0-9.]+)\\s*GHz");
std::ifstream cpuinfo("/proc/cpuinfo");
std::smatch m;
for(std::string line; getline(cpuinfo, line);)
if(regex_match(line, m, re))
return std::stod(m[1]);
return 1; // Fallback to cycles, should it fail to parse.
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<atomic_queue::CpuTopologyInfo> atomic_queue::get_cpu_topology_info() {
std::vector<CpuTopologyInfo> cpus;
unsigned constexpr M = 3;
using MemberPtr = unsigned CpuTopologyInfo::*;
MemberPtr const member_ptrs[M] = {
&CpuTopologyInfo::socket_id,
&CpuTopologyInfo::core_id,
&CpuTopologyInfo::hw_thread_id
};
std::regex const res[M] = {
std::regex("physical id\\s+:\\s+([0-9]+)"),
std::regex("core id\\s+:\\s+([0-9]+)"),
std::regex("processor\\s+:\\s+([0-9]+)")
};
std::ifstream cpuinfo("/proc/cpuinfo");
std::smatch m;
CpuTopologyInfo element;
unsigned valid_members = 0;
for(std::string line; getline(cpuinfo, line);) {
for(unsigned i = 0, mask = 1; i < M; ++i, mask <<= 1) {
if(valid_members & mask || !regex_match(line, m, res[i]))
continue;
(element.*member_ptrs[i]) = std::stoul(m[1]);
valid_members |= mask;
if(valid_members == ((1u << M) - 1)) {
cpus.push_back(element);
valid_members = 0;
}
break;
}
}
if(std::thread::hardware_concurrency() != cpus.size())
throw std::runtime_error("get_cpu_topology_info() invariant broken.");
return sort_by_hw_thread_id(cpus);
}
std::vector<atomic_queue::CpuTopologyInfo> atomic_queue::sort_by_core_id(std::vector<atomic_queue::CpuTopologyInfo> const& v) {
auto u = v;
std::sort(u.begin(), u.end(), [](auto& a, auto& b) {
return std::tie(a.socket_id, a.core_id, a.hw_thread_id) < std::tie(b.socket_id, b.core_id, b.hw_thread_id);
});
return u;
}
std::vector<atomic_queue::CpuTopologyInfo> atomic_queue::sort_by_hw_thread_id(std::vector<atomic_queue::CpuTopologyInfo> const& v) {
auto u = v;
std::sort(u.begin(), u.end(), [](auto& a, auto& b) {
return a.hw_thread_id < b.hw_thread_id;
});
return u;
}
std::vector<unsigned> atomic_queue::hw_thread_id(std::vector<atomic_queue::CpuTopologyInfo> const& v) {
std::vector<unsigned> u(v.size());
for(unsigned i = 0, j = u.size(); i < j; ++i)
u[i] = v[i].hw_thread_id;
return u;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace {
void set_thread_affinity_(cpu_set_t const& cpuset) {
// TODO: Investigate whether setting the thread affinity after starting the thread can cause the
// thread stack to be on a remote NUMA node.
auto thread = ::pthread_self();
if(int err = ::pthread_setaffinity_np(thread, sizeof cpuset, &cpuset))
throw std::system_error(err, std::system_category(), "pthread_setaffinity_np");
}
int default_thread_affinity = -1;
auto const real_pthread_create = reinterpret_cast<decltype(&pthread_create)>(::dlsym(RTLD_NEXT, "pthread_create"));
} // namespace
void atomic_queue::set_thread_affinity(unsigned hw_thread_id) {
// TODO: Investigate whether setting the thread affinity after starting the thread can cause the
// thread stack to be on a remote NUMA node.
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(hw_thread_id, &cpuset);
set_thread_affinity_(cpuset);
}
void atomic_queue::reset_thread_affinity() {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for(unsigned i = 0, j = std::thread::hardware_concurrency(); i < j; ++i)
CPU_SET(i, &cpuset);
set_thread_affinity_(cpuset);
}
void atomic_queue::set_default_thread_affinity(unsigned hw_thread_id) {
default_thread_affinity = hw_thread_id;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int pthread_create(pthread_t* newthread,
pthread_attr_t const* attr,
void*(*start_routine)(void*),
void* arg)
{
if(!real_pthread_create)
std::abort();
pthread_attr_t attr2;
pthread_attr_t *pattr = const_cast<pthread_attr_t*>(attr);
if(default_thread_affinity >= 0) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(default_thread_affinity, &cpuset);
if(!pattr) {
if(::pthread_attr_init(&attr2))
std::abort();
pattr = &attr2;
}
if(::pthread_attr_setaffinity_np(pattr, sizeof cpuset, &cpuset))
std::abort();
}
int r = real_pthread_create(newthread, pattr, start_routine, arg);
if(pattr == &attr2)
::pthread_attr_destroy(&attr2);
return r;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////