Skip to content

Commit 72fc4f3

Browse files
committed
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.
1 parent 7803601 commit 72fc4f3

File tree

2 files changed

+144
-262
lines changed

2 files changed

+144
-262
lines changed

storage/innobase/include/ut0new.h

Lines changed: 134 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ with (), thus:
148148
/** Maximum number of retries to allocate memory. */
149149
extern const size_talloc_max_retries;
150150

151+
constexpr uint32_t INVALID_AUTOEVENT_IDX = 0xFFFFFFFFU;
152+
151153
/** Keys for registering allocations with performance schema.
152154
Pointers to these variables are supplied to PFS code via the pfs_info[]
153155
array and the PFS code initializes them via PSI_MEMORY_CALL(register_memory)().
@@ -180,11 +182,11 @@ ut_new_boot();
180182

181183
/**
182184
Retrieve a memory key (registered with PFS),
183-
given filename hash of the caller
185+
given AUTOEVENT_IDX of the caller
184186
185-
@param[in] filename_hash - FILENAME_HASH value of the caller
187+
@param[in] autoevent_idx - AUTOEVENT_IDX value of the caller
186188
@return registered memory key or PSI_NOT_INSTRUMENTED */
187-
PSI_memory_key ut_new_get_key_by_file(uint32_t filename_hash);
189+
PSI_memory_key ut_new_get_key_by_file(uint32_t autoevent_idx);
188190

189191
#endif /* UNIV_PFS_MEMORY */
190192

