MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
bounded_queue-t.cc
1 /* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
15 
16 // First include (the generated) my_config.h, to get correct platform defines.
17 #include "my_config.h"
18 #include <gtest/gtest.h>
19 #include <algorithm>
20 #include <stddef.h>
21 
22 #include "bounded_queue.h"
23 #include "filesort_utils.h"
24 #include "my_sys.h"
25 
26 namespace bounded_queue_unittest {
27 
28 const int num_elements= 14;
29 
30 // A simple helper function to determine array size.
31 template <class T, int size>
32 int array_size(const T (&)[size])
33 {
34  return size;
35 }
36 
37 
38 /*
39  Elements to be sorted by tests below.
40  We put some data in front of 'val' to verify (when debugging)
41  that all the reinterpret_casts involved when using QUEUE are correct.
42 */
44 {
45  Test_element() { *this= -1; }
46  Test_element(int i) { *this= i; }
47 
48  Test_element &operator=(int i)
49  {
50  val= i;
51  snprintf(text, array_size(text), "%4d", i);
52  return *this;
53  }
54 
55  char text[8]; // Some data.
56  int val; // The value we use for generating the key.
57 };
58 
59 
60 /*
61  The key, which is actually sorted by queue_xxx() functions.
62  We sort on the key only.
63  */
64 struct Test_key
65 {
66  Test_key() : element(NULL), key(-1) {}
67 
68  Test_element *element; // The actual data element.
69  int key; // The generated key for the data element.
70 };
71 
72 
73 /*
74  Comparison function for Test_key objects.
75  */
76 int test_key_compare(size_t *cmp_arg, Test_key **a, Test_key **b)
77 {
78  EXPECT_EQ(*cmp_arg, sizeof(int));
79 
80  int a_num= (*a)->key;
81  int b_num= (*b)->key;
82 
83  if (a_num > b_num)
84  return +1;
85  if (a_num < b_num)
86  return -1;
87  return 0;
88 }
89 
90 
91 /*
92  Generates a Test_key for a given Test_element.
93  */
94 void test_keymaker(Sort_param *sp, Test_key *key, Test_element *element)
95 {
96  key->element= element;
97  key->key= element->val;
98 }
99 
100 
101 /*
102  A struct to wrap the actual keys, and an array of pointers to the keys.
103  */
104 template<int sz, typename Key_type>
106 {
107  Key_container()
108  {
109  for (int ix= 0; ix <= sz; ++ix)
110  key_ptrs[ix]= &key_data[ix];
111  }
112 
113  Key_type *key_ptrs[sz+1];
114  Key_type key_data[sz+1];
115 };
116 
117 
118 class BoundedQueueTest : public ::testing::Test
119 {
120 protected:
121  BoundedQueueTest() : m_key_size(sizeof(int))
122  {
123  }
124 
125  virtual void SetUp()
126  {
127  int ix;
128  for (ix=0; ix < array_size(m_test_data); ++ix)
129  m_test_data[ix]= ix;
130  std::random_shuffle(&m_test_data[0], &m_test_data[array_size(m_test_data)]);
131  }
132 
133  void insert_test_data()
134  {
135  for (int ix= 0; ix < array_size(m_test_data); ++ix)
136  m_queue.push(&m_test_data[ix]);
137  }
138 
139  // Key pointers and data, used by the queue_xxx() functions.
140  Key_container<num_elements / 2, Test_key> m_keys;
141 
142  // Some random intput data, to be sorted.
143  Test_element m_test_data[num_elements];
144 
145  size_t m_key_size;
147 private:
148  GTEST_DISALLOW_COPY_AND_ASSIGN_(BoundedQueueTest);
149 };
150 
151 
152 // Google Test recommends DeathTest suffix for classes used in death tests.
154 
155 #if !defined(DBUG_OFF)
156 /*
157  Verifies that we DBUG_ASSERT if trying to push to an un-initialized queue.
158  */
159 TEST_F(BoundedQueueDeathTest, DieIfNotInitialized)
160 {
161  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
162  Test_element foo= 1;
163  EXPECT_DEATH_IF_SUPPORTED(m_queue.push(&foo),
164  ".*Assertion .*is_initialized.*");
165 }
166 
167 /*
168  Verifies that popping an empty queue hits a DBUG_ASSERT.
169  */
170 TEST_F(BoundedQueueDeathTest, DieIfPoppingEmptyQueue)
171 {
172  EXPECT_EQ(0, m_queue.init(0, true, test_key_compare,
173  m_key_size,
174  &test_keymaker, NULL, m_keys.key_ptrs));
175  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
176  EXPECT_DEATH_IF_SUPPORTED(m_queue.pop(),
177  ".*Assertion .*elements > 0.*");
178 }
179 #endif // !defined(DBUG_OFF)
180 
181 
182 /*
183  Verifies that construct, initialize, destroy works.
184  */
185 TEST_F(BoundedQueueTest, ConstructAndDestruct)
186 {
187  EXPECT_EQ(0, m_queue.init(num_elements/2, true,
188  test_key_compare,
189  m_key_size,
190  &test_keymaker, NULL, m_keys.key_ptrs));
191 }
192 
193 
194 /*
195  Verifies that we reject too large queues.
196  */
197 TEST_F(BoundedQueueTest, TooManyElements)
198 {
199  EXPECT_EQ(1, m_queue.init(UINT_MAX, true,
200  test_key_compare,
201  m_key_size,
202  &test_keymaker, NULL, m_keys.key_ptrs));
203  EXPECT_EQ(1, m_queue.init(UINT_MAX - 1, true,
204  test_key_compare,
205  m_key_size,
206  &test_keymaker, NULL, m_keys.key_ptrs));
207 }
208 
209 
210 /*
211  Verifies that zero-size queue works.
212  */
213 TEST_F(BoundedQueueTest, ZeroSizeQueue)
214 {
215  EXPECT_EQ(0, m_queue.init(0, true, test_key_compare,
216  m_key_size,
217  &test_keymaker, NULL, m_keys.key_ptrs));
218  insert_test_data();
219  EXPECT_EQ(1U, m_queue.num_elements());
220 }
221 
222 
223 /*
224  Verifies that push and bounded size works, and that pop() gives sorted order.
225  */
226 TEST_F(BoundedQueueTest, PushAndPopKeepLargest)
227 {
228  EXPECT_EQ(0, m_queue.init(num_elements/2, false, test_key_compare,
229  m_key_size,
230  &test_keymaker, NULL, m_keys.key_ptrs));
231  insert_test_data();
232  // We expect the queue to contain [7 .. 13]
233  const int max_key_val= array_size(m_test_data) - 1;
234  while (m_queue.num_elements() > 0)
235  {
236  Test_key **top= m_queue.pop();
237  int expected_key_val= max_key_val - m_queue.num_elements();
238  int key_val= (*top)->key;
239  EXPECT_EQ(expected_key_val, key_val);
240  Test_element *element= (*top)->element;
241  EXPECT_EQ(expected_key_val, element->val);
242  }
243 }
244 
245 
246 /*
247  Verifies that push and bounded size works, and that pop() gives sorted order.
248  Note that with max_at_top == true, we will pop() in reverse order.
249  */
250 TEST_F(BoundedQueueTest, PushAndPopKeepSmallest)
251 {
252  EXPECT_EQ(0, m_queue.init(num_elements/2, true, test_key_compare,
253  m_key_size,
254  &test_keymaker, NULL, m_keys.key_ptrs));
255  insert_test_data();
256  // We expect the queue to contain [6 .. 0]
257  while (m_queue.num_elements() > 0)
258  {
259  Test_key **top= m_queue.pop();
260  int expected_key_val= m_queue.num_elements();
261  int key_val= (*top)->key;
262  EXPECT_EQ(expected_key_val, key_val);
263  Test_element *element= (*top)->element;
264  EXPECT_EQ(expected_key_val, element->val);
265  }
266 }
267 
268 
269 /*
270  Verifies that push, with bounded size, followed by sort() works.
271  */
272 TEST_F(BoundedQueueTest, InsertAndSort)
273 {
274  EXPECT_EQ(0, m_queue.init(num_elements/2, true, test_key_compare,
275  m_key_size,
276  &test_keymaker, NULL, m_keys.key_ptrs));
277  insert_test_data();
278  uchar *base= (uchar*) &m_keys.key_ptrs[0];
279  size_t size= sizeof(Test_key);
280  // We sort our keys as strings, so erase all the element pointers first.
281  for (int ii= 0; ii < array_size(m_keys.key_data); ++ii)
282  m_keys.key_data[ii].element= NULL;
283 
284  my_string_ptr_sort(base, array_size(m_keys.key_ptrs), size);
285  for (int ii= 0; ii < num_elements/2; ++ii)
286  {
287  Test_key *sorted_key= m_keys.key_ptrs[ii];
288  EXPECT_EQ(ii, sorted_key->key);
289  }
290 }
291 
292 
293 /*
294  A test of the function get_merge_many_buffs_cost_fast()
295  */
296 TEST(CostEstimationTest, MergeManyBuff)
297 {
298  ha_rows num_rows= 512;
299  ulong num_keys= 100;
300  ulong row_lenght= 100;
301  double prev_cost= 0.0;
302  while (num_rows <= MAX_FILE_SIZE/4)
303  {
304  double merge_cost=
305  get_merge_many_buffs_cost_fast(num_rows, num_keys, row_lenght);
306  EXPECT_LT(0.0, merge_cost);
307  EXPECT_LT(prev_cost, merge_cost);
308  num_rows*= 2;
309  prev_cost= merge_cost;
310  }
311 }
312 
313 
314 /*
315  Comparison function for integers.
316  */
317 int int_ptr_compare(size_t *cmp_arg, int **a, int **b)
318 {
319  EXPECT_EQ(*cmp_arg, sizeof(int));
320 
321  int a_num= **a;
322  int b_num= **b;
323 
324  if (a_num > b_num)
325  return +1;
326  if (a_num < b_num)
327  return -1;
328  return 0;
329 }
330 
331 
332 /*
333  Generates an integer key for a given integer element.
334  */
335 void int_keymaker(Sort_param *sp, int *to, int *from)
336 {
337  memcpy(to, from, sizeof(int));
338 }
339 
340 
341 /*
342  Some basic performance testing, to compute the overhead of Bounded_queue.
343  Run the with 'bounded_queue-t --disable-tap-output' to see the
344  millisecond output from Google Test.
345  */
346 const int num_rows= 10000;
347 const int row_limit= 100;
348 const int num_iterations= 10;
349 
350 class PerfTestSmall : public ::testing::Test
351 {
352 public:
353  /*
354  The extra overhead of malloc/free should be part of the measurement,
355  so we do not define the key container as a member here.
356  */
358  enum { limit= row_limit };
359 };
360 
361 class PerfTestLarge : public ::testing::Test
362 {
363 public:
364  /*
365  The extra overhead of malloc/free should be part of the measurement,
366  so we do not define the key container as a member here.
367  */
369  enum { limit= num_rows };
370 };
371 
372 
373 template <int limit>
374 void insert_and_sort()
375 {
376  typedef Key_container<limit, int> Container;
377  for (int it= 0; it < num_iterations; ++it)
378  {
379  Container *keys= new Container;
380  srand(0);
382  EXPECT_EQ(0, queue.init(limit, true, int_ptr_compare,
383  sizeof(int), &int_keymaker, NULL, keys->key_ptrs));
384  for (int ix= 0; ix < num_rows; ++ix)
385  {
386  int data= rand();
387  queue.push(&data);
388  }
389  my_string_ptr_sort((uchar*) &keys->key_ptrs[0],
390  queue.num_elements(), sizeof(int));
391  delete keys;
392  }
393 }
394 
395 
396 /*
397  Test with Bounded_queue size == <limit>.
398  */
399 TEST_F(PerfTestSmall, InsertAndSort)
400 {
401  insert_and_sort<limit>();
402 }
403 
404 
405 /*
406  Test with Bounded_queue size == <number of rows>
407  */
408 TEST_F(PerfTestLarge, InsertAndSort)
409 {
410  insert_and_sort<limit>();
411 }
412 
413 
414 /*
415  Test without bounded queue, i.e. insert keys into array, and sort it.
416  */
417 TEST_F(PerfTestLarge, WithoutQueue)
418 {
419  for (int it= 0; it < num_iterations; ++it)
420  {
421  Container *keys= new Container;
422  srand(0);
423  for (int ix= 0; ix < limit; ++ix)
424  {
425  int data= rand();
426  keys->key_data[ix]= data;
427  }
428  my_string_ptr_sort((uchar*) &keys->key_ptrs[0], limit, sizeof(int));
429  delete keys;
430  }
431 }
432 
433 
434 /*
435  Computes the overhead of setting up sort arrays, and rand() calls.
436  */
437 TEST_F(PerfTestLarge, NoSorting)
438 {
439  for (int it= 0; it < num_iterations; ++it)
440  {
441  Container *keys= new Container;
442  srand(0);
443  for (int ix= 0; ix < limit; ++ix)
444  {
445  int data= rand();
446  keys->key_data[ix]= data;
447  }
448  delete keys;
449  }
450 }
451 
452 } // namespace