From: Tor Didriksen Date: October 14 2010 3:35pm Subject: bzr commit into mysql-next-mr-bugfixing branch (tor.didriksen:3227) List-Archive: http://lists.mysql.com/commits/120781 Message-Id: <20101014153531.EF1FF3734@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============4297216357888445310==" --===============4297216357888445310== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/didrik/repo/next-mr-opt-team-wl1393-merge/ based on revid:tor.didriksen@stripped 3227 Tor Didriksen 2010-10-14 testing how to templatize Bounded_queue modified: sql/bounded_queue.cc sql/bounded_queue.h sql/filesort.cc unittest/gunit/bounded_queue-t.cc === modified file 'sql/bounded_queue.cc' --- a/sql/bounded_queue.cc 2010-10-12 11:39:45 +0000 +++ b/sql/bounded_queue.cc 2010-10-14 15:35:25 +0000 @@ -13,64 +13,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include "bounded_queue.h" #include "sql_const.h" #include "sql_sort.h" -Bounded_queue::Bounded_queue() -{ - memset(&m_queue, 0, sizeof(m_queue)); -} - - -Bounded_queue::~Bounded_queue() -{ - delete_queue(&m_queue); -} - - -int Bounded_queue::init(ha_rows max_elements, uint offset_to_key, - pbool max_at_top, - queue_compare compare, size_t compare_length, - keymaker_function keymaker, Sort_param *sort_param, - uchar **sort_keys) -{ - DBUG_ASSERT(sort_keys != NULL); - - m_sort_keys= sort_keys; - m_compare_length= compare_length; - m_keymaker= keymaker; - m_sort_param= sort_param; - // init_queue() takes an uint, and also does (max_elements + 1) - if (max_elements >= (UINT_MAX - 1)) - return 1; - // We allocate space for one extra element, for replace when queue is full. - return init_queue(&m_queue, (uint) max_elements + 1, offset_to_key, max_at_top, - compare, &m_compare_length); -} - - -void Bounded_queue::push(uchar *element) -{ - DBUG_ASSERT(is_initialized()); - if (queue_is_full((&m_queue))) - { - uchar **pq_top= (uchar **) queue_top(&m_queue); - (*m_keymaker)(m_sort_param, *pq_top, element); - queue_replaced(&m_queue); - } else { - (*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements], element); - queue_insert(&m_queue, (uchar*)&m_sort_keys[m_queue.elements]); - } -} - - -uchar* Bounded_queue::pop() -{ - return queue_remove(&m_queue, 0); -} - double get_merge_many_buffs_cost_fast(ha_rows num_buffers, ha_rows max_n_elems, ha_rows last_n_elems, uint elem_size) === modified file 'sql/bounded_queue.h' --- a/sql/bounded_queue.h 2010-10-12 11:39:45 +0000 +++ b/sql/bounded_queue.h 2010-10-14 15:35:25 +0000 @@ -16,6 +16,7 @@ #ifndef BOUNDED_QUEUE_INCLUDED #define BOUNDED_QUEUE_INCLUDED +#include #include "my_global.h" #include "my_base.h" #include "queues.h" @@ -36,11 +37,19 @@ typedef void (*keymaker_function)(Sort_p This is a wrapper on top of QUEUE and the queue_xxx() functions. It keeps the top N elements which are inserted. */ +template class Bounded_queue { public: - Bounded_queue(); - ~Bounded_queue(); + Bounded_queue() + { + memset(&m_queue, 0, sizeof(m_queue)); + } + + ~Bounded_queue() + { + delete_queue(&m_queue); + } /** Initialize the queue. @@ -61,7 +70,7 @@ public: int init(ha_rows max_elements, uint offset_to_key, pbool max_at_top, queue_compare compare, size_t compare_length, keymaker_function keymaker, Sort_param *sort_param, - uchar **sort_keys); + Key_type **sort_keys); /** Pushes an element on the queue. @@ -69,14 +78,17 @@ public: @param element The element to be pushed. */ - void push(uchar *element); + void push(Element_type *element); /** Removes an element from the queue. @retval Pointer to the removed element. */ - uchar *pop(); + Key_type **pop() + { + return (Key_type**) queue_remove(&m_queue, 0); + } /** The number of elements in the queue. @@ -89,13 +101,57 @@ public: bool is_initialized() const { return m_queue.max_elements > 0; } private: - uchar **m_sort_keys; + Key_type **m_sort_keys; size_t m_compare_length; keymaker_function m_keymaker; Sort_param *m_sort_param; st_queue m_queue; }; + +template +int Bounded_queue::init(ha_rows max_elements, + uint offset_to_key, + pbool max_at_top, + queue_compare compare, + size_t compare_length, + keymaker_function keymaker, + Sort_param *sort_param, + Key_type **sort_keys) +{ + DBUG_ASSERT(sort_keys != NULL); + + m_sort_keys= sort_keys; + m_compare_length= compare_length; + m_keymaker= keymaker; + m_sort_param= sort_param; + // init_queue() takes an uint, and also does (max_elements + 1) + if (max_elements >= (UINT_MAX - 1)) + return 1; + // We allocate space for one extra element, for replace when queue is full. + return init_queue(&m_queue, (uint) max_elements + 1, offset_to_key, max_at_top, + compare, &m_compare_length); +} + + +template +void Bounded_queue::push(Element_type *element) +{ + DBUG_ASSERT(is_initialized()); + if (queue_is_full((&m_queue))) + { + uchar **pq_top= (uchar **) queue_top(&m_queue); + (*m_keymaker)(m_sort_param, *pq_top, (uchar*) element); + queue_replaced(&m_queue); + } else { + (*m_keymaker)(m_sort_param, + (uchar*) m_sort_keys[m_queue.elements], + (uchar*) element); + queue_insert(&m_queue, (uchar*)&m_sort_keys[m_queue.elements]); + } +} + + /* Calculate cost of merge sort === modified file 'sql/filesort.cc' --- a/sql/filesort.cc 2010-10-12 11:39:45 +0000 +++ b/sql/filesort.cc 2010-10-14 15:35:25 +0000 @@ -47,6 +47,10 @@ */ static double PQ_slowness= 2.0; +#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION +template class Bounded_queue; +#endif + /// How to write record_ref. #define WRITE_REF(file,from) \ if (my_b_write((file),(uchar*) (from),param->ref_length)) \ @@ -60,7 +64,7 @@ static uchar *read_buffpek_from_file(IO_ static ha_rows find_all_keys(Sort_param *param,SQL_SELECT *select, uchar **sort_keys, IO_CACHE *buffer_file, IO_CACHE *tempfile, - Bounded_queue *pq, + Bounded_queue *pq, ha_rows *found_rows); static int write_keys(Sort_param *param,uchar * *sort_keys, uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); @@ -162,7 +166,7 @@ ha_rows filesort(THD *thd, TABLE *table, Sort_param param; bool multi_byte_charset; ha_rows found_rows; - Bounded_queue pq; + Bounded_queue pq; DBUG_ENTER("filesort"); DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length);); @@ -543,7 +547,7 @@ static ha_rows find_all_keys(Sort_param uchar **sort_keys, IO_CACHE *buffpek_pointers, IO_CACHE *tempfile, - Bounded_queue *pq, + Bounded_queue *pq, ha_rows *found_rows) { int error,flag,quick_select; === modified file 'unittest/gunit/bounded_queue-t.cc' --- a/unittest/gunit/bounded_queue-t.cc 2010-10-12 11:39:45 +0000 +++ b/unittest/gunit/bounded_queue-t.cc 2010-10-14 15:35:25 +0000 @@ -18,10 +18,13 @@ #include "my_config.h" #include #include +#include #include "bounded_queue.h" #include "my_sys.h" +void break_here() {} + namespace { const int num_elements= 14; @@ -35,14 +38,45 @@ int array_size(const T (&)[size]) } +struct Test_element +{ + Test_element() { *this= -1; } + Test_element(int i) { *this= i; } + + Test_element &operator=(int i) + { + val= i; + snprintf(text, array_size(text), "%4d", i); + return *this; + } + + char text[8]; + int val; +}; + + +struct Test_key +{ + Test_element *element; + int key; +}; + + extern "C" -int int_ptr_compare(void *cmp_arg, uchar *a, uchar *b) +int test_key_compare(void *cmp_arg, uchar *a, uchar *b) { size_t first_arg= *(size_t*) cmp_arg; EXPECT_EQ(first_arg, sizeof(int)); - int a_num= (**(int**)a); - int b_num= (**(int**)b); + Test_key **a_ptr= (Test_key**) a; + Test_key **b_ptr= (Test_key**) b; + + int a_num= (*a_ptr)->key; + int b_num= (*b_ptr)->key; + + //printf("compare (%p %p %d) (%p %p %d)\n", + //a_ptr, *a_ptr, a_num, b_ptr, *b_ptr, b_num); + if (a_num > b_num) return +1; if (a_num < b_num) @@ -51,17 +85,23 @@ int int_ptr_compare(void *cmp_arg, uchar } -// We use the data value itself as key. +// We use the data value as key. void mock_keymaker(Sort_param *sp, uchar *to, uchar *ref_pos) { - memcpy(to, ref_pos, sizeof(int)); + Test_element *element= (Test_element*) ref_pos; + int key_val= element->val; + + Test_key *key= (Test_key*) to; + key->element= element; + key->key= key_val; + //printf("element %p key %p %d\n", element, key, key_val); } class Bounded_queue_test : public ::testing::Test { protected: - Bounded_queue_test() : m_element_size(sizeof(int)) + Bounded_queue_test() : m_key_size(sizeof(int)) { } @@ -73,17 +113,26 @@ protected: std::random_shuffle(&m_test_data[0], &m_test_data[array_size(m_test_data)]); for (ix=0; ix < array_size(m_key_ptrs); ++ix) - m_key_ptrs[ix]= (uchar*) &m_key_data[ix]; + m_key_ptrs[ix]= &m_key_data[ix]; + + for (ix=0; ix < array_size(m_key_data); ++ix) + { + m_key_data[ix].element= NULL; + m_key_data[ix].key= -1; + } } // Key pointers and data, used by the queue_xxx() functions. - uchar* m_key_ptrs[num_keys]; - int m_key_data[num_keys]; + Test_key *m_key_ptrs[num_keys+1]; + int foo1[1024*16]; + Test_key m_key_data[num_keys+1]; + int foo2[1024*16]; + // Some random intput data, to be sorted. - int m_test_data[num_elements]; + Test_element m_test_data[num_elements]; - size_t m_element_size; - Bounded_queue m_queue; + size_t m_key_size; + Bounded_queue m_queue; private: GTEST_DISALLOW_COPY_AND_ASSIGN_(Bounded_queue_test); }; @@ -99,8 +148,8 @@ typedef Bounded_queue_test Bounded_queue TEST_F(Bounded_queue_DeathTest, die_if_not_initialized) { ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - int foo= 1; - EXPECT_DEATH_IF_SUPPORTED(m_queue.push((uchar*) &foo), + Test_element foo= 1; + EXPECT_DEATH_IF_SUPPORTED(m_queue.push(&foo), ".*Assertion .*is_initialized.*"); } #endif // !defined(DBUG_OFF) @@ -112,8 +161,8 @@ TEST_F(Bounded_queue_DeathTest, die_if_n TEST_F(Bounded_queue_test, construct_and_destruct) { EXPECT_EQ(0, m_queue.init(num_elements/2, 0, TRUE, - (queue_compare) (get_ptr_compare(m_element_size)), - m_element_size, + (queue_compare) (get_ptr_compare(m_key_size)), + m_key_size, &mock_keymaker, NULL, m_key_ptrs)); } @@ -124,12 +173,12 @@ TEST_F(Bounded_queue_test, construct_and TEST_F(Bounded_queue_test, too_many_elements) { EXPECT_EQ(1, m_queue.init(UINT_MAX, 0, TRUE, - (queue_compare) (get_ptr_compare(m_element_size)), - m_element_size, + (queue_compare) (get_ptr_compare(m_key_size)), + m_key_size, &mock_keymaker, NULL, m_key_ptrs)); EXPECT_EQ(1, m_queue.init(UINT_MAX - 1, 0, TRUE, - (queue_compare) (get_ptr_compare(m_element_size)), - m_element_size, + (queue_compare) (get_ptr_compare(m_key_size)), + m_key_size, &mock_keymaker, NULL, m_key_ptrs)); } @@ -139,22 +188,22 @@ TEST_F(Bounded_queue_test, too_many_elem */ TEST_F(Bounded_queue_test, push_and_pop) { - EXPECT_EQ(0, m_queue.init(num_elements/2, 0, TRUE, int_ptr_compare, - m_element_size, + EXPECT_EQ(0, m_queue.init(num_elements/2, 0, TRUE, test_key_compare, + m_key_size, &mock_keymaker, NULL, m_key_ptrs)); for (int ix= 0; ix < array_size(m_test_data); ++ix) { - m_queue.push((uchar*) &m_test_data[ix]); + m_queue.push(&m_test_data[ix]); } - int prev_element= num_elements; - int expected_value= num_elements / 2; - while (!m_queue.num_elements()) - { - uchar *top_p= m_queue.pop(); - int *top= *(int**) top_p; - EXPECT_GT(prev_element, *top); - EXPECT_EQ(expected_value--, *top); - prev_element= *top; + int expected_key_val= num_elements/2; + while (m_queue.num_elements() > 0) + { + Test_key **top= m_queue.pop(); + int key_val= (*top)->key; + EXPECT_EQ(expected_key_val, key_val); + Test_element *element= (*top)->element; + EXPECT_EQ(expected_key_val, element->val); + --expected_key_val; } } @@ -164,22 +213,26 @@ TEST_F(Bounded_queue_test, push_and_pop) */ TEST_F(Bounded_queue_test, insert_and_sort) { - EXPECT_EQ(0, m_queue.init(num_elements/2, 0, TRUE, int_ptr_compare, - m_element_size, + EXPECT_EQ(0, m_queue.init(num_elements/2, 0, TRUE, test_key_compare, + m_key_size, &mock_keymaker, NULL, m_key_ptrs)); for (int ix= 0; ix < array_size(m_test_data); ++ix) { - m_queue.push((uchar*) &m_test_data[ix]); + m_queue.push(&m_test_data[ix]); } - uchar *base= (uchar*) m_key_ptrs; + + // Sort our keys as strings, so erase all the element pointers first. + uchar *base= (uchar*) &m_key_ptrs[0]; uint items= m_queue.num_elements(); - size_t size= m_element_size; + size_t size= sizeof(Test_key); + for (int ii= 0; ii < array_size(m_key_data); ++ii) + m_key_data[ii].element= NULL; + my_string_ptr_sort(base, items, size); for (int ii= 0; ii < num_elements/2; ++ii) { - uchar *uchar_ptr= m_key_ptrs[ii]; - int sorted_val= *(int*) uchar_ptr; - EXPECT_EQ(ii, sorted_val); + Test_key *sorted_key= m_key_ptrs[ii]; + EXPECT_EQ(ii, sorted_key->key); } } @@ -213,31 +266,31 @@ TEST(Cost_estimation_test, merge_many_bu Run the with 'bounded_queue-t --disable-tap-output' to see the millisecond output from Google Test. */ -const int num_rows= 100000; +const int num_rows= 1000; const int limit= 100; -const int num_iterations= 100; +const int num_iterations= 10000; /* Test with Bounded_queue size == limit. */ TEST(Bounded_queue_performance, with_small_queue) { - uchar* key_ptrs[limit+1]; - int key_data[limit+1]; + int *key_ptrs[limit+1]; + int key_data[limit+1]; for (int it= 0; it < num_iterations; ++it) { int ix; for (ix=0; ix < array_size(key_ptrs); ++ix) - key_ptrs[ix]= (uchar*) &key_data[ix]; + key_ptrs[ix]= &key_data[ix]; srand(0); - Bounded_queue queue; - EXPECT_EQ(0, queue.init(limit, 0, TRUE, int_ptr_compare, + Bounded_queue queue; + EXPECT_EQ(0, queue.init(limit, 0, TRUE, test_key_compare, sizeof(int), &mock_keymaker, NULL, key_ptrs)); for (ix= 0; ix < num_rows; ++ix) { int data= rand(); - queue.push((uchar*) &data); + queue.push(&data); } - my_string_ptr_sort((uchar*) key_ptrs, queue.num_elements(), sizeof(int)); + my_string_ptr_sort((uchar*) &key_ptrs[0], queue.num_elements(), sizeof(int)); } } @@ -245,25 +298,25 @@ TEST(Bounded_queue_performance, with_sma /* Test with Bounded_queue size == */ -uchar *key_ptrs[num_rows]; -int key_data[num_rows]; +int *key_ptrs[num_rows]; +int key_data[num_rows]; TEST(Bounded_queue_performance, with_large_queue) { for (int it= 0; it < num_iterations; ++it) { int ix; for (ix=0; ix < array_size(key_ptrs); ++ix) - key_ptrs[ix]= (uchar*) &key_data[ix]; + key_ptrs[ix]= &key_data[ix]; srand(0); - Bounded_queue queue; - EXPECT_EQ(0, queue.init(num_rows, 0, TRUE, int_ptr_compare, + Bounded_queue queue; + EXPECT_EQ(0, queue.init(num_rows, 0, TRUE, test_key_compare, sizeof(int), &mock_keymaker, NULL, key_ptrs)); for (ix= 0; ix < num_rows; ++ix) { int data= rand(); - queue.push((uchar*) &data); + queue.push(&data); } - my_string_ptr_sort((uchar*) key_ptrs, queue.num_elements(), sizeof(int)); + my_string_ptr_sort((uchar*) &key_ptrs[0], queue.num_elements(), sizeof(int)); } } @@ -277,14 +330,14 @@ TEST(Bounded_queue_performance, without_ { int ix; for (ix=0; ix < array_size(key_ptrs); ++ix) - key_ptrs[ix]= (uchar*) &key_data[ix]; + key_ptrs[ix]= &key_data[ix]; srand(0); for (ix= 0; ix < num_rows; ++ix) { int data= rand(); key_data[ix]= data; } - my_string_ptr_sort((uchar*) key_ptrs, num_rows, sizeof(int)); + my_string_ptr_sort((uchar*) &key_ptrs[0], num_rows, sizeof(int)); } } @@ -298,7 +351,7 @@ TEST(Bounded_queue_performance, no_sorti { int ix; for (ix=0; ix < array_size(key_ptrs); ++ix) - key_ptrs[ix]= (uchar*) &key_data[ix]; + key_ptrs[ix]= &key_data[ix]; srand(0); for (ix= 0; ix < num_rows; ++ix) { --===============4297216357888445310== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/tor.didriksen@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: tor.didriksen@stripped\ # ebynhbs9p1icjgcr # target_branch: file:///export/home/didrik/repo/next-mr-opt-team-\ # wl1393-merge/ # testament_sha1: 3f5915a8d46070d152e2a82809f655d466dfb063 # timestamp: 2010-10-14 17:35:31 +0200 # base_revision_id: tor.didriksen@stripped\ # zg4z3dzenikh75fs # # Begin bundle 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 63Xjp0B01KdkoFkChP/F3JFOFCQ4ReWhgA== --===============4297216357888445310==--