1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2012, 2015, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /**************************************************//** |
21 | @file include/ut0counter.h |
22 | |
23 | Counter utility class |
24 | |
25 | Created 2012/04/12 by Sunny Bains |
26 | *******************************************************/ |
27 | |
28 | #ifndef ut0counter_h |
29 | #define ut0counter_h |
30 | |
31 | #include <my_rdtsc.h> |
32 | #include "univ.i" |
33 | #include "os0thread.h" |
34 | |
35 | /** CPU cache line size */ |
36 | #ifdef CPU_LEVEL1_DCACHE_LINESIZE |
37 | # define CACHE_LINE_SIZE CPU_LEVEL1_DCACHE_LINESIZE |
38 | #else |
39 | # error CPU_LEVEL1_DCACHE_LINESIZE is undefined |
40 | #endif /* CPU_LEVEL1_DCACHE_LINESIZE */ |
41 | |
42 | /** Default number of slots to use in ib_counter_t */ |
43 | #define IB_N_SLOTS 64 |
44 | |
45 | /** Get the offset into the counter array. */ |
46 | template <typename Type, int N> |
47 | struct generic_indexer_t { |
48 | /** @return offset within m_counter */ |
49 | static size_t offset(size_t index) UNIV_NOTHROW |
50 | { |
51 | return(((index % N) + 1) * (CACHE_LINE_SIZE / sizeof(Type))); |
52 | } |
53 | }; |
54 | |
55 | /** Use the result of my_timer_cycles(), which mainly uses RDTSC for cycles, |
56 | to index into the counter array. See the comments for my_timer_cycles() */ |
57 | template <typename Type=ulint, int N=1> |
58 | struct counter_indexer_t : public generic_indexer_t<Type, N> { |
59 | /** @return result from RDTSC or similar functions. */ |
60 | static size_t get_rnd_index() UNIV_NOTHROW |
61 | { |
62 | size_t c = static_cast<size_t>(my_timer_cycles()); |
63 | |
64 | if (c != 0) { |
65 | return(c); |
66 | } else { |
67 | /* We may go here if my_timer_cycles() returns 0, |
68 | so we have to have the plan B for the counter. */ |
69 | #if !defined(_WIN32) |
70 | return(size_t(os_thread_get_curr_id())); |
71 | #else |
72 | LARGE_INTEGER cnt; |
73 | QueryPerformanceCounter(&cnt); |
74 | |
75 | return(static_cast<size_t>(cnt.QuadPart)); |
76 | #endif /* !_WIN32 */ |
77 | } |
78 | } |
79 | |
80 | /** @return a random offset to the array */ |
81 | static size_t get_rnd_offset() UNIV_NOTHROW |
82 | { |
83 | return(generic_indexer_t<Type, N>::offset(get_rnd_index())); |
84 | } |
85 | }; |
86 | |
87 | #define default_indexer_t counter_indexer_t |
88 | |
89 | /** Class for using fuzzy counters. The counter is not protected by any |
90 | mutex and the results are not guaranteed to be 100% accurate but close |
91 | enough. Creates an array of counters and separates each element by the |
92 | CACHE_LINE_SIZE bytes */ |
93 | template < |
94 | typename Type, |
95 | int N = IB_N_SLOTS, |
96 | template<typename, int> class Indexer = default_indexer_t> |
97 | struct MY_ALIGNED(CACHE_LINE_SIZE) ib_counter_t |
98 | { |
99 | #ifdef UNIV_DEBUG |
100 | ~ib_counter_t() |
101 | { |
102 | size_t n = (CACHE_LINE_SIZE / sizeof(Type)); |
103 | |
104 | /* Check that we aren't writing outside our defined bounds. */ |
105 | for (size_t i = 0; i < UT_ARR_SIZE(m_counter); i += n) { |
106 | for (size_t j = 1; j < n - 1; ++j) { |
107 | ut_ad(m_counter[i + j] == 0); |
108 | } |
109 | } |
110 | } |
111 | #endif /* UNIV_DEBUG */ |
112 | |
113 | /** Increment the counter by 1. */ |
114 | void inc() UNIV_NOTHROW { add(1); } |
115 | |
116 | /** Increment the counter by 1. |
117 | @param[in] index a reasonably thread-unique identifier */ |
118 | void inc(size_t index) UNIV_NOTHROW { add(index, 1); } |
119 | |
120 | /** Add to the counter. |
121 | @param[in] n amount to be added */ |
122 | void add(Type n) UNIV_NOTHROW { add(m_policy.get_rnd_offset(), n); } |
123 | |
124 | /** Add to the counter. |
125 | @param[in] index a reasonably thread-unique identifier |
126 | @param[in] n amount to be added */ |
127 | void add(size_t index, Type n) UNIV_NOTHROW { |
128 | size_t i = m_policy.offset(index); |
129 | |
130 | ut_ad(i < UT_ARR_SIZE(m_counter)); |
131 | |
132 | m_counter[i] += n; |
133 | } |
134 | |
135 | /* @return total value - not 100% accurate, since it is not atomic. */ |
136 | operator Type() const UNIV_NOTHROW { |
137 | Type total = 0; |
138 | |
139 | for (size_t i = 0; i < N; ++i) { |
140 | total += m_counter[m_policy.offset(i)]; |
141 | } |
142 | |
143 | return(total); |
144 | } |
145 | |
146 | private: |
147 | /** Indexer into the array */ |
148 | Indexer<Type, N>m_policy; |
149 | |
150 | /** Slot 0 is unused. */ |
151 | Type m_counter[(N + 1) * (CACHE_LINE_SIZE / sizeof(Type))]; |
152 | }; |
153 | |
154 | #endif /* ut0counter_h */ |
155 | |