MySQL 9.1.0
Source Code Documentation
ut0counter.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2012, 2024, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file include/ut0counter.h
29
30 Counter utility class
31
32 Created 2012/04/12 by Sunny Bains
33 *******************************************************/
34
35#ifndef ut0counter_h
36#define ut0counter_h
37
38#include <my_rdtsc.h>
39
40#include "univ.i"
41
42#include "os0thread.h"
43#include "ut0cpu_cache.h"
44#include "ut0dbg.h"
45
46#include <array>
47#include <atomic>
48#include <functional>
49
50/** Default number of slots to use in ib_counter_t */
51constexpr uint32_t IB_N_SLOTS = 64;
52
53/** Get the offset into the counter array. */
54template <typename Type, int N>
56 /** Default constructor/destructor should be OK. */
57
58 /** @return offset within m_counter */
59 static size_t offset(size_t index) UNIV_NOTHROW {
60 return (((index % N) + 1) * (ut::INNODB_CACHE_LINE_SIZE / sizeof(Type)));
61 }
62};
63
64/** Use the result of my_timer_cycles(), which mainly uses RDTSC for cycles,
65to index into the counter array. See the comments for my_timer_cycles() */
66template <typename Type = ulint, int N = 1>
67struct counter_indexer_t : public generic_indexer_t<Type, N> {
68 /** Default constructor/destructor should be OK. */
69
70 enum { fast = 1 };
71
72 /** @return result from RDTSC or similar functions. */
73 static size_t get_rnd_index() UNIV_NOTHROW {
74 size_t c = static_cast<size_t>(my_timer_cycles());
75
76 if (c != 0) {
77 return (c);
78 } else {
79 /* We may go here if my_timer_cycles() returns 0,
80 so we have to have the plan B for the counter. */
81#if !defined(_WIN32)
83#else
84 LARGE_INTEGER cnt;
85 QueryPerformanceCounter(&cnt);
86
87 return static_cast<size_t>(cnt.QuadPart);
88#endif /* !_WIN32 */
89 }
90 }
91};
92
93/** For counters where N=1 */
94template <typename Type = ulint, int N = 1>
96 /** Default constructor/destructor should are OK. */
97
98 enum { fast = 0 };
99
100 /** @return offset within m_counter */
101 static size_t offset(size_t index [[maybe_unused]]) UNIV_NOTHROW {
102 static_assert(N == 1);
103 return ((ut::INNODB_CACHE_LINE_SIZE / sizeof(Type)));
104 }
105
106 /** @return 1 */
107 static size_t get_rnd_index() UNIV_NOTHROW {
108 static_assert(N == 1);
109 return (1);
110 }
111};
112
113#define default_indexer_t counter_indexer_t
114
115/** Class for using fuzzy counters. The counter is not protected by any
116mutex and the results are not guaranteed to be 100% accurate but close
117enough. Creates an array of counters and separates each element by the
118ut::INNODB_CACHE_LINE_SIZE bytes */
119template <typename Type, int N = IB_N_SLOTS,
120 template <typename, int> class Indexer = default_indexer_t>
122 public:
123 ib_counter_t() { memset(m_counter, 0x0, sizeof(m_counter)); }
124
126
127 static bool is_fast() { return (Indexer<Type, N>::fast); }
128
130#ifdef UNIV_DEBUG
131 size_t n = (ut::INNODB_CACHE_LINE_SIZE / sizeof(Type));
132
133 /* Check that we aren't writing outside our defined bounds. */
134 for (size_t i = 0; i < UT_ARR_SIZE(m_counter); i += n) {
135 for (size_t j = 1; j < n - 1; ++j) {
136 ut_ad(m_counter[i + j] == 0);
137 }
138 }
139#endif /* UNIV_DEBUG */
140 return (true);
141 }
142
143 /** If you can't use a good index id. Increment by 1. */
144 void inc() UNIV_NOTHROW { add(1); }
145
146 /** If you can't use a good index id.
147 @param n is the amount to increment */
149 size_t i = m_policy.offset(m_policy.get_rnd_index());
150
152
153 m_counter[i] += n;
154 }
155
156 /** Use this if you can use a unique identifier, saves a
157 call to get_rnd_index().
158 @param index index into a slot
159 @param n amount to increment */
160 void add(size_t index, Type n) UNIV_NOTHROW {
161 size_t i = m_policy.offset(index);
162
164
165 m_counter[i] += n;
166 }
167
168 /** If you can't use a good index id. Decrement by 1. */
169 void dec() UNIV_NOTHROW { sub(1); }
170
171 /** If you can't use a good index id.
172 @param n the amount to decrement */
174 size_t i = m_policy.offset(m_policy.get_rnd_index());
175
177
178 m_counter[i] -= n;
179 }
180
181 /** Use this if you can use a unique identifier, saves a
182 call to get_rnd_index().
183 @param index index into a slot
184 @param n amount to decrement */
185 void sub(size_t index, Type n) UNIV_NOTHROW {
186 size_t i = m_policy.offset(index);
187
189
190 m_counter[i] -= n;
191 }
192
193 /* @return total value - not 100% accurate, since it is not atomic. */
194 operator Type() const UNIV_NOTHROW {
195 Type total = 0;
196
197 for (size_t i = 0; i < N; ++i) {
198 total += m_counter[m_policy.offset(i)];
199 }
200
201 return (total);
202 }
203
204 Type operator[](size_t index) const UNIV_NOTHROW {
205 size_t i = m_policy.offset(index);
206
208
209 return (m_counter[i]);
210 }
211
212 private:
213 /** Indexer into the array */
214 Indexer<Type, N> m_policy;
215
216 /** Slot 0 is unused. */
218};
219
220/** Sharded atomic counter. */
221namespace Counter {
222
223using Type = uint64_t;
224
225using N = std::atomic<Type>;
226
227static_assert(ut::INNODB_CACHE_LINE_SIZE >= sizeof(N),
228 "Atomic counter size > ut::INNODB_CACHE_LINE_SIZE");
229
230using Pad = byte[ut::INNODB_CACHE_LINE_SIZE - sizeof(N)];
231
232/** Counter shard. */
233struct Shard {
234 /** Separate on cache line. */
236
237 /** Sharded counter. */
239};
240
241using Function = std::function<void(const Type)>;
242
243/** Relaxed order by default. */
244constexpr auto Memory_order = std::memory_order_relaxed;
245
246template <size_t COUNT = 128>
247struct Shards {
248 /* Shard array. */
249 std::array<Shard, COUNT> m_arr{};
250
251 /* Memory order for the shards. */
252 std::memory_order m_memory_order{Memory_order};
253
254 /** Override default memory order.
255 @param[in] memory_order memory order */
256 void set_order(std::memory_order memory_order) {
257 m_memory_order = memory_order;
258 }
259};
260
261/** Increment the counter for a shard by n.
262@param[in,out] shards Sharded counter to increment.
263@param[in] id Shard key.
264@param[in] n Number to add.
265@return previous value. */
266template <size_t COUNT>
267inline Type add(Shards<COUNT> &shards, size_t id, size_t n) {
268 auto &shard_arr = shards.m_arr;
269 auto order = shards.m_memory_order;
270
271 return (shard_arr[id % shard_arr.size()].m_n.fetch_add(n, order));
272}
273
274/** Decrement the counter for a shard by n.
275@param[in,out] shards Sharded counter to increment.
276@param[in] id Shard key.
277@param[in] n Number to add.
278@return previous value. */
279template <size_t COUNT>
280inline Type sub(Shards<COUNT> &shards, size_t id, size_t n) {
281 ut_ad(get(shards, id) >= n);
282
283 auto &shard_arr = shards.m_arr;
284 auto order = shards.m_memory_order;
285 return (shard_arr[id % shard_arr.size()].m_n.fetch_sub(n, order));
286}
287
288/** Increment the counter of a shard by 1.
289@param[in,out] shards Sharded counter to increment.
290@param[in] id Shard key.
291@return previous value. */
292template <size_t COUNT>
293inline Type inc(Shards<COUNT> &shards, size_t id) {
294 return (add(shards, id, 1));
295}
296
297/** Decrement the counter of a shard by 1.
298@param[in,out] shards Sharded counter to decrement.
299@param[in] id Shard key.
300@return previous value. */
301template <size_t COUNT>
302inline Type dec(Shards<COUNT> &shards, size_t id) {
303 return (sub(shards, id, 1));
304}
305
306/** Get the counter value for a shard.
307@param[in,out] shards Sharded counter to increment.
308@param[in] id Shard key.
309@return current value. */
310template <size_t COUNT>
311inline Type get(const Shards<COUNT> &shards, size_t id) noexcept {
312 auto &shard_arr = shards.m_arr;
313 auto order = shards.m_memory_order;
314
315 return (shard_arr[id % shard_arr.size()].m_n.load(order));
316}
317
318/** Iterate over the shards.
319@param[in] shards Shards to iterate over
320@param[in] f Callback function
321*/
322template <size_t COUNT>
323inline void for_each(const Shards<COUNT> &shards, Function &&f) noexcept {
324 for (const auto &shard : shards.m_arr) {
325 f(shard.m_n);
326 }
327}
328
329/** Get the total value of all shards.
330@param[in] shards Shards to sum.
331@return total value. */
332template <size_t COUNT>
333inline Type total(const Shards<COUNT> &shards) noexcept {
334 Type n = 0;
335
336 for_each(shards, [&](const Type count) { n += count; });
337
338 return (n);
339}
340
341/** Clear the counter - reset to 0.
342@param[in,out] shards Shards to clear. */
343template <size_t COUNT>
344inline void clear(Shards<COUNT> &shards) noexcept {
345 for (auto &shard : shards.m_arr) {
346 shard.m_n.store(0, shards.m_memory_order);
347 }
348}
349
350/** Copy the counters, overwrite destination.
351@param[in,out] dst Destination shard
352@param[in] src Source shard. */
353template <size_t COUNT>
354inline void copy(Shards<COUNT> &dst, const Shards<COUNT> &src) noexcept {
355 size_t i{0};
356 for_each(src, [&](const Type count) {
357 dst.m_arr[i++].m_n.store(count, dst.m_memory_order);
358 });
359}
360
361/** Accumulate the counters, add source to destination.
362@param[in,out] dst Destination shard
363@param[in] src Source shard. */
364template <size_t COUNT>
365inline void add(Shards<COUNT> &dst, const Shards<COUNT> &src) noexcept {
366 size_t i{0};
367 for_each(src, [&](const Type count) {
368 dst.m_arr[i++].m_n.fetch_add(count, dst.m_memory_order);
369 });
370}
371} // namespace Counter
372
373#endif /* ut0counter_h */
Class for using fuzzy counters.
Definition: ut0counter.h:121
static bool is_fast()
Definition: ut0counter.h:127
void sub(Type n) 1
If you can't use a good index id.
Definition: ut0counter.h:173
void add(Type n) 1
If you can't use a good index id.
Definition: ut0counter.h:148
void add(size_t index, Type n) 1
Use this if you can use a unique identifier, saves a call to get_rnd_index().
Definition: ut0counter.h:160
ib_counter_t()
Definition: ut0counter.h:123
bool validate() 1
Definition: ut0counter.h:129
void dec() 1
If you can't use a good index id.
Definition: ut0counter.h:169
Type operator[](size_t index) const 1
Definition: ut0counter.h:204
Type m_counter[(N+1) *(ut::INNODB_CACHE_LINE_SIZE/sizeof(Type))]
Slot 0 is unused.
Definition: ut0counter.h:217
Indexer< Type, N > m_policy
Indexer into the array.
Definition: ut0counter.h:214
void sub(size_t index, Type n) 1
Use this if you can use a unique identifier, saves a call to get_rnd_index().
Definition: ut0counter.h:185
~ib_counter_t()
Definition: ut0counter.h:125
void inc() 1
If you can't use a good index id.
Definition: ut0counter.h:144
Multi-platform timer code.
ulonglong my_timer_cycles(void)
A cycle timer.
Definition: my_rdtsc.cc:98
static int count
Definition: myisam_ftdump.cc:45
Sharded atomic counter.
Definition: ut0counter.h:221
Type inc(Shards< COUNT > &shards, size_t id)
Increment the counter of a shard by 1.
Definition: ut0counter.h:293
std::function< void(const Type)> Function
Definition: ut0counter.h:241
uint64_t Type
Definition: ut0counter.h:223
Type add(Shards< COUNT > &shards, size_t id, size_t n)
Increment the counter for a shard by n.
Definition: ut0counter.h:267
byte[ut::INNODB_CACHE_LINE_SIZE - sizeof(N)] Pad
Definition: ut0counter.h:230
void copy(Shards< COUNT > &dst, const Shards< COUNT > &src) noexcept
Copy the counters, overwrite destination.
Definition: ut0counter.h:354
constexpr auto Memory_order
Relaxed order by default.
Definition: ut0counter.h:244
Type dec(Shards< COUNT > &shards, size_t id)
Decrement the counter of a shard by 1.
Definition: ut0counter.h:302
Type total(const Shards< COUNT > &shards) noexcept
Get the total value of all shards.
Definition: ut0counter.h:333
Type sub(Shards< COUNT > &shards, size_t id, size_t n)
Decrement the counter for a shard by n.
Definition: ut0counter.h:280
void for_each(const Shards< COUNT > &shards, Function &&f) noexcept
Iterate over the shards.
Definition: ut0counter.h:323
std::atomic< Type > N
Definition: ut0counter.h:225
void clear(Shards< COUNT > &shards) noexcept
Clear the counter - reset to 0.
Definition: ut0counter.h:344
Type get(const Shards< COUNT > &shards, size_t id) noexcept
Get the counter value for a shard.
Definition: ut0counter.h:311
constexpr size_t INNODB_CACHE_LINE_SIZE
CPU cache line size.
Definition: ut0cpu_cache.h:41
const thread_local size_t this_thread_hash
The hash value of the current thread's id.
Definition: os0thread.h:74
The interface to the operating system process and thread control primitives.
Counter shard.
Definition: ut0counter.h:233
N m_n
Sharded counter.
Definition: ut0counter.h:238
Pad m_pad
Separate on cache line.
Definition: ut0counter.h:235
Definition: ut0counter.h:247
void set_order(std::memory_order memory_order)
Override default memory order.
Definition: ut0counter.h:256
std::array< Shard, COUNT > m_arr
Definition: ut0counter.h:249
std::memory_order m_memory_order
Definition: ut0counter.h:252
Use the result of my_timer_cycles(), which mainly uses RDTSC for cycles, to index into the counter ar...
Definition: ut0counter.h:67
static size_t get_rnd_index() 1
Definition: ut0counter.h:73
@ fast
Definition: ut0counter.h:70
Get the offset into the counter array.
Definition: ut0counter.h:55
static size_t offset(size_t index) 1
Default constructor/destructor should be OK.
Definition: ut0counter.h:59
For counters where N=1.
Definition: ut0counter.h:95
static size_t get_rnd_index() 1
Definition: ut0counter.h:107
@ fast
Definition: ut0counter.h:98
static size_t offset(size_t index) 1
Definition: ut0counter.h:101
Version control for database, common definitions, and include files.
#define UNIV_NOTHROW
Definition: univ.i:456
#define UT_ARR_SIZE(a)
Definition: univ.i:524
constexpr uint32_t IB_N_SLOTS
Default number of slots to use in ib_counter_t.
Definition: ut0counter.h:51
#define default_indexer_t
Definition: ut0counter.h:113
Utilities related to CPU cache.
Debug utilities for Innobase.
#define ut_ad(EXPR)
Debug assertion.
Definition: ut0dbg.h:105
int n
Definition: xcom_base.cc:509