From: Marc Alff Date: April 13 2010 10:05am Subject: bzr commit into mysql-6.0-codebase-bugfixing branch (marc.alff:3857) List-Archive: http://lists.mysql.com/commits/105484 Message-Id: <20100413100545.5408B40D8F2@MarcBook.local> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Boundary_(ID_/S0BZ/X247+CVql7vWXA3A)" --Boundary_(ID_/S0BZ/X247+CVql7vWXA3A) MIME-version: 1.0 Content-type: text/plain; CHARSET=US-ASCII Content-transfer-encoding: 7BIT Content-disposition: inline #At file:///Users/malff/BZR_TREE/mysql-6.0-codebase-bugfixing/ based on revid:alik@stripped 3857 Marc Alff 2010-04-13 [merge] Manual merge, mysql-next-mr-bugfixing --> mysql-6.0-codebase-bugfixing modified: storage/perfschema/pfs_global.h storage/perfschema/pfs_instr.cc storage/perfschema/pfs_instr.h === modified file 'storage/perfschema/pfs_global.h' --- a/storage/perfschema/pfs_global.h 2010-01-12 01:48:52 +0000 +++ b/storage/perfschema/pfs_global.h 2010-04-13 10:05:22 +0000 @@ -1,4 +1,4 @@ -/* Copyright (C) 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,27 +30,49 @@ void pfs_free(void *ptr); inline uint randomized_index(const void *ptr, uint max_size) { + static uint seed1= 0; + static uint seed2= 0; + uint result; + register intptr value; + if (unlikely(max_size == 0)) return 0; /* - ptr is typically an aligned structure, - so the last bits are not really random, but this has no effect. - Apply a factor A*x to spread - close values of ptr further apart (which helps with arrays), - and to spread values way beyond a typical max_size. - Then, apply a modulo to end within [0, max_size - 1]. - A is big prime numbers, to avoid resonating with max_size, - to have a uniform distribution in [0, max_size - 1]. - The value of A is chosen so that index(ptr) and index(ptr + N) (for arrays) - are likely to be not similar for typical values of max_size - (50, 100, 1000, etc). - In other words, (sizeof(T)*A % max_size) should not be a small number, - to avoid that with 'T array[max_size]', index(array[i]) - and index(array[i + 1]) end up pointing in the same area in [0, max_size - 1]. + ptr is typically an aligned structure, and can be in an array. + - The last bits are not random because of alignment, + so we divide by 8. + - The high bits are mostly constant, especially with 64 bits architectures, + but we keep most of them anyway, by doing computation in intptr. + The high bits are significant depending on where the data is + stored (the data segment, the stack, the heap, ...). + - To spread consecutive cells in an array further, we multiply by + a factor A. This factor should not be too high, which would cause + an overflow and cause loss of randomness (droping the top high bits). + The factor is a prime number, to help spread the distribution. + - To add more noise, and to be more robust if the calling code is + passing a constant value instead of a random identity, + we add the previous results, for hysteresys, with a degree 2 polynom, + X^2 + X + 1. + - Last, a modulo is applied to be within the [0, max_size - 1] range. + Note that seed1 and seed2 are static, and are *not* thread safe, + which is even better. + Effect with arrays: T array[N] + - ptr(i) = & array[i] = & array[0] + i * sizeof(T) + - ptr(i+1) = ptr(i) + sizeof(T). + What we want here, is to have index(i) and index(i+1) fall into + very different areas in [0, max_size - 1], to avoid locality. */ - return static_cast - (((reinterpret_cast (ptr)) * 2166179) % max_size); + value= (reinterpret_cast (ptr)) >> 3; + value*= 1789; + value+= seed2 + seed1 + 1; + value%= max_size; + + result= static_cast (value); + seed2= seed1*seed1; + seed1= result; + + return result; } void pfs_print_error(const char *format, ...); === modified file 'storage/perfschema/pfs_instr.cc' --- a/storage/perfschema/pfs_instr.cc 2010-04-08 10:50:40 +0000 +++ b/storage/perfschema/pfs_instr.cc 2010-04-13 10:05:22 +0000 @@ -18,13 +18,15 @@ Performance schema instruments (implementation). */ +#include + #include "my_global.h" -#include "m_string.h" #include "sql_priv.h" #include "my_sys.h" #include "pfs_stat.h" #include "pfs_instr.h" #include "pfs_global.h" +#include "m_string.h" // strmov /** @addtogroup Performance_schema_buffers @@ -446,6 +448,75 @@ void cleanup_file_hash(void) } } +void PFS_scan::init(uint random, uint max_size) +{ + m_pass= 0; + + if (max_size == 0) + { + /* Degenerated case, no buffer */ + m_pass_max= 0; + return; + } + + DBUG_ASSERT(random < max_size); + + if (PFS_MAX_ALLOC_RETRY < max_size) + { + /* + The buffer is big compared to PFS_MAX_ALLOC_RETRY, + scan it only partially. + */ + if (random + PFS_MAX_ALLOC_RETRY < max_size) + { + /* + Pass 1: [random, random + PFS_MAX_ALLOC_RETRY - 1] + Pass 2: not used. + */ + m_pass_max= 1; + m_first[0]= random; + m_last[0]= random + PFS_MAX_ALLOC_RETRY; + m_first[1]= 0; + m_last[1]= 0; + } + else + { + /* + Pass 1: [random, max_size - 1] + Pass 2: [0, ...] + The combined length of pass 1 and 2 is PFS_MAX_ALLOC_RETRY. + */ + m_pass_max= 2; + m_first[0]= random; + m_last[0]= max_size; + m_first[1]= 0; + m_last[1]= PFS_MAX_ALLOC_RETRY - (max_size - random); + } + } + else + { + /* + The buffer is small compared to PFS_MAX_ALLOC_RETRY, + scan it in full in two passes. + Pass 1: [random, max_size - 1] + Pass 2: [0, random - 1] + */ + m_pass_max= 2; + m_first[0]= random; + m_last[0]= max_size; + m_first[1]= 0; + m_last[1]= random; + } + + DBUG_ASSERT(m_first[0] < max_size); + DBUG_ASSERT(m_first[1] < max_size); + DBUG_ASSERT(m_last[1] <= max_size); + DBUG_ASSERT(m_last[1] <= max_size); + /* The combined length of all passes should not exceed PFS_MAX_ALLOC_RETRY. */ + DBUG_ASSERT((m_last[0] - m_first[0]) + + (m_last[1] - m_first[1]) <= PFS_MAX_ALLOC_RETRY); +} + /** Create instrumentation for a mutex instance. @param klass the mutex class @@ -454,17 +525,15 @@ void cleanup_file_hash(void) */ PFS_mutex* create_mutex(PFS_mutex_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, mutex_max); + PFS_scan scan; + uint random= randomized_index(identity, mutex_max); - /* - Pass 1: [random, mutex_max - 1] - Pass 2: [0, mutex_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, mutex_max); + scan.has_pass(); + scan.next_pass()) { - PFS_mutex *pfs= mutex_array + i; - PFS_mutex *pfs_last= mutex_array + mutex_max; + PFS_mutex *pfs= mutex_array + scan.first(); + PFS_mutex *pfs_last= mutex_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -512,17 +581,15 @@ void destroy_mutex(PFS_mutex *pfs) */ PFS_rwlock* create_rwlock(PFS_rwlock_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, rwlock_max); + PFS_scan scan; + uint random= randomized_index(identity, rwlock_max); - /* - Pass 1: [random, rwlock_max - 1] - Pass 2: [0, rwlock_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, rwlock_max); + scan.has_pass(); + scan.next_pass()) { - PFS_rwlock *pfs= rwlock_array + i; - PFS_rwlock *pfs_last= rwlock_array + rwlock_max; + PFS_rwlock *pfs= rwlock_array + scan.first(); + PFS_rwlock *pfs_last= rwlock_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -576,17 +643,15 @@ void destroy_rwlock(PFS_rwlock *pfs) */ PFS_cond* create_cond(PFS_cond_class *klass, const void *identity) { - int pass; - uint i= randomized_index(identity, cond_max); + PFS_scan scan; + uint random= randomized_index(identity, cond_max); - /* - Pass 1: [random, cond_max - 1] - Pass 2: [0, cond_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, cond_max); + scan.has_pass(); + scan.next_pass()) { - PFS_cond *pfs= cond_array + i; - PFS_cond *pfs_last= cond_array + cond_max; + PFS_cond *pfs= cond_array + scan.first(); + PFS_cond *pfs_last= cond_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -634,17 +699,15 @@ void destroy_cond(PFS_cond *pfs) PFS_thread* create_thread(PFS_thread_class *klass, const void *identity, ulong thread_id) { - int pass; - uint i= randomized_index(identity, thread_max); + PFS_scan scan; + uint random= randomized_index(identity, thread_max); - /* - Pass 1: [random, thread_max - 1] - Pass 2: [0, thread_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, thread_max); + scan.has_pass(); + scan.next_pass()) { - PFS_thread *pfs= thread_array + i; - PFS_thread *pfs_last= thread_array + thread_max; + PFS_thread *pfs= thread_array + scan.first(); + PFS_thread *pfs_last= thread_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -728,7 +791,7 @@ find_or_create_file(PFS_thread *thread, const char *filename, uint len) { PFS_file *pfs; - int pass; + PFS_scan scan; if (! filename_hash_inited) { @@ -840,17 +903,14 @@ search: } /* filename is not constant, just using it for noise on create */ - uint i= randomized_index(filename, file_max); + uint random= randomized_index(filename, file_max); - /* - Pass 1: [random, file_max - 1] - Pass 2: [0, file_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, file_max); + scan.has_pass(); + scan.next_pass()) { - pfs= file_array + i; - PFS_file *pfs_last= file_array + file_max; - + pfs= file_array + scan.first(); + PFS_file *pfs_last= file_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) @@ -935,17 +995,15 @@ void destroy_file(PFS_thread *thread, PF */ PFS_table* create_table(PFS_table_share *share, const void *identity) { - int pass; - uint i= randomized_index(identity, table_max); + PFS_scan scan; + uint random= randomized_index(identity, table_max); - /* - Pass 1: [random, table_max - 1] - Pass 2: [0, table_max - 1] - */ - for (pass= 1; pass <= 2; i=0, pass++) + for (scan.init(random, table_max); + scan.has_pass(); + scan.next_pass()) { - PFS_table *pfs= table_array + i; - PFS_table *pfs_last= table_array + table_max; + PFS_table *pfs= table_array + scan.first(); + PFS_table *pfs_last= table_array + scan.last(); for ( ; pfs < pfs_last; pfs++) { if (pfs->m_lock.is_free()) === modified file 'storage/perfschema/pfs_instr.h' --- a/storage/perfschema/pfs_instr.h 2010-04-09 08:22:10 +0000 +++ b/storage/perfschema/pfs_instr.h 2010-04-13 10:05:22 +0000 @@ -21,7 +21,6 @@ Performance schema instruments (declarations). */ -#include #include #include "pfs_lock.h" #include "pfs_instr_class.h" @@ -137,6 +136,48 @@ struct PFS_table : public PFS_instr */ #define LOCKER_STACK_SIZE 3 +/** + @def PFS_MAX_ALLOC_RETRY + Maximum number of times the code attempts to allocate an item + from internal buffers, before giving up. +*/ +#define PFS_MAX_ALLOC_RETRY 1000 + +#define PFS_MAX_SCAN_PASS 2 + +/** + Helper to scan circular buffers. + Given a buffer of size [0, max_size - 1], + and a random starting point in the buffer, + this helper returns up to two [first, last -1] intervals that: + - fit into the [0, max_size - 1] range, + - have a maximum combined length of at most PFS_MAX_ALLOC_RETRY. +*/ +struct PFS_scan +{ +public: + void init(uint random, uint max_size); + + bool has_pass() const + { return (m_pass < m_pass_max); } + + void next_pass() + { m_pass++; } + + uint first() const + { return m_first[m_pass]; } + + uint last() const + { return m_last[m_pass]; } + +private: + uint m_pass; + uint m_pass_max; + uint m_first[PFS_MAX_SCAN_PASS]; + uint m_last[PFS_MAX_SCAN_PASS]; +}; + + /** Instrumented thread implementation. @see PSI_thread. */ struct PFS_thread { --Boundary_(ID_/S0BZ/X247+CVql7vWXA3A) MIME-version: 1.0 Content-type: text/bzr-bundle; CHARSET=US-ASCII; name="bzr/marc.alff@stripped" Content-transfer-encoding: 7BIT Content-disposition: inline; filename="bzr/marc.alff@stripped" # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marc.alff@stripped # target_branch: file:///Users/malff/BZR_TREE/mysql-6.0-codebase-\ # bugfixing/ # testament_sha1: 35ddadbc54a76dfd839e569755074e0f09fe5b2d # timestamp: 2010-04-13 04:05:45 -0600 # source_branch: file:///Users/malff/BZR_TREE/mysql-next-mr-bugfixing/ # base_revision_id: alik@stripped # # Begin bundle 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 --Boundary_(ID_/S0BZ/X247+CVql7vWXA3A)--