@@ -293,7 +295,7 @@ class ut_allocator {
293295
)
294296
{
295297
#ifdef UNIV_PFS_MEMORY
296-
const PSI_memory_key other_key = other.get_mem_key(0);
298+
const PSI_memory_key other_key = other.get_mem_key();
297299

298300
m_key = (other_key != mem_key_std)
299301
? other_key
@@ -315,7 +317,7 @@ class ut_allocator {
315317
#endif /* UNIV_PFS_MEMORY */
316318
}
317319

318-
pointer allocate(size_type n) { return allocate(n, NULL, 0); }
320+
pointer allocate(size_type n) { return allocate(n, NULL, INVALID_AUTOEVENT_IDX); }
319321

320322
/** Allocate a chunk of memory that can hold 'n_elements' objects of
321323
type 'T' and trace the allocation.
@@ -335,7 +337,7 @@ class ut_allocator {
335337
const_pointer,
336338
uint32_t
337339
#ifdef UNIV_PFS_MEMORY
338-
filename_hash /* filename hash of the caller */
340+
autoevent_idx /* AUTOEVENT_IDX of the caller */
339341
#endif
340342
,
341343
boolset_to_zero = false,
@@ -397,7 +399,7 @@ class ut_allocator {
397399
#ifdef UNIV_PFS_MEMORY
398400
ut_new_pfx_t* pfx = static_cast<ut_new_pfx_t*>(ptr);
399401

400-
allocate_trace(total_bytes, filename_hash, pfx);
402+
allocate_trace(total_bytes, autoevent_idx, pfx);
401403

402404
return(reinterpret_cast<pointer>(pfx + 1));
403405
#else
@@ -479,15 +481,15 @@ class ut_allocator {
479481
reallocate(
480482
void* ptr,
481483
size_type n_elements,
482-
uint32_tfilename_hash)
484+
uint32_tautoevent_idx)
483485
{
484486
if (n_elements == 0) {
485487
deallocate(static_cast<pointer>(ptr));
486488
return(NULL);
487489
}
488490

489491
if (ptr == NULL) {
490-
return(allocate(n_elements, NULL, filename_hash, false, false));
492+
return(allocate(n_elements, NULL, autoevent_idx, false, false));
491493
}
492494

493495
if (n_elements > max_size()) {
@@ -530,7 +532,7 @@ class ut_allocator {
530532
deallocate_trace(pfx_new);
531533

532534
/* pfx_new is set here to describe the new block. */
533-
allocate_trace(total_bytes, filename_hash, pfx_new);
535+
allocate_trace(total_bytes, autoevent_idx, pfx_new);
534536

535537
return(reinterpret_cast<pointer>(pfx_new + 1));
536538
}
@@ -546,10 +548,10 @@ class ut_allocator {
546548
pointer
547549
new_array(
548550
size_type n_elements,
549-
uint32_t filename_hash
551+
uint32_t autoevent_idx
550552
)
551553
{
552-
T* p = allocate(n_elements, NULL, filename_hash, false, false);
554+
T* p = allocate(n_elements, NULL, autoevent_idx, false, false);
553555

554556
if (p == NULL) {
555557
return(NULL);
@@ -688,16 +690,16 @@ class ut_allocator {
688690
@return performance schema key */
689691
PSI_memory_key
690692
get_mem_key(
691-
uint32_t filename_hash) const
693+
uint32_t autoevent_idx = INVALID_AUTOEVENT_IDX) const
692694
{
693695
if (m_key != PSI_NOT_INSTRUMENTED) {
694696
return(m_key);
695697
}
696698

697-
if (filename_hash == 0) {
699+
if (autoevent_idx == INVALID_AUTOEVENT_IDX) {
698700
return(mem_key_std);
699701
}
700-
const PSI_memory_key key = ut_new_get_key_by_file(filename_hash);
702+
const PSI_memory_key key = ut_new_get_key_by_file(autoevent_idx);
701703

702704
if (key != PSI_NOT_INSTRUMENTED) {
703705
return(key);
@@ -739,16 +741,16 @@ class ut_allocator {
739741
corresponds to "file", that will be used (see ut_new_boot())
740742
4. Otherwise, the name associated with mem_key_other will be used.
741743
@param[in] size number of bytes that were allocated
742-
@param[in]filename_hash FILENAME_HASH of the caller
744+
@param[in]autoevent_idx autoevent_idx of the caller
743745
@param[out] pfx placeholder to store the info which will be
744746
needed when freeing the memory */
745747
void
746748
allocate_trace(
747749
size_tsize,
748-
const uint32_t filename_hash,
750+
const uint32_t autoevent_idx,
749751
ut_new_pfx_t* pfx)
750752
{
751-
const PSI_memory_key key = get_mem_key(filename_hash);
753+
const PSI_memory_key key = get_mem_key(autoevent_idx);
752754

753755
pfx->m_key = PSI_MEMORY_CALL(memory_alloc)(key, size, & pfx->m_owner);
754756
pfx->m_size = size;
@@ -801,36 +803,124 @@ operator!=(
801803
/*
802804
constexpr trickery ahead.
803805
804-
Retrieve the FILENAME_HASH = djb2(basename_noext(__FILE__)) at the compile time.
805-
We use the number rather than __FILE__ because integers is better to deal with
806-
(hashing, searching) that C style strings.
806+
Compute AUTOEVENT_IDX at compile time.
807+
(index in the auto_event_names array, corresponding to basename of __FILE__)
808+
809+
The tricks are necessary to reduce the cost of lookup the
810+
PSI_memory_key for auto event.
807811
*/
808812

809-
static constexpr const char * basename_helper(const char* s, const char * last_slash)
813+
static constexpr const char* cexpr_basename_helper(const char* s, const char* last_slash)
810814
{
811815
return
812-
*s == '\0' ? last_slash :
813-
*s == '/' || *s == '\\' ? basename_helper(s + 1, s + 1) :
814-
basename_helper(s + 1, last_slash);
816+
*s == '\0' ? last_slash :
817+
*s == '/' || *s == '\\' ? cexpr_basename_helper(s + 1, s + 1) :
818+
cexpr_basename_helper(s + 1, last_slash);
819+
}
820+
821+
static constexpr const char* cexpr_basename(const char* filename)
822+
{
823+
return cexpr_basename_helper(filename, filename);
815824
}
816825

817-
static constexpr const char* ut_basename(const char *filename)
826+
static constexpr bool cexpr_strequal_ignore_dot(const char* a, const char* b)
818827
{
819-
return basename_helper(filename, filename);
828+
return *a == 0 || *a == '.' ? (*b == 0 || *b == '.')
829+
: *a == *b ? cexpr_strequal_ignore_dot(a + 1, b + 1) : false;
820830
}
821831

822-
/** Compute djb2 hash for a string. Stop at '.' , or '\0' */
823-
constexpr uint32_t ut_filename_hash(const char *s, uint32_t h= 5381)
832+
constexpr const char* const auto_event_names[] =
833+
{
834+
"btr0btr",
835+
"btr0buf",
836+
"btr0bulk",
837+
"btr0cur",
838+
"btr0pcur",
839+
"btr0sea",
840+
"buf0buf",
841+
"buf0dblwr",
842+
"buf0dump",
843+
"dict0dict",
844+
"dict0mem",
845+
"dict0stats",
846+
"eval0eval",
847+
"fil0crypt",
848+
"fil0fil",
849+
"fsp0file",
850+
"fts0ast",
851+
"fts0blex",
852+
"fts0config",
853+
"fts0file",
854+
"fts0fts",
855+
"fts0opt",
856+
"fts0pars",
857+
"fts0que",
858+
"fts0sql",
859+
"fts0tlex",
860+
"gis0sea",
861+
"ha_innodb",
862+
"ha0ha",
863+
"handler0alter",
864+
"hash0hash",
865+
"i_s",
866+
"lexyy",
867+
"lock0lock",
868+
"mem0mem",
869+
"os0event",
870+
"os0file",
871+
"pars0lex",
872+
"rem0rec",
873+
"row0ftsort",
874+
"row0import",
875+
"row0log",
876+
"row0merge",
877+
"row0mysql",
878+
"row0sel",
879+
"srv0start",
880+
"sync0arr",
881+
"sync0debug",
882+
"sync0rw",
883+
"sync0start",
884+
"sync0types",
885+
"trx0i_s",
886+
"trx0i_s",
887+
"trx0roll",
888+
"trx0rseg",
889+
"trx0seg",
890+
"trx0trx",
891+
"trx0undo",
892+
"ut0list",
893+
"ut0mem",
894+
"ut0new",
895+
"ut0pool",
896+
"ut0rbt",
897+
"ut0wqueue",
898+
"xtrabackup",
899+
nullptr
900+
};
901+
902+
constexpr uint32_t cexpr_lookup_auto_event_name(const char* name, uint32_t idx = 0)
824903
{
825-
return *s == 0 || *s == '.' ? h :
826-
ut_filename_hash(s + 1, static_cast<uint32_t>(uint64_t{33} * h + *s));
904+
return !auto_event_names[idx] ? INVALID_AUTOEVENT_IDX :
905+
cexpr_strequal_ignore_dot(name, auto_event_names[idx]) ? idx :
906+
cexpr_lookup_auto_event_name(name, idx + 1);
827907
}
828908

829-
/* Force constexpr to be evaluated at compile time.*/
830-
#define FORCE_CONSTEXPR(expr)[&]() \
831-
{ static constexpr auto x = (expr); return x; }()
909+
/*
910+
The AUTOEVENT_IDX macro.
911+
912+
Note, that there is a static_assert that checks whether
913+
basename of the __FILE is not registered in the auto_event_names array.
914+
If you run into this assert, add the basename to the array.
832915
833-
#define FILENAME_HASH FORCE_CONSTEXPR(ut_filename_hash(ut_basename(__FILE__)))
916+
Weird looking lambda is used to force the evaluation at the compile time.
917+
*/
918+
#define AUTOEVENT_IDX []()\
919+
{\
920+
constexpr auto idx = cexpr_lookup_auto_event_name(cexpr_basename(__FILE__)); \
921+
static_assert(idx != INVALID_AUTOEVENT_IDX, "auto_event_names contains no entry for " __FILE__);\
922+
return idx; \
923+
}()
834924

835925

836926
/** Allocate, trace the allocation and construct an object.
@@ -850,7 +940,7 @@ pointer must be passed to UT_DELETE() when no longer needed.
850940
object if the passed in pointer is NULL, e.g. if allocate() has
851941
failed to allocate memory and has returned NULL. */ \
852942
::new(ut_allocator<byte>(key).allocate( \
853-
sizeof expr, NULL, FILENAME_HASH, false, false)) expr
943+
sizeof expr, NULL, AUTOEVENT_IDX, false, false)) expr
854944

855945
/** Allocate, trace the allocation and construct an object.
856946
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
872962
we redirect this to a templated function. */
873963
#define UT_DELETE(ptr) ut_delete(ptr)
874964

965+
875966
/** Destroy and account object created by UT_NEW() or UT_NEW_NOKEY().
876967
@param[in,out] ptr pointer to the object */
877968
template <typename T>
@@ -898,7 +989,7 @@ The returned pointer must be passed to UT_DELETE_ARRAY().
898989
@param[in] key performance schema memory tracing key
899990
@return pointer to the first allocated object or NULL */
900991
#define UT_NEW_ARRAY(type, n_elements, key) \
901-
ut_allocator<type>(key).new_array(n_elements, FILENAME_HASH)
992+
ut_allocator<type>(key).new_array(n_elements, AUTOEVENT_IDX)
902993

903994
/** Allocate and account 'n_elements' objects of type 'type'.
904995
Use this macro to allocate memory within InnoDB instead of 'new[]' and
@@ -929,31 +1020,31 @@ ut_delete_array(
9291020

9301021
#define ut_malloc(n_bytes, key)static_cast<void*>( \
9311022
ut_allocator<byte>(key).allocate( \
932-
n_bytes, NULL, FILENAME_HASH, false, false))
1023+
n_bytes, NULL, AUTOEVENT_IDX, false, false))
9331024

9341025
#define ut_malloc_dontdump(n_bytes, key) static_cast<void*>( \
9351026
ut_allocator<byte>(key).allocate_large( \
9361027
n_bytes, NULL, true))
9371028

9381029
#define ut_zalloc(n_bytes, key)static_cast<void*>( \
9391030
ut_allocator<byte>(key).allocate( \
940-
n_bytes, NULL, FILENAME_HASH, true, false))
1031+
n_bytes, NULL, AUTOEVENT_IDX, true, false))
9411032

9421033
#define ut_malloc_nokey(n_bytes)static_cast<void*>( \
9431034
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).allocate( \
944-
n_bytes, NULL, FILENAME_HASH, false, false))
1035+
n_bytes, NULL, AUTOEVENT_IDX, false, false))
9451036

9461037
#define ut_zalloc_nokey(n_bytes)static_cast<void*>( \
9471038
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).allocate( \
948-
n_bytes, NULL, FILENAME_HASH, true, false))
1039+
n_bytes, NULL, AUTOEVENT_IDX, true, false))
9491040

9501041
#define ut_zalloc_nokey_nofatal(n_bytes)static_cast<void*>( \
9511042
ut_allocator<byte, false>(PSI_NOT_INSTRUMENTED).allocate( \
952-
n_bytes, NULL, FILENAME_HASH, true, false))
1043+
n_bytes, NULL, AUTOEVENT_IDX, true, false))
9531044

9541045
#define ut_realloc(ptr, n_bytes)static_cast<void*>( \
9551046
ut_allocator<byte>(PSI_NOT_INSTRUMENTED).reallocate( \
956-
ptr, n_bytes, FILENAME_HASH))
1047+
ptr, n_bytes, AUTOEVENT_IDX))
9571048

9581049
#define ut_free(ptr) ut_allocator<byte>(PSI_NOT_INSTRUMENTED).deallocate( \
9591050
reinterpret_cast<byte*>(ptr))

0 commit comments

Comments
 (0)