| 1 | /* |
| 2 | Copyright (c) 2012, Oracle and/or its affiliates. All Rights Reserved. |
| 3 | This program is free software; you can redistribute it and/or modify it under |
| 4 | the terms of the GNU General Public License as published by the Free Software |
| 5 | Foundation; version 2 of the License. |
| 6 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 7 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 8 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 9 | You should have received a copy of the GNU General Public License along with |
| 10 | this program; if not, write to the Free Software Foundation, Inc., |
| 11 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
| 12 | *****************************************************************************/ |
| 13 | |
| 14 | /**************************************************//** |
| 15 | @file include/ut0counter.h |
| 16 | Counter utility class |
| 17 | Created 2012/04/12 by Sunny Bains |
| 18 | *******************************************************/ |
| 19 | |
| 20 | #ifndef UT0COUNTER_H |
| 21 | #define UT0COUNTER_H |
| 22 | |
| 23 | #include <string.h> |
| 24 | |
| 25 | /** CPU cache line size */ |
| 26 | #define CACHE_LINE_SIZE 64 |
| 27 | |
| 28 | /** Default number of slots to use in ib_counter_t */ |
| 29 | #define IB_N_SLOTS 64 |
| 30 | |
| 31 | #ifdef __WIN__ |
| 32 | #define get_curr_thread_id() GetCurrentThreadId() |
| 33 | #else |
| 34 | #define get_curr_thread_id() pthread_self() |
| 35 | #endif |
| 36 | |
| 37 | #define UT_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
| 38 | |
| 39 | /** Get the offset into the counter array. */ |
| 40 | template <typename Type, int N> |
| 41 | struct generic_indexer_t { |
| 42 | /** Default constructor/destructor should be OK. */ |
| 43 | |
| 44 | /** @return offset within m_counter */ |
| 45 | size_t offset(size_t index) const { |
| 46 | return(((index % N) + 1) * (CACHE_LINE_SIZE / sizeof(Type))); |
| 47 | } |
| 48 | }; |
| 49 | |
| 50 | #ifdef HAVE_SCHED_GETCPU |
| 51 | //#include <utmpx.h> // Including this causes problems with EMPTY symbol |
| 52 | #include <sched.h> // Include this instead |
| 53 | /** Use the cpu id to index into the counter array. If it fails then |
| 54 | use the thread id. */ |
| 55 | template <typename Type, int N> |
| 56 | struct get_sched_indexer_t : public generic_indexer_t<Type, N> { |
| 57 | /** Default constructor/destructor should be OK. */ |
| 58 | |
| 59 | /* @return result from sched_getcpu(), the thread id if it fails. */ |
| 60 | size_t get_rnd_index() const { |
| 61 | |
| 62 | size_t cpu = sched_getcpu(); |
| 63 | if (cpu == (size_t) -1) { |
| 64 | cpu = get_curr_thread_id(); |
| 65 | } |
| 66 | |
| 67 | return(cpu); |
| 68 | } |
| 69 | }; |
| 70 | #endif /* HAVE_SCHED_GETCPU */ |
| 71 | |
| 72 | /** Use the thread id to index into the counter array. */ |
| 73 | template <typename Type, int N> |
| 74 | struct thread_id_indexer_t : public generic_indexer_t<Type, N> { |
| 75 | /** Default constructor/destructor should are OK. */ |
| 76 | |
| 77 | /* @return a random number, currently we use the thread id. Where |
| 78 | thread id is represented as a pointer, it may not work as |
| 79 | effectively. */ |
| 80 | size_t get_rnd_index() const { |
| 81 | return (size_t)get_curr_thread_id(); |
| 82 | } |
| 83 | }; |
| 84 | |
| 85 | /** For counters wher N=1 */ |
| 86 | template <typename Type, int N=1> |
| 87 | struct single_indexer_t { |
| 88 | /** Default constructor/destructor should are OK. */ |
| 89 | |
| 90 | /** @return offset within m_counter */ |
| 91 | size_t offset(size_t index) const { |
| 92 | DBUG_ASSERT(N == 1); |
| 93 | return((CACHE_LINE_SIZE / sizeof(Type))); |
| 94 | } |
| 95 | |
| 96 | /* @return 1 */ |
| 97 | size_t get_rnd_index() const { |
| 98 | DBUG_ASSERT(N == 1); |
| 99 | return(1); |
| 100 | } |
| 101 | }; |
| 102 | |
| 103 | /** Class for using fuzzy counters. The counter is not protected by any |
| 104 | mutex and the results are not guaranteed to be 100% accurate but close |
| 105 | enough. Creates an array of counters and separates each element by the |
| 106 | CACHE_LINE_SIZE bytes */ |
| 107 | template < |
| 108 | typename Type, |
| 109 | int N = IB_N_SLOTS, |
| 110 | template<typename, int> class Indexer = thread_id_indexer_t> |
| 111 | class ib_counter_t { |
| 112 | public: |
| 113 | ib_counter_t() { memset(m_counter, 0x0, sizeof(m_counter)); } |
| 114 | |
| 115 | ~ib_counter_t() |
| 116 | { |
| 117 | DBUG_ASSERT(validate()); |
| 118 | } |
| 119 | |
| 120 | bool validate() { |
| 121 | #ifdef UNIV_DEBUG |
| 122 | size_t n = (CACHE_LINE_SIZE / sizeof(Type)); |
| 123 | |
| 124 | /* Check that we aren't writing outside our defined bounds. */ |
| 125 | for (size_t i = 0; i < UT_ARRAY_SIZE(m_counter); i += n) { |
| 126 | for (size_t j = 1; j < n - 1; ++j) { |
| 127 | DBUG_ASSERT(m_counter[i + j] == 0); |
| 128 | } |
| 129 | } |
| 130 | #endif /* UNIV_DEBUG */ |
| 131 | return(true); |
| 132 | } |
| 133 | |
| 134 | /** If you can't use a good index id. Increment by 1. */ |
| 135 | void inc() { add(1); } |
| 136 | |
| 137 | /** If you can't use a good index id. |
| 138 | * @param n - is the amount to increment */ |
| 139 | void add(Type n) { |
| 140 | size_t i = m_policy.offset(m_policy.get_rnd_index()); |
| 141 | |
| 142 | DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter)); |
| 143 | |
| 144 | m_counter[i] += n; |
| 145 | } |
| 146 | |
| 147 | /** Use this if you can use a unique indentifier, saves a |
| 148 | call to get_rnd_index(). |
| 149 | @param i - index into a slot |
| 150 | @param n - amount to increment */ |
| 151 | void add(size_t index, Type n) { |
| 152 | size_t i = m_policy.offset(index); |
| 153 | |
| 154 | DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter)); |
| 155 | |
| 156 | m_counter[i] += n; |
| 157 | } |
| 158 | |
| 159 | /** If you can't use a good index id. Decrement by 1. */ |
| 160 | void dec() { sub(1); } |
| 161 | |
| 162 | /** If you can't use a good index id. |
| 163 | * @param - n is the amount to decrement */ |
| 164 | void sub(Type n) { |
| 165 | size_t i = m_policy.offset(m_policy.get_rnd_index()); |
| 166 | |
| 167 | DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter)); |
| 168 | |
| 169 | m_counter[i] -= n; |
| 170 | } |
| 171 | |
| 172 | /** Use this if you can use a unique indentifier, saves a |
| 173 | call to get_rnd_index(). |
| 174 | @param i - index into a slot |
| 175 | @param n - amount to decrement */ |
| 176 | void sub(size_t index, Type n) { |
| 177 | size_t i = m_policy.offset(index); |
| 178 | |
| 179 | DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter)); |
| 180 | |
| 181 | m_counter[i] -= n; |
| 182 | } |
| 183 | |
| 184 | /* @return total value - not 100% accurate, since it is not atomic. */ |
| 185 | operator Type() const { |
| 186 | Type total = 0; |
| 187 | |
| 188 | for (size_t i = 0; i < N; ++i) { |
| 189 | total += m_counter[m_policy.offset(i)]; |
| 190 | } |
| 191 | |
| 192 | return(total); |
| 193 | } |
| 194 | |
| 195 | private: |
| 196 | /** Indexer into the array */ |
| 197 | Indexer<Type, N>m_policy; |
| 198 | |
| 199 | /** Slot 0 is unused. */ |
| 200 | Type m_counter[(N + 1) * (CACHE_LINE_SIZE / sizeof(Type))]; |
| 201 | }; |
| 202 | |
| 203 | #endif /* UT0COUNTER_H */ |
| 204 | |