|
| 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 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 */ |
0 commit comments