From: Tor Didriksen Date: May 18 2011 8:29am Subject: bzr commit into mysql-trunk branch (tor.didriksen:3099) Bug#12552221 List-Archive: http://lists.mysql.com/commits/137636 X-Bug: 12552221 Message-Id: <20110518082951.610BC37B2@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============6388841850890287900==" --===============6388841850890287900== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/didrik/repo/trunk-dynarray/ based on revid:sergey.glukhov@stripped 3099 Tor Didriksen 2011-05-18 Bug#12552221 - MEMORY LEAK IN UPDATE_REF_AND_KEYS Allocate array in memroot rather than heap. Fixes the leak, and also yields a 1-2% performance gain with sysbench if you have lots of cpus/threads. @ sql/mem_root_array.h A typesafe replacement for DYNAMIC_ARRAY. We use MEM_ROOT for allocating storage, rather than the C++ heap. The interface is chosen to be similar to std::vector. @ sql/sql_select.cc Use Key_use_array rather than DYNAMIC_ARRAY. @ sql/sql_select.h Use Key_use_array rather than DYNAMIC_ARRAY. @ sql/sql_test.cc Use Key_use_array rather than DYNAMIC_ARRAY. @ sql/sql_test.h Use Key_use_array rather than DYNAMIC_ARRAY. @ unittest/gunit/dynarray-t.cc Unit tests for Mem_root_array. Also performance testing comparing DYNAMIC_ARRAY with std::vector and Mem_root_array. added: sql/mem_root_array.h unittest/gunit/dynarray-t.cc modified: sql/sql_select.cc sql/sql_select.h sql/sql_test.cc sql/sql_test.h unittest/gunit/CMakeLists.txt === added file 'sql/mem_root_array.h' --- a/sql/mem_root_array.h 1970-01-01 00:00:00 +0000 +++ b/sql/mem_root_array.h 2011-05-18 08:29:46 +0000 @@ -0,0 +1,175 @@ +/* Copyright (c) 2011, 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifndef MEM_ROOT_ARRAY_INCLUDED +#define MEM_ROOT_ARRAY_INCLUDED + +#include + +/** + A typesafe replacement for DYNAMIC_ARRAY. + We use MEM_ROOT for allocating storage, rather than the C++ heap. + The interface is chosen to be similar to std::vector. + + @remark + Unlike DYNAMIC_ARRAY, elements are properly copied + (rather than memcpy()d) if the underlying array needs to be expanded. + + @remark + Depending on has_trivial_destructor, we destroy objects which are + removed from the array (including when the array object itself is destroyed). + + @remark + Note that MEM_ROOT has no facility for reusing free space, + so don't use this if multiple re-expansions are likely to happen. + + @param Element_type The type of the elements of the container. + Elements must be copyable. + @param has_trivial_destructor If true, we don't destroy elements. + We could have used type traits to determine this. + __has_trivial_destructor is supported by some (but not all) + compilers we use. +*/ +template +class Mem_root_array +{ +public: + Mem_root_array(MEM_ROOT *root) + : m_root(root), m_array(NULL), m_size(0), m_capacity(0) + { + DBUG_ASSERT(m_root != NULL); + } + + ~Mem_root_array() + { + clear(); + } + + Element_type &at(size_t n) + { + DBUG_ASSERT(n < size()); + return m_array[n]; + } + + const Element_type &at(size_t n) const + { + DBUG_ASSERT(n < size()); + return m_array[n]; + } + + // Returns a pointer to the first element in the array. + Element_type *begin() { return &m_array[0]; } + + // Returns a pointer to the past-the-end element in the array. + Element_type *end() { return &m_array[size()]; } + + // Erases all of the elements. + void clear() + { + if (!empty()) + chop(0); + } + + /* + Chops the tail off the array, erasing all tail elements. + @param pos Index of first element to erase. + */ + void chop(const size_t pos) + { + DBUG_ASSERT(pos < m_size); + if (!has_trivial_destructor) + { + for (size_t ix= pos; ix < m_size; ++ix) + { + Element_type *p= &m_array[ix]; + p->~Element_type(); // Destroy discarded element. + } + } + m_size= pos; + } + + /* + Reserves space for array elements. + Copies over existing elements, in case we are re-expanding the array. + + @param n number of elements. + @retval true if out-of-memory, false otherwise. + */ + bool reserve(size_t n) + { + if (n <= m_capacity) + return false; + + void *mem= alloc_root(m_root, n * element_size()); + if (!mem) + return true; + Element_type *array= static_cast(mem); + + // Copy all the existing elements into the new array. + for (size_t ix= 0; ix < m_size; ++ix) + { + Element_type *new_p= &array[ix]; + Element_type *old_p= &m_array[ix]; + new (new_p) Element_type(*old_p); // Copy into new location. + if (!has_trivial_destructor) + old_p->~Element_type(); // Destroy the old element. + } + + // Forget the old array. + m_array= array; + m_capacity= n; + return false; + } + + /* + Adds a new element at the end of the array, after its current last + element. The content of this new element is initialized to a copy of + the input argument. + + @param element Object to copy. + @retval true if out-of-memory, false otherwise. + */ + bool push_back(const Element_type &element) + { + const size_t min_capacity= 20; + const size_t expansion_factor= 2; + if (0 == m_capacity && reserve(min_capacity)) + return true; + if (m_size == m_capacity && reserve(m_capacity * expansion_factor)) + return true; + Element_type *p= &m_array[m_size++]; + new (p) Element_type(element); + return false; + } + + size_t capacity() const { return m_capacity; } + size_t element_size() const { return sizeof(Element_type); } + bool empty() const { return size() == 0; } + size_t size() const { return m_size; } + +private: + MEM_ROOT *const m_root; + Element_type *m_array; + size_t m_size; + size_t m_capacity; + + // Not (yet) implemented. + Mem_root_array(const Mem_root_array&); + Mem_root_array &operator=(const Mem_root_array&); +}; + + +#endif // MEM_ROOT_ARRAY_INCLUDED === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2011-05-16 11:01:28 +0000 +++ b/sql/sql_select.cc 2011-05-18 08:29:46 +0000 @@ -38,8 +38,7 @@ #include "sql_parse.h" // check_stack_overrun #include "sql_partition.h" // make_used_partitions_str #include "sql_acl.h" // *_ACL -#include "sql_test.h" // print_where, print_keyuse_array, - // print_sjm, print_plan, TEST_join +#include "sql_test.h" // misc. debug printing utilities #include "records.h" // init_read_record, end_read_record #include "filesort.h" // filesort_free_buffers #include "sql_union.h" // mysql_union @@ -59,11 +58,11 @@ const char *join_type_str[]={ "UNKNOWN", struct st_sargable_param; -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); +static void optimize_keyuse(JOIN *join, Key_use_array *keyuse_array); static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, Item *conds, - DYNAMIC_ARRAY *keyuse); + Key_use_array *keyuse); static bool optimize_semijoin_nests(JOIN *join); -static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, +static bool update_ref_and_keys(THD *thd, Key_use_array *keyuse, JOIN_TAB *join_tab, uint tables, Item *conds, COND_EQUAL *cond_equal, @@ -3547,7 +3546,7 @@ bool JOIN::destroy() while ((sj_nest= sj_list_it++)) sj_nest->sj_mat_exec= NULL; - delete_dynamic(&keyuse); + keyuse.clear(); delete procedure; DBUG_RETURN(test(error)); } @@ -4710,7 +4709,7 @@ static uint get_tmp_table_rec_length(Lis static bool make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds, - DYNAMIC_ARRAY *keyuse_array) + Key_use_array *keyuse_array) { int error; TABLE *table; @@ -6092,7 +6091,7 @@ max_part_bit(key_part_map bits) */ static bool -add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) +add_key_part(Key_use_array *keyuse_array, KEY_FIELD *key_field) { Field *field=key_field->field; TABLE *form= field->table; @@ -6122,7 +6121,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array key_field->null_rejecting, key_field->cond_guard, key_field->sj_pred_no); - if (insert_dynamic(keyuse_array, &keyuse)) + if (keyuse_array->push_back(keyuse)) return TRUE; } } @@ -6135,7 +6134,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array #define FT_KEYPART (MAX_REF_PARTS+10) static bool -add_ft_keys(DYNAMIC_ARRAY *keyuse_array, +add_ft_keys(Key_use_array *keyuse_array, JOIN_TAB *stat,Item *cond,table_map usable_tables) { Item_func_match *cond_func=NULL; @@ -6197,7 +6196,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, false, // null_rejecting NULL, // cond_guard UINT_MAX); // sj_pred_no - return insert_dynamic(keyuse_array, &keyuse); + return keyuse_array->push_back(keyuse); } @@ -6313,7 +6312,7 @@ static void add_key_fields_for_nj(JOIN * */ static bool -update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, +update_ref_and_keys(THD *thd, Key_use_array *keyuse,JOIN_TAB *join_tab, uint tables, Item *cond, COND_EQUAL *cond_equal, table_map normal_tables, SELECT_LEX *select_lex, SARGABLE_PARAM **sargables) @@ -6356,8 +6355,6 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR /* set a barrier for the array of SARGABLE_PARAM */ (*sargables)[0].field= 0; - if (my_init_dynamic_array(keyuse, sizeof(Key_use), 20, 64)) - return TRUE; if (cond) { add_key_fields(join_tab->join, &end, &and_level, cond, normal_tables, @@ -6431,21 +6428,21 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR used in the query, we drop the partial key parts from consideration). Special treatment for ft-keys. */ - if (keyuse->elements) + if (!keyuse->empty()) { Key_use *save_pos, *use; - my_qsort(keyuse->buffer, keyuse->elements, sizeof(Key_use), + my_qsort(keyuse->begin(), keyuse->size(), keyuse->element_size(), reinterpret_cast(sort_keyuse)); const Key_use key_end(NULL, NULL, 0, 0, 0, 0, 0, 0, false, NULL, 0); - if (insert_dynamic(keyuse, &key_end)) // added for easy testing + if (keyuse->push_back(key_end)) // added for easy testing return TRUE; - use= save_pos= dynamic_element(keyuse, 0, Key_use *); + use= save_pos= keyuse->begin(); const Key_use *prev= &key_end; found_eq_constant=0; - for (i=0 ; i < keyuse->elements-1 ; i++,use++) + for (i=0 ; i < keyuse->size()-1 ; i++,use++) { if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL) use->table->const_key_parts[use->key]|= use->keypart_map; @@ -6478,9 +6475,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR use->table->reginfo.join_tab->checked_keys.set_bit(use->key); save_pos++; } - i= (uint) (save_pos - (Key_use *)keyuse->buffer); - (void) set_dynamic(keyuse, &key_end, i); - keyuse->elements=i; + i= (uint) (save_pos - keyuse->begin()); + keyuse->at(i) = key_end; + keyuse->chop(i); } DBUG_EXECUTE("opt", print_keyuse_array(keyuse);); return FALSE; @@ -6490,12 +6487,11 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR Update some values in keyuse for faster choose_table_order() loop. */ -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) +static void optimize_keyuse(JOIN *join, Key_use_array *keyuse_array) { - Key_use *end, *keyuse= dynamic_element(keyuse_array, 0, Key_use *); - - for (end= keyuse+ keyuse_array->elements ; keyuse < end ; keyuse++) + for (size_t ix= 0; ix < keyuse_array->size(); ++ix) { + Key_use *keyuse= &keyuse_array->at(ix); table_map map; /* If we find a ref, assume this table matches a proportional === modified file 'sql/sql_select.h' --- a/sql/sql_select.h 2011-05-02 11:51:41 +0000 +++ b/sql/sql_select.h 2011-05-18 08:29:46 +0000 @@ -30,6 +30,7 @@ #include "records.h" /* READ_RECORD */ #include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */ +#include "mem_root_array.h" /* Values in optimize */ #define KEY_OPTIMIZE_EXISTS 1 @@ -37,12 +38,24 @@ /** Information about usage of an index to satisfy an equality condition. - - @note such objects are stored in DYNAMIC_ARRAY which uses sizeof(), so keep - this class as POD as possible. */ class Key_use { public: + // We need the default constructor for unit testing. + Key_use() + : table(NULL), + val(NULL), + used_tables(0), + key(0), + keypart(0), + optimize(0), + keypart_map(0), + ref_table_rows(0), + null_rejecting(false), + cond_guard(NULL), + sj_pred_no(UINT_MAX) + {} + Key_use(TABLE *table_arg, Item *val_arg, table_map used_tables_arg, uint key_arg, uint keypart_arg, uint optimize_arg, key_part_map keypart_map_arg, ha_rows ref_table_rows_arg, @@ -85,7 +98,7 @@ public: bool *cond_guard; /** 0..64 <=> This was created from semi-join IN-equality # sj_pred_no. - MAX_UINT Otherwise + UINT_MAX Otherwise Not used if the index is fulltext (such index cannot be used for semijoin). @@ -93,6 +106,10 @@ public: uint sj_pred_no; }; + +// Key_use has a trivial destructor, no need to run it from Mem_root_array. +typedef Mem_root_array Key_use_array; + class store_key; typedef struct st_table_ref : public Sql_alloc @@ -1816,7 +1833,9 @@ public: bool skip_sort_order; bool need_tmp, hidden_group_fields; - DYNAMIC_ARRAY keyuse; + + Key_use_array keyuse; + List all_fields; ///< to store all fields that used in query ///Above list changed to use temporary table List tmp_all_fields1, tmp_all_fields2, tmp_all_fields3; @@ -1892,7 +1911,9 @@ public: JOIN(THD *thd_arg, List &fields_arg, ulonglong select_options_arg, select_result *result_arg) - :fields_list(fields_arg), sj_subselects(thd_arg->mem_root, 4) + : keyuse(thd_arg->mem_root), + fields_list(fields_arg), + sj_subselects(thd_arg->mem_root, 4) { init(thd_arg, fields_arg, select_options_arg, result_arg); } @@ -1946,7 +1967,7 @@ public: all_fields= fields_arg; if (&fields_list != &fields_arg) /* Avoid valgrind-warning */ fields_list= fields_arg; - bzero((char*) &keyuse,sizeof(keyuse)); + keyuse.clear(); tmp_table_param.init(); tmp_table_param.end_write_records= HA_POS_ERROR; rollup.state= ROLLUP::STATE_NONE; === modified file 'sql/sql_test.cc' --- a/sql/sql_test.cc 2010-11-05 22:19:41 +0000 +++ b/sql/sql_test.cc 2011-05-18 08:29:46 +0000 @@ -239,7 +239,7 @@ TEST_join(JOIN *join) #define FT_KEYPART (MAX_REF_PARTS+10) -void print_keyuse(Key_use *keyuse) +void print_keyuse(const Key_use *keyuse) { char buff[256]; char buf2[64]; @@ -266,14 +266,14 @@ void print_keyuse(Key_use *keyuse) /* purecov: begin inspected */ -void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array) +void print_keyuse_array(const Key_use_array *keyuse_array) { DBUG_LOCK_FILE; - fprintf(DBUG_FILE, "Key_use array (%d elements)\n", keyuse_array->elements); + fprintf(DBUG_FILE, "Key_use array (%d elements)\n", + (int) keyuse_array->size()); DBUG_UNLOCK_FILE; - for(uint i=0; i < keyuse_array->elements; i++) - print_keyuse(reinterpret_cast - (dynamic_array_ptr(keyuse_array, i))); + for(uint i=0; i < keyuse_array->size(); i++) + print_keyuse(&keyuse_array->at(i)); } === modified file 'sql/sql_test.h' --- a/sql/sql_test.h 2010-08-19 07:10:58 +0000 +++ b/sql/sql_test.h 2011-05-18 08:29:46 +0000 @@ -17,6 +17,7 @@ #define SQL_TEST_INCLUDED #include "mysqld.h" +#include "sql_select.h" class JOIN; struct TABLE_LIST; @@ -31,7 +32,7 @@ void print_plan(JOIN* join,uint idx, dou double current_read_time, const char *info); void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl); void print_sjm(TABLE_LIST *emb_sj_nest); -void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array); +void print_keyuse_array(const Key_use_array *keyuse_array); #endif void mysql_print_status(); === modified file 'unittest/gunit/CMakeLists.txt' --- a/unittest/gunit/CMakeLists.txt 2011-05-13 09:36:13 +0000 +++ b/unittest/gunit/CMakeLists.txt 2011-05-18 08:29:46 +0000 @@ -206,6 +206,7 @@ ENDIF() SET(TESTS bounded_queue dbug + dynarray mdl mdl_mytap my_bitmap === added file 'unittest/gunit/dynarray-t.cc' --- a/unittest/gunit/dynarray-t.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/dynarray-t.cc 2011-05-18 08:29:46 +0000 @@ -0,0 +1,408 @@ +/* Copyright (c) 2011, 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 + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +// First include (the generated) my_config.h, to get correct platform defines, +// then gtest.h (before any other MySQL headers), to avoid min() macros etc ... +#include "my_config.h" +#include + +#include +#include +#include + +#include "sql_select.h" +#include "mem_root_array.h" + +/** + WL#5774 Decrease number of malloc's for normal DML queries. + One of the malloc's was due to DYNAMIC_ARRAY keyuse; + We replace the DYNAMIC_ARRAY with a std::vector-like class Mem_root_array. + + Below are unit tests for comparing performance, and for testing + functionality of Mem_root_array. +*/ + +pthread_key(MEM_ROOT**, THR_MALLOC); +pthread_key(THD*, THR_THD); + +extern "C" void sql_alloc_error_handler(void) +{ + ADD_FAILURE(); +} + + +/* + Rewrite of sort_keyuse() to comparison operator for use by std::less<> + It is a template argument, so static rather than in unnamed namespace. +*/ +static inline bool operator<(const Key_use &a, const Key_use &b) +{ + if (a.table->tablenr != b.table->tablenr) + return a.table->tablenr < b.table->tablenr; + if (a.key != b.key) + return a.key < b.key; + if (a.keypart != b.keypart) + return a.keypart < b.keypart; + const bool atab = test((a.used_tables & ~OUTER_REF_TABLE_BIT)); + const bool btab = test((b.used_tables & ~OUTER_REF_TABLE_BIT)); + if (atab != btab) + return atab < btab; + return + ((a.optimize & KEY_OPTIMIZE_REF_OR_NULL) < + (b.optimize & KEY_OPTIMIZE_REF_OR_NULL)); +} + + +/* + Compare for equality. + It is a template argument, so static rather than in unnamed namespace. +*/ +static inline bool operator==(const Key_use &lhs, const Key_use &rhs) +{ + return + lhs.table->tablenr == rhs.table->tablenr && + lhs.key == rhs.key && + lhs.keypart == rhs.keypart && + test((lhs.used_tables & ~OUTER_REF_TABLE_BIT)) + == + test((rhs.used_tables & ~OUTER_REF_TABLE_BIT)) && + (lhs.optimize & KEY_OPTIMIZE_REF_OR_NULL) + == + (rhs.optimize & KEY_OPTIMIZE_REF_OR_NULL); +} + + +namespace { + +/* + Cut'n paste this function from sql_select.cc, + to avoid linking in the entire server for this unit test. +*/ +inline int sort_keyuse(Key_use *a, Key_use *b) +{ + int res; + if (a->table->tablenr != b->table->tablenr) + return (int) (a->table->tablenr - b->table->tablenr); + if (a->key != b->key) + return (int) (a->key - b->key); + if (a->keypart != b->keypart) + return (int) (a->keypart - b->keypart); + // Place const values before other ones + if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) - + test((b->used_tables & ~OUTER_REF_TABLE_BIT)))) + return res; + /* Place rows that are not 'OPTIMIZE_REF_OR_NULL' first */ + return (int) ((a->optimize & KEY_OPTIMIZE_REF_OR_NULL) - + (b->optimize & KEY_OPTIMIZE_REF_OR_NULL)); +} + + +std::ostream &operator<<(std::ostream &s, const Key_use &v) +{ + return s << "{" + << v.table->tablenr << ", " + << v.key << ", " + << v.keypart << ", " + << v.used_tables << ", " + << v.optimize + << "}" + ; +} + + +// We generate some random data at startup, for testing of sorting. +void generate_test_data(Key_use *keys, TABLE *tables, int n) +{ + int ix; + for (ix= 0; ix < n; ++ix) + { + tables[ix].tablenr= ix % 3; + keys[ix]= + Key_use(&tables[ix], + NULL, // Item *val + 0, // table_map used_tables + ix % 4, // uint key + ix % 2, // uint keypart + 0, // uint optimize + 0, // keypart_map + 0, // ha_rows ref_table_rows + true, // bool null_rejecting + NULL, // bool *cond_guard + 0 // uint sj_pred_no + ); + } + std::random_shuffle(&keys[0], &keys[n]); +} + + +// Play around with these constants to see std::sort speedup vs. my_qsort. +const int num_elements= 200; +const int num_iterations= 10; + +/* + This class is used for comparing performance of + std::vector<> and std::sort() + vs + DYNAMIC_ARRAY and my_qsort() + */ +class DynArrayTest : public ::testing::Test +{ +public: + DynArrayTest() {} + + static void SetUpTestCase() + { + generate_test_data(test_data, table_list, num_elements); + } + + virtual void SetUp() + { + my_init_dynamic_array(&m_keyuse_dyn, sizeof(Key_use), num_elements, 64); + m_keyuse_vec.reserve(num_elements); + } + + void insert_and_sort_dynamic() + { + reset_dynamic(&m_keyuse_dyn); + for (int ix= 0; ix < num_elements; ++ix) + { + insert_dynamic(&m_keyuse_dyn, &test_data[ix]); + } + my_qsort(m_keyuse_dyn.buffer, m_keyuse_dyn.elements, sizeof(Key_use), + reinterpret_cast(sort_keyuse)); + } + + void insert_and_sort_vector() + { + m_keyuse_vec.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_keyuse_vec.push_back(test_data[ix]); + } + std::sort(m_keyuse_vec.begin(), m_keyuse_vec.end(), std::less()); + } + + DYNAMIC_ARRAY m_keyuse_dyn; + std::vector m_keyuse_vec; +private: + static Key_use test_data[num_elements]; + static TABLE table_list[num_elements]; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DynArrayTest); +}; + +Key_use DynArrayTest::test_data[num_elements]; +TABLE DynArrayTest::table_list[num_elements]; + + +// Test insert_dynamic() and my_qsort(). +TEST_F(DynArrayTest, DynArray) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_dynamic(); +} + + +// Test vector::push_back() and std::sort() +TEST_F(DynArrayTest, Vector) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_vector(); +} + + +/* + This class is for unit testing of Mem_root_array. + */ +class MemRootTest : public ::testing::Test +{ +protected: + MemRootTest() + : m_mem_root_p(&m_mem_root), + m_array_mysys(m_mem_root_p), + m_array_std(m_mem_root_p) + {} + + virtual void SetUp() + { + init_sql_alloc(&m_mem_root, 1024, 0); + ASSERT_EQ(0, my_pthread_setspecific_ptr(THR_MALLOC, &m_mem_root_p)); + MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); + ASSERT_EQ(root, m_mem_root_p); + + m_array_mysys.reserve(num_elements); + m_array_std.reserve(num_elements); + } + + virtual void TearDown() + { + free_root(&m_mem_root, MYF(0)); + } + + static void SetUpTestCase() + { + generate_test_data(test_data, table_list, num_elements); + ASSERT_EQ(0, pthread_key_create(&THR_THD, NULL)); + ASSERT_EQ(0, pthread_key_create(&THR_MALLOC, NULL)); + } + + static void TearDownTestCase() + { + pthread_key_delete(THR_THD); + pthread_key_delete(THR_MALLOC); + } + + void insert_and_sort_mysys() + { + m_array_mysys.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_array_mysys.push_back(test_data[ix]); + } + my_qsort(m_array_mysys.begin(), m_array_mysys.size(), + m_array_mysys.element_size(), + reinterpret_cast(sort_keyuse)); + } + + void insert_and_sort_std() + { + m_array_std.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_array_std.push_back(test_data[ix]); + } + std::sort(m_array_std.begin(), m_array_std.end(), std::less()); + } + + MEM_ROOT m_mem_root; + MEM_ROOT *m_mem_root_p; + Key_use_array m_array_mysys; + Key_use_array m_array_std; +private: + static Key_use test_data[num_elements]; + static TABLE table_list[num_elements]; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(MemRootTest); +}; + +Key_use MemRootTest::test_data[num_elements]; +TABLE MemRootTest::table_list[num_elements]; + + +// Test Mem_root_array::push_back() and my_qsort() +TEST_F(MemRootTest, KeyUseMysys) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_mysys(); +} + + +// Test Mem_root_array::push_back() and std::sort() +TEST_F(MemRootTest, KeyUseStd) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_std(); +} + + +// Test that my_qsort() and std::sort() generate same order. +TEST_F(MemRootTest, KeyUseCompare) +{ + insert_and_sort_mysys(); + insert_and_sort_std(); + for (int ix= 0; ix < num_elements; ++ix) + { + Key_use k1= m_array_mysys.at(ix); + Key_use k2= m_array_std.at(ix); + EXPECT_EQ(k1, k2); + } +} + + +// Test that Mem_root_array re-expanding works. +TEST_F(MemRootTest, Reserve) +{ + Mem_root_array intarr(m_mem_root_p); + intarr.reserve(2); + const uint num_pushes= 20; + for (uint ix=0; ix < num_pushes; ++ix) + { + EXPECT_EQ(ix, intarr.size()); + EXPECT_FALSE(intarr.push_back(ix)); + EXPECT_EQ(ix, intarr.at(ix)); + } + for (uint ix=0; ix < num_pushes; ++ix) + { + EXPECT_EQ(ix, intarr.at(ix)); + } + EXPECT_EQ(sizeof(uint), intarr.element_size()); + EXPECT_EQ(num_pushes, intarr.size()); + EXPECT_LE(num_pushes, intarr.capacity()); +} + + +class DestroyCounter +{ +public: + DestroyCounter(const DestroyCounter &rhs) : p_counter(rhs.p_counter) {} + DestroyCounter(size_t *p) : p_counter(p) {} + ~DestroyCounter() { (*p_counter)+= 1; } +private: + size_t *p_counter; +}; + + +// Test chop() and clear() and that destructors are executed. +TEST_F(MemRootTest, ChopAndClear) +{ + Mem_root_array array(m_mem_root_p); + const size_t nn= 4; + array.reserve(nn); + size_t counter= 0; + DestroyCounter foo(&counter); + for (size_t ix= 0; ix < array.capacity(); ++ix) + array.push_back(foo); + + EXPECT_EQ(0U, counter); + array.chop(nn / 2); + EXPECT_EQ(nn / 2, counter); + EXPECT_EQ(nn / 2, array.size()); + + array.clear(); + EXPECT_EQ(nn, counter); +} + + +// Test that elements are destroyed if push_back() needs to call reserve(). +TEST_F(MemRootTest, ReserveDestroy) +{ + Mem_root_array array(m_mem_root_p); + const size_t nn= 4; + array.reserve(nn / 2); + size_t counter= 0; + DestroyCounter foo(&counter); + for (size_t ix= 0; ix < nn; ++ix) + array.push_back(foo); + + EXPECT_EQ(nn / 2, counter); + EXPECT_EQ(nn, array.size()); + + counter= 0; + array.clear(); + EXPECT_EQ(nn, counter); +} + + +} --===============6388841850890287900== 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\ # h2oxanu2lqthr64c # target_branch: file:///export/home/didrik/repo/trunk-dynarray/ # testament_sha1: 56d91b553f60ae1e601c6f649694db808756fd69 # timestamp: 2011-05-18 10:29:51 +0200 # base_revision_id: sergey.glukhov@stripped\ # dudqede7tc6c8oj3 # # Begin bundle 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 AuqDWSKp0i9qE2XuE840v/i7kinChIGZwvgQ --===============6388841850890287900==--