-
Notifications
You must be signed in to change notification settings - Fork 63
/
util.hpp
207 lines (170 loc) · 6.7 KB
/
util.hpp
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* util.hpp
*/
#ifndef UTIL_HPP_
#define UTIL_HPP_
#include <chrono>
#include <string>
#include <cstdio>
#include <memory>
#include <vector>
#include <sstream>
#define UB_CACHE_LINE_SIZE 64
#if USE_LIBPFC
#define IF_PFC(x) x
#else
#define IF_PFC(x)
#endif
// returns the argument followed by a comma, often useful to pass to x-macros
#define APPEND_COMMA(x) x,
template <typename T>
static inline bool is_pow2(T x) {
static_assert(std::is_unsigned<T>::value, "must use unsigned integral types");
return x && !(x & (x - 1));
}
/* use some reasonable default clock to return a point in time measured in nanos,
* which has no relation to wall-clock time (is suitable for measuring intervals)
*/
static inline int64_t nanos() {
auto t = std::chrono::high_resolution_clock::now();
return std::chrono::time_point_cast<std::chrono::nanoseconds>(t).time_since_epoch().count();
}
/**
* @brief Allocate a 2 MB-aligned pointer and set its hugepage status.
*
* Return a pointer to a NEWLY ALLOCATED memory region of at least size,
* aligned to a 2MB boundary and with an effort to ensure the pointer is
* backed by these specified page size. Specifically, it uses
* madvise() to try to force either hugepages or no hugepages.
*
* Many distributions have the sysfs tunable:
* /sys/kernel/mm/transparent_hugepage/enabled
* set to "madvise" and in this case both directions will work.
*
* @param size size of required region
* @param huge true to force 2MB pages, false to force 4k pages
* @return void*
*/
void *new_huge_ptr(size_t size, bool huge = true);
/**
* Return a pointer to a NEWLY ALLOCATED memory region of at least size, aligned to the given alignment.
*
* The returned pointer is freed by passing it to free().
*/
void *new_aligned_pointer(size_t size, size_t alignment);
/**
* Return a pointer to a single static region of the of the given size and alignment.
* The same global region is REUSED for all calls to this function, so it is only appropriate
* for temporary use within a test.
*
* The pointer is allocated in huge pages if possible.
*/
void *aligned_ptr(size_t base_alignment, size_t required_size, bool set_zero = false);
void *misaligned_ptr(size_t base_alignment, size_t required_size, ssize_t misalignment);
/**
* @brief Return pointer to a global storage region of 4k pages.
*
* The same as aligned_pointer but using 4k pages, not huge pages.
*/
void *aligned_ptr_4k(size_t base_alignment, size_t required_size, bool set_zero = false);
/*
* Given a printf-style format and args, return the formatted string as a std::string.
*
* See https://stackoverflow.com/a/26221725/149138.
*/
template<typename ... Args>
std::string string_format(const std::string& format, Args ... args) {
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
std::unique_ptr<char[]> buf( new char[ size ] );
snprintf( buf.get(), size, format.c_str(), args ... );
return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}
/* stricty speaking this overload is not necessary but it avoids a gcc warning about a format operation without args */
static inline std::string string_format(const std::string& format) {
return format;
}
/** make a string like [1,2,3] out of anything supporting std::begin/end and whose elements support ostream << */
template <typename T>
std::string container_to_string(const T& container) {
std::stringstream ss;
ss << "[";
bool first = true;
for (const auto& e : container) {
if (!first) ss << ",";
first = false;
ss << e;
}
ss << "]";
return ss.str();
}
/*
* Split a string delimited by sep.
*
* See https://stackoverflow.com/a/7408245/149138
*/
template <typename S>
static inline std::vector<std::string> split_helper(const std::string &text, S splitfunc) {
using ptype = decltype(splitfunc(text,0));
std::vector<std::string> tokens;
std::size_t start = 0;
ptype result;
while ((result = splitfunc(text, start)).first != std::string::npos) {
tokens.push_back(text.substr(start, result.first - start));
start = result.first + result.second;
}
tokens.push_back(text.substr(start));
return tokens;
}
/** splits a string based on finding occurrences of the entire passed separator string */
static inline std::vector<std::string> split_on_string(const std::string &text, const std::string& sep) {
return split_helper(text, [=](const std::string& s, size_t start) { return std::make_pair(s.find(sep, start), sep.length()); });
}
/** splits on any of the characters in sep_chars */
static inline std::vector<std::string> split_on_any(const std::string &text, const std::string& sep_chars) {
return split_helper(text, [=](const std::string& s, size_t start) { return std::make_pair(s.find_first_of(sep_chars, start), 1); });
}
/** Take a string and escape it so that it will be treated as a literal string in a regex */
std::string escape_for_regex(const std::string& input);
/**
* Returns true if the entire string target matches pattern, where pattern can contain * wildcards
* that match any number of characters.
*/
bool wildcard_match(const std::string& target, const std::string& pattern);
constexpr size_t MAX_SHUFFLED_REGION_SIZE = 400 * 1024 * 1024;
/** the whole cache line object is filled with pointers to the next chunk */
struct CacheLine {
static_assert(UB_CACHE_LINE_SIZE % sizeof(CacheLine *) == 0, "cache line size not a multiple of pointer size");
CacheLine* nexts[UB_CACHE_LINE_SIZE / sizeof(CacheLine *)];
void setNexts(CacheLine* next) {
std::fill(std::begin(nexts), std::end(nexts), next);
}
};
struct region {
size_t size;
void *start; // actually a CacheLine object
};
/**
* Return a region of memory of size bytes, where each cache line sized chunk points to another random chunk
* within the region. The pointers cover all chunks in a cycle of maximum size.
*
* The region_struct is returned by reference and points to a static variable that is overwritten every time
* this function is called.
*
* Non-zero offset means that the returned region will be offset relative to the start of a cache line, e.g.,
* offset 60 could be used to ensure each load crosses a cache line.
*/
region& shuffled_region(const size_t size, const size_t offset = 0);
/**
* Touch each cache line (or other specified stride) in region of size size.
*/
long touch_lines(void *region, size_t size, size_t stride = UB_CACHE_LINE_SIZE);
void flush_caches(size_t working_set = 16 * 1024 * 1024);
/**
* Return the string description of the given system errno
*/
std::string errno_to_str(int e);
/**
* This method always returns zero, but the optimizer doesn't know that.
*/
int always_zero();
#endif /* UTIL_HPP_ */