summaryrefslogtreecommitdiff
path: root/storage/innobase
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2020-06-15 01:47:45 +0200
committerVladislav Vaintroub <wlad@mariadb.com>2020-06-16 12:12:37 +0200
commit72fc4f3fef52629903f8397a0dc0083c94f8de09 (patch)
tree13f8360ee737007c98c5af514cd3f3c48a796d3f /storage/innobase
parent7803601dcb8e40746a10a4012b0c8eb08274db3e (diff)
downloadmariadb-git-72fc4f3fef52629903f8397a0dc0083c94f8de09.tar.gz
MDEV-22841 ut_new_get_key_by_file is unnecessarily expensive, followup
Make ut_new_get_key_by_file event less expensive remove binary search, compute auto_event_keys offset at compile time.
Diffstat (limited to 'storage/innobase')
-rw-r--r--storage/innobase/include/ut0new.h177
-rw-r--r--storage/innobase/ut/ut0new.cc229
2 files changed, 144 insertions, 262 deletions
diff --git a/storage/innobase/include/ut0new.h b/storage/innobase/include/ut0new.h
index e878ef5ee9c..5137c8f3ae1 100644
--- a/storage/innobase/include/ut0new.h
+++ b/storage/innobase/include/ut0new.h
@@ -148,6 +148,8 @@ InnoDB:
/** Maximum number of retries to allocate memory. */
extern const size_t alloc_max_retries;
+constexpr uint32_t INVALID_AUTOEVENT_IDX = 0xFFFFFFFFU;
+
/** Keys for registering allocations with performance schema.
Pointers to these variables are supplied to PFS code via the pfs_info[]
array and the PFS code initializes them via PSI_MEMORY_CALL(register_memory)().
@@ -180,11 +182,11 @@ ut_new_boot();
/**
Retrieve a memory key (registered with PFS),
-given filename hash of the caller
+given AUTOEVENT_IDX of the caller
-@param[in] filename_hash - FILENAME_HASH value of the caller
+@param[in] autoevent_idx - AUTOEVENT_IDX value of the caller
@return registered memory key or PSI_NOT_INSTRUMENTED */
-PSI_memory_key ut_new_get_key_by_file(uint32_t filename_hash);
+PSI_memory_key ut_new_get_key_by_file(uint32_t autoevent_idx);
#endif /* UNIV_PFS_MEMORY */
@@ -293,7 +295,7 @@ public:
)
{
#ifdef UNIV_PFS_MEMORY
- const PSI_memory_key other_key = other.get_mem_key(0);
+ const PSI_memory_key other_key = other.get_mem_key();
m_key = (other_key != mem_key_std)
? other_key
@@ -315,7 +317,7 @@ public:
#endif /* UNIV_PFS_MEMORY */
}
- pointer allocate(size_type n) { return allocate(n, NULL, 0); }
+ pointer allocate(size_type n) { return allocate(n, NULL, INVALID_AUTOEVENT_IDX); }
/** Allocate a chunk of memory that can hold 'n_elements' objects of
type 'T' and trace the allocation.
@@ -335,7 +337,7 @@ public:
const_pointer,
uint32_t
#ifdef UNIV_PFS_MEMORY
- filename_hash /* filename hash of the caller */
+ autoevent_idx /* AUTOEVENT_IDX of the caller */
#endif
,
bool set_to_zero = false,
@@ -397,7 +399,7 @@ public:
#ifdef UNIV_PFS_MEMORY
ut_new_pfx_t* pfx = static_cast<ut_new_pfx_t*>(ptr);
- allocate_trace(total_bytes, filename_hash, pfx);
+ allocate_trace(total_bytes, autoevent_idx, pfx);
return(reinterpret_cast<pointer>(pfx + 1));
#else
@@ -479,7 +481,7 @@ public:
reallocate(
void* ptr,
size_type n_elements,
- uint32_t filename_hash)
+ uint32_t autoevent_idx)
{
if (n_elements == 0) {
deallocate(static_cast<pointer>(ptr));
@@ -487,7 +489,7 @@ public:
}
if (ptr == NULL) {
- return(allocate(n_elements, NULL, filename_hash, false, false));
+ return(allocate(n_elements, NULL, autoevent_idx, false, false));
}
if (n_elements > max_size()) {
@@ -530,7 +532,7 @@ public:
deallocate_trace(pfx_new);
/* pfx_new is set here to describe the new block. */
- allocate_trace(total_bytes, filename_hash, pfx_new);
+ allocate_trace(total_bytes, autoevent_idx, pfx_new);
return(reinterpret_cast<pointer>(pfx_new + 1));
}
@@ -546,10 +548,10 @@ public:
pointer
new_array(
size_type n_elements,
- uint32_t filename_hash
+ uint32_t autoevent_idx
)
{
- T* p = allocate(n_elements, NULL, filename_hash, false, false);
+ T* p = allocate(n_elements, NULL, autoevent_idx, false, false);
if (p == NULL) {
return(NULL);
@@ -688,16 +690,16 @@ public:
@return performance schema key */
PSI_memory_key
get_mem_key(
- uint32_t filename_hash) const
+ uint32_t autoevent_idx = INVALID_AUTOEVENT_IDX) const
{
if (m_key != PSI_NOT_INSTRUMENTED) {
return(m_key);
}
- if (filename_hash == 0) {
+ if (autoevent_idx == INVALID_AUTOEVENT_IDX) {
return(mem_key_std);
}
- const PSI_memory_key key = ut_new_get_key_by_file(filename_hash);
+ const PSI_memory_key key = ut_new_get_key_by_file(autoevent_idx);
if (key != PSI_NOT_INSTRUMENTED) {
return(key);
@@ -739,16 +741,16 @@ private:
corresponds to "file", that will be used (see ut_new_boot())
4. Otherwise, the name associated with mem_key_other will be used.
@param[in] size number of bytes that were allocated
- @param[in] filename_hash FILENAME_HASH of the caller
+ @param[in] autoevent_idx autoevent_idx of the caller
@param[out] pfx placeholder to store the info which will be
needed when freeing the memory */
void
allocate_trace(
size_t size,
- const uint32_t filename_hash,
+ const uint32_t autoevent_idx,
ut_new_pfx_t* pfx)
{
- const PSI_memory_key key = get_mem_key(filename_hash);
+ const PSI_memory_key key = get_mem_key(autoevent_idx);
pfx->m_key = PSI_MEMORY_CALL(memory_alloc)(key, size, & pfx->m_owner);
pfx->m_size = size;
@@ -801,36 +803,124 @@ operator!=(
/*
constexpr trickery ahead.
- Retrieve the FILENAME_HASH = djb2(basename_noext(__FILE__)) at the compile time.
- We use the number rather than __FILE__ because integers is better to deal with
- (hashing, searching) that C style strings.
+ Compute AUTOEVENT_IDX at compile time.
+ (index in the auto_event_names array, corresponding to basename of __FILE__)
+
+ The tricks are necessary to reduce the cost of lookup the
+ PSI_memory_key for auto event.
*/
-static constexpr const char * basename_helper(const char* s, const char * last_slash)
+static constexpr const char* cexpr_basename_helper(const char* s, const char* last_slash)
{
return
- *s == '\0' ? last_slash :
- *s == '/' || *s == '\\' ? basename_helper(s + 1, s + 1) :
- basename_helper(s + 1, last_slash);
+ *s == '\0' ? last_slash :
+ *s == '/' || *s == '\\' ? cexpr_basename_helper(s + 1, s + 1) :
+ cexpr_basename_helper(s + 1, last_slash);
+}
+
+static constexpr const char* cexpr_basename(const char* filename)
+{
+ return cexpr_basename_helper(filename, filename);
}
-static constexpr const char* ut_basename(const char *filename)
+static constexpr bool cexpr_strequal_ignore_dot(const char* a, const char* b)
{
- return basename_helper(filename, filename);
+ return *a == 0 || *a == '.' ? (*b == 0 || *b == '.')
+ : *a == *b ? cexpr_strequal_ignore_dot(a + 1, b + 1) : false;
}
-/** Compute djb2 hash for a string. Stop at '.' , or '\0' */
-constexpr uint32_t ut_filename_hash(const char *s, uint32_t h= 5381)
+constexpr const char* const auto_event_names[] =
+{
+ "btr0btr",
+ "btr0buf",
+ "btr0bulk",
+ "btr0cur",
+ "btr0pcur",
+ "btr0sea",
+ "buf0buf",
+ "buf0dblwr",
+ "buf0dump",
+ "dict0dict",
+ "dict0mem",
+ "dict0stats",
+ "eval0eval",
+ "fil0crypt",
+ "fil0fil",
+ "fsp0file",
+ "fts0ast",
+ "fts0blex",
+ "fts0config",
+ "fts0file",
+ "fts0fts",
+ "fts0opt",
+ "fts0pars",
+ "fts0que",
+ "fts0sql",
+ "fts0tlex",
+ "gis0sea",
+ "ha_innodb",
+ "ha0ha",
+ "handler0alter",
+ "hash0hash",
+ "i_s",
+ "lexyy",
+ "lock0lock",
+ "mem0mem",
+ "os0event",
+ "os0file",
+ "pars0lex",
+ "rem0rec",
+ "row0ftsort",
+ "row0import",
+ "row0log",
+ "row0merge",
+ "row0mysql",
+ "row0sel",
+ "srv0start",
+ "sync0arr",
+ "sync0debug",
+ "sync0rw",
+ "sync0start",
+ "sync0types",
+ "trx0i_s",
+ "trx0i_s",
+ "trx0roll",
+ "trx0rseg",
+ "trx0seg",
+ "trx0trx",
+ "trx0undo",
+ "ut0list",
+ "ut0mem",
+ "ut0new",
+ "ut0pool",
+ "ut0rbt",
+ "ut0wqueue",
+ "xtrabackup",
+ nullptr
+};
+
+constexpr uint32_t cexpr_lookup_auto_event_name(const char* name, uint32_t idx = 0)
{
- return *s == 0 || *s == '.' ? h :
- ut_filename_hash(s + 1, static_cast<uint32_t>(uint64_t{33} * h + *s));
+ return !auto_event_names[idx] ? INVALID_AUTOEVENT_IDX :
+ cexpr_strequal_ignore_dot(name, auto_event_names[idx]) ? idx :
+ cexpr_lookup_auto_event_name(name, idx + 1);
}
-/* Force constexpr to be evaluated at compile time.*/
-#define FORCE_CONSTEXPR(expr)[&]() \
-{ static constexpr auto x = (expr); return x; }()
+/*
+ The AUTOEVENT_IDX macro.
+
+ Note, that there is a static_assert that checks whether
+ basename of the __FILE is not registered in the auto_event_names array.
+ If you run into this assert, add the basename to the array.
-#define FILENAME_HASH FORCE_CONSTEXPR(ut_filename_hash(ut_basename(__FILE__)))
+ Weird looking lambda is used to force the evaluation at the compile time.
+*/
+#define AUTOEVENT_IDX []()\
+{\
+ constexpr auto idx = cexpr_lookup_auto_event_name(cexpr_basename(__FILE__)); \
+ static_assert(idx != INVALID_AUTOEVENT_IDX, "auto_event_names contains no entry for " __FILE__);\
+ return idx; \
+}()
/** Allocate, trace the allocation and construct an object.
@@ -850,7 +940,7 @@ pointer must be passed to UT_DELETE() when no longer needed.
object if the passed in pointer is NULL, e.g. if allocate() has
failed to allocate memory and has returned NULL. */ \
::new(ut_allocator<byte>(key).allocate( \
- sizeof expr, NULL, FILENAME_HASH, false, false)) expr
+ sizeof expr, NULL, AUTOEVENT_IDX, false, false)) expr
/** Allocate, trace the allocation and construct an object.
Use this macro instead of 'new' within InnoDB and instead of UT_NEW()
@@ -872,6 +962,7 @@ We can't instantiate ut_allocator without having the type of the object, thus
we redirect this to a templated function. */
#define UT_DELETE(ptr) ut_delete(ptr)
+
/** Destroy and account object created by UT_NEW() or UT_NEW_NOKEY().
@param[in,out] ptr pointer to the object */
template <typename T>
@@ -898,7 +989,7 @@ The returned pointer must be passed to UT_DELETE_ARRAY().
@param[in] key performance schema memory tracing key
@return pointer to the first allocated object or NULL */
#define UT_NEW_ARRAY(type, n_elements, key) \
- ut_allocator<type>(key).new_array(n_elements, FILENAME_HASH)
+ ut_allocator<type>(key).new_array(n_elements, AUTOEVENT_IDX)
/** Allocate and account 'n_elements' objects of type 'type'.
Use this macro to allocate memory within InnoDB instead of 'new[]' and
@@ -929,7 +1020,7 @@ ut_delete_array(
#define ut_malloc(n_bytes, key) static_cast<void*>( \
ut_allocator<byte>(key).allocate( \
- n_bytes, NULL, FILENAME_HASH, false, false))
+ n_bytes, NULL, AUTOEVENT_IDX, false, false))
#define ut_malloc_dontdump(n_bytes, key) static_cast<void*>( \
ut_allocator<byte>(key).allocate_large( \
@@ -937,23 +1028,23 @@ ut_delete_array(
#define ut_zalloc(n_bytes, key) static_cast<void*>( \
ut_allocator<byte>(key).allocate( \
- n_bytes, NULL, FILENAME_HASH, true, false))
+ n_bytes, NULL, AUTOEVENT_IDX, true, false))
#define ut_malloc_nokey(n_bytes) static_cast<void*>( \
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).allocate( \
- n_bytes, NULL, FILENAME_HASH, false, false))
+ n_bytes, NULL, AUTOEVENT_IDX, false, false))
#define ut_zalloc_nokey(n_bytes) static_cast<void*>( \
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).allocate( \
- n_bytes, NULL, FILENAME_HASH, true, false))
+ n_bytes, NULL, AUTOEVENT_IDX, true, false))
#define ut_zalloc_nokey_nofatal(n_bytes) static_cast<void*>( \
ut_allocator<byte, false>(PSI_NOT_INSTRUMENTED).allocate( \
- n_bytes, NULL, FILENAME_HASH, true, false))
+ n_bytes, NULL, AUTOEVENT_IDX, true, false))
#define ut_realloc(ptr, n_bytes) static_cast<void*>( \
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).reallocate( \
- ptr, n_bytes, FILENAME_HASH))
+ ptr, n_bytes, AUTOEVENT_IDX))
#define ut_free(ptr) ut_allocator<byte>(PSI_NOT_INSTRUMENTED).deallocate( \
reinterpret_cast<byte*>(ptr))
diff --git a/storage/innobase/ut/ut0new.cc b/storage/innobase/ut/ut0new.cc
index d0877abd42f..c4cf8364806 100644
--- a/storage/innobase/ut/ut0new.cc
+++ b/storage/innobase/ut/ut0new.cc
@@ -44,175 +44,6 @@ PSI_memory_key mem_key_row_merge_sort;
PSI_memory_key mem_key_std;
#ifdef UNIV_PFS_MEMORY
-static const char* auto_event_names[] =
-{
- "btr0btr",
- "btr0bulk",
- "btr0cur",
- "btr0defragment",
- "btr0pcur",
- "btr0sea",
- "btr0types",
- "buf0buddy",
- "buf0buf",
- "buf0checksum",
- "buf0dblwr",
- "buf0dump",
- "buf0flu",
- "buf0lru",
- "buf0rea",
- "buf0types",
- "data0data",
- "data0type",
- "data0types",
- "db0err",
- "dict0boot",
- "dict0crea",
- "dict0defrag_bg",
- "dict0dict",
- "dict0load",
- "dict0mem",
- "dict0pagecompress",
- "dict0priv",
- "dict0stats",
- "dict0stats_bg",
- "dict0types",
- "dyn0buf",
- "dyn0types",
- "eval0eval",
- "eval0proc",
- "fil0crypt",
- "fil0fil",
- "fil0pagecompress",
- "fsp0file",
- "fsp0fsp",
- "fsp0space",
- "fsp0sysspace",
- "fsp0types",
- "fts0ast",
- "fts0blex",
- "fts0config",
- "fts0fts",
- "fts0opt",
- "fts0pars",
- "fts0plugin",
- "fts0priv",
- "fts0que",
- "fts0sql",
- "fts0tlex",
- "fts0tokenize",
- "fts0types",
- "fts0vlc",
- "fut0fut",
- "fut0lst",
- "gis0geo",
- "gis0rtree",
- "gis0sea",
- "gis0type",
- "ha0ha",
- "ha0storage",
- "ha_innodb",
- "ha_prototypes",
- "handler0alter",
- "hash0hash",
- "i_s",
- "ib0mutex",
- "ibuf0ibuf",
- "ibuf0types",
- "lexyy",
- "lock0iter",
- "lock0lock",
- "lock0prdt",
- "lock0priv",
- "lock0types",
- "lock0wait",
- "log0crypt",
- "log0log",
- "log0recv",
- "log0sync",
- "log0types",
- "mach0data",
- "mem0mem",
- "mtr0log",
- "mtr0mtr",
- "mtr0types",
- "os0api",
- "os0event",
- "os0file",
- "os0proc",
- "os0thread",
- "page0cur",
- "page0page",
- "page0types",
- "page0zip",
- "pars0grm",
- "pars0lex",
- "pars0opt",
- "pars0pars",
- "pars0sym",
- "pars0types",
- "que0que",
- "que0types",
- "read0read",
- "read0types",
- "rem0cmp",
- "rem0rec",
- "rem0types",
- "row0ext",
- "row0ftsort",
- "row0import",
- "row0ins",
- "row0log",
- "row0merge",
- "row0mysql",
- "row0purge",
- "row0quiesce",
- "row0row",
- "row0sel",
- "row0types",
- "row0uins",
- "row0umod",
- "row0undo",
- "row0upd",
- "row0vers",
- "srv0conc",
- "srv0mon",
- "srv0srv",
- "srv0start",
- "sync0arr",
- "sync0debug",
- "sync0policy",
- "sync0rw",
- "sync0sync",
- "sync0types",
- "trx0i_s",
- "trx0purge",
- "trx0rec",
- "trx0roll",
- "trx0rseg",
- "trx0sys",
- "trx0trx",
- "trx0types",
- "trx0undo",
- "trx0xa",
- "ut0byte",
- "ut0counter",
- "ut0crc32",
- "ut0dbg",
- "ut0list",
- "ut0lst",
- "ut0mem",
- "ut0mutex",
- "ut0new",
- "ut0pool",
- "ut0rbt",
- "ut0rnd",
- "ut0sort",
- "ut0stage",
- "ut0ut",
- "ut0vec",
- "ut0wqueue"
-};
/** Auxiliary array of performance schema 'PSI_memory_info'.
Each allocation appears in
@@ -240,8 +71,8 @@ static PSI_memory_info pfs_info[] = {
{&mem_key_std, "std", 0},
};
-constexpr int NKEYS= static_cast<int>UT_ARR_SIZE(auto_event_names);
-std::pair<uint32_t, PSI_memory_key> search_array[NKEYS];
+static const int NKEYS = static_cast<int>UT_ARR_SIZE(auto_event_names)-1;
+static PSI_memory_key auto_event_keys[NKEYS];
/** Setup the internal objects needed for UT_NEW() to operate.
This must be called before the first call to UT_NEW(). */
@@ -250,66 +81,26 @@ void ut_new_boot()
PSI_MEMORY_CALL(register_memory)("innodb", pfs_info, static_cast<int>
UT_ARR_SIZE(pfs_info));
- static PSI_memory_key auto_event_keys[NKEYS];
- static PSI_memory_info pfs_info_auto[NKEYS];
+ PSI_memory_info pfs_info_auto[NKEYS];
for (int i= 0; i < NKEYS; i++)
{
pfs_info_auto[i]= {&auto_event_keys[i], auto_event_names[i], 0};
}
PSI_MEMORY_CALL(register_memory)("innodb", pfs_info_auto,NKEYS);
-
- if (auto_event_keys[0] == PSI_NOT_INSTRUMENTED)
- return; // PSI is off
-
- for (int i= 0; i < NKEYS; i++)
- {
- search_array[i]= {ut_filename_hash(auto_event_names[i]), auto_event_keys[i]};
- }
-
- std::sort(search_array, std::end(search_array));
-
-#ifdef UNIV_DEBUG
- /* assumption that hash value is not 0 in ut0new.h, get_mem_key() */
- ut_ad(search_array[0].first);
-
- /* Check for hash duplicates */
- for(int i= 0; i < NKEYS-1; i++)
- {
- if (search_array[i].first == search_array[i + 1].first)
- {
- // This can only happen if autoevent_names was updated
- // previously, or the hash function changed
- ib::fatal() << __FILE__ "Duplicates found in filename hashes";
- }
- }
-#endif
}
-/** Retrieve a memory key (registered with PFS), corresponding to source file hash.
+/** Retrieve a memory key (registered with PFS), corresponding to source file .
+
+@param[in] autoevent_idx - offset to the auto_event_names corresponding to the
+file name of the caller.
-@param[in] filename_hash - hash value (computed at compile time) of a ut_filename_hash
- for a one of the auto_event_names.
@return registered memory key or PSI_NOT_INSTRUMENTED
*/
-PSI_memory_key ut_new_get_key_by_file(uint32_t filename_hash)
+PSI_memory_key ut_new_get_key_by_file(uint32_t autoevent_idx)
{
- if(search_array[0].second == PSI_NOT_INSTRUMENTED)
- {
- // PSI is off.
- return PSI_NOT_INSTRUMENTED;
- }
-
- std::pair<uint32, PSI_memory_key> e{ filename_hash, 0 };
- auto result= std::lower_bound(search_array, std::end(search_array), e);
- if (result != std::end(search_array) && result->first == filename_hash)
- return result->second;
-
-#ifdef UNIV_DEBUG
- ib::fatal() << __FILE__ " ut_new_get_key_by_file : hash not found";
-#endif
-
- return PSI_NOT_INSTRUMENTED;
+ ut_ad(autoevent_idx < NKEYS);
+ return auto_event_keys[autoevent_idx];
}
#else /* UNIV_PFS_MEMORY */