| 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 | |