From: Tor Didriksen Date: April 14 2011 1:37pm Subject: bzr commit into mysql-trunk branch (tor.didriksen:3318) WL#5774 List-Archive: http://lists.mysql.com/commits/135451 Message-Id: <20110414133719.E9AA737A3@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============4195727821917107168==" --===============4195727821917107168== 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:olav.sandstaa@stripped 3318 Tor Didriksen 2011-04-14 WL#5774 Decrease number of malloc's for normal DML queries One of the malloc's was due to DYNAMIC_ARRAY keyuse; Replace it with an array in MEM_ROOT instead. This fix 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. @ 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-04-14 13:37:15 +0000 @@ -0,0 +1,172 @@ +/* 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. + + 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. + @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. + Maybe we should *require* that Element_type has_trivial_destructor? + (since we do not guarantee destruction of objects in a MEM_ROOT anyways) +*/ +template +class Mem_root_array +{ +public: + Mem_root_array() + : m_root(NULL), m_array(NULL), m_size(0), m_capacity(0) + { + } + + Mem_root_array(MEM_ROOT *root) + : m_root(root), m_array(NULL), m_size(0), m_capacity(0) + { + } + + ~Mem_root_array() + { + clear(); + } + + void set_mem_root(MEM_ROOT *root) { m_root= root; } + + 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 an pointer to the past-the-end element in the array. + Element_type *end() { return &m_array[size()]; } + + // Erases all of the elements. + void clear() + { + chop(0); + } + + /* + Chops the tail off the array, erasing all tail elements. + @param pos Index of first element to erase. + */ + void chop(size_t pos) + { + if (pos >= m_size) + return; + if (!has_trivial_destructor) + { + for (size_t ix= pos; pos < m_size; ++pos) + { + 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= 10; + 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 element_size() const { return sizeof(Element_type); } + size_t size() const { return m_size; } + size_t capacity() const { return m_capacity; } + +private: + MEM_ROOT *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-04-14 09:32:17 +0000 +++ b/sql/sql_select.cc 2011-04-14 13:37:15 +0000 @@ -59,11 +59,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, table_map all_table_map); -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, @@ -3466,7 +3466,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)); } @@ -4629,7 +4629,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; @@ -6016,7 +6016,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; @@ -6046,7 +6046,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; } } @@ -6059,7 +6059,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; @@ -6121,7 +6121,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); } @@ -6237,7 +6237,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) @@ -6280,7 +6280,8 @@ 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)) + keyuse->set_mem_root(thd->mem_root); + if (keyuse->reserve(20)) return TRUE; if (cond) { @@ -6355,21 +6356,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->size()) { 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; @@ -6402,9 +6403,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; @@ -6414,12 +6415,11 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR Update some values in keyuse for faster choose_plan() 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-04-06 11:13:33 +0000 +++ b/sql/sql_select.h 2011-04-14 13:37:15 +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,12 @@ /** 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() { memset(this, 0, sizeof(*this)); } + 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, @@ -93,6 +94,12 @@ public: uint sj_pred_no; }; + +typedef Mem_root_array Key_use_array; +#ifndef DBUG_OFF +void print_keyuse_array(Key_use_array *keyuse_array); +#endif + class store_key; typedef struct st_table_ref : public Sql_alloc @@ -1807,7 +1814,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; === modified file 'sql/sql_test.cc' --- a/sql/sql_test.cc 2010-11-05 22:19:41 +0000 +++ b/sql/sql_test.cc 2011-04-14 13:37:15 +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(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-04-14 13:37:15 +0000 @@ -31,7 +31,6 @@ 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); #endif void mysql_print_status(); === modified file 'unittest/gunit/CMakeLists.txt' --- a/unittest/gunit/CMakeLists.txt 2011-04-13 11:31:44 +0000 +++ b/unittest/gunit/CMakeLists.txt 2011-04-14 13:37:15 +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-04-14 13:37:15 +0000 @@ -0,0 +1,357 @@ +/* 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= 1000; + +/* + 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; + intarr.set_mem_root(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()); +} + + +} --===============4195727821917107168== 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\ # jpsuzmlrlz31or2t # target_branch: file:///export/home/didrik/repo/trunk-dynarray/ # testament_sha1: 855db5816a24cb786f917d65f7b565b286d9f36e # timestamp: 2011-04-14 15:37:19 +0200 # base_revision_id: olav.sandstaa@stripped\ # j8i2y8ra7n4cvwqw # # Begin bundle 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 aYmb/i7kinChIS1PWLQ= --===============4195727821917107168==--