MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
filesort_utils.cc
1 /* Copyright (c) 2010, 2013, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include "filesort_utils.h"
17 #include "sql_const.h"
18 #include "sql_sort.h"
19 #include "table.h"
20 
21 #include <algorithm>
22 #include <functional>
23 #include <vector>
24 
25 namespace {
29 double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
30 {
31  return
32  2.0 * ((double) num_elements * elem_size) / IO_SIZE
33  + num_elements * log((double) num_buffers) * ROWID_COMPARE_COST / M_LN2;
34 }
35 }
36 
44 double get_merge_many_buffs_cost_fast(ha_rows num_rows,
45  ha_rows num_keys_per_buffer,
46  uint elem_size)
47 {
48  ha_rows num_buffers= num_rows / num_keys_per_buffer;
49  ha_rows last_n_elems= num_rows % num_keys_per_buffer;
50  double total_cost;
51 
52  // Calculate CPU cost of sorting buffers.
53  total_cost=
54  ( num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) +
55  last_n_elems * log(1.0 + last_n_elems) ) * ROWID_COMPARE_COST;
56 
57  // Simulate behavior of merge_many_buff().
58  while (num_buffers >= MERGEBUFF2)
59  {
60  // Calculate # of calls to merge_buffers().
61  const ha_rows loop_limit= num_buffers - MERGEBUFF*3/2;
62  const ha_rows num_merge_calls= 1 + loop_limit/MERGEBUFF;
63  const ha_rows num_remaining_buffs=
64  num_buffers - num_merge_calls * MERGEBUFF;
65 
66  // Cost of merge sort 'num_merge_calls'.
67  total_cost+=
68  num_merge_calls *
69  get_merge_cost(num_keys_per_buffer * MERGEBUFF, MERGEBUFF, elem_size);
70 
71  // # of records in remaining buffers.
72  last_n_elems+= num_remaining_buffs * num_keys_per_buffer;
73 
74  // Cost of merge sort of remaining buffers.
75  total_cost+=
76  get_merge_cost(last_n_elems, 1 + num_remaining_buffs, elem_size);
77 
78  num_buffers= num_merge_calls;
79  num_keys_per_buffer*= MERGEBUFF;
80  }
81 
82  // Simulate final merge_buff call.
83  last_n_elems+= num_keys_per_buffer * num_buffers;
84  total_cost+= get_merge_cost(last_n_elems, 1 + num_buffers, elem_size);
85  return total_cost;
86 }
87 
88 
89 uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
90 {
91  DBUG_ENTER("alloc_sort_buffer");
92 
93  DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
94  DBUG_SET("+d,simulate_out_of_memory"););
95 
96  if (m_idx_array.is_null())
97  {
98  uchar **sort_keys=
99  (uchar**) my_malloc(num_records * (record_length + sizeof(uchar*)),
100  MYF(0));
101  m_idx_array= Idx_array(sort_keys, num_records);
102  m_record_length= record_length;
103  uchar **start_of_data= m_idx_array.array() + m_idx_array.size();
104  m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
105  }
106  else
107  {
108  DBUG_ASSERT(num_records == m_idx_array.size());
109  DBUG_ASSERT(record_length == m_record_length);
110  }
111  DBUG_RETURN(m_idx_array.array());
112 }
113 
114 
116 {
117  my_free(m_idx_array.array());
118  m_idx_array= Idx_array();
119  m_record_length= 0;
120  m_start_of_data= NULL;
121 }
122 
123 
124 namespace {
125 
126 /*
127  An inline function which does memcmp().
128  This one turns out to be pretty fast on all platforms, except sparc.
129  See the accompanying unit tests, which measure various implementations.
130  */
131 inline bool my_mem_compare(const uchar *s1, const uchar *s2, size_t len)
132 {
133  DBUG_ASSERT(len > 0);
134  DBUG_ASSERT(s1 != NULL);
135  DBUG_ASSERT(s2 != NULL);
136  do {
137  if (*s1++ != *s2++)
138  return *--s1 < *--s2;
139  } while (--len != 0);
140  return false;
141 }
142 
143 
144 class Mem_compare :
145  public std::binary_function<const uchar*, const uchar*, bool>
146 {
147 public:
148  Mem_compare(size_t n) : m_size(n) {}
149  bool operator()(const uchar *s1, const uchar *s2) const
150  {
151 #ifdef __sun
152  // Usually faster on SUN, see comment for native_compare()
153  return memcmp(s1, s2, m_size) < 0;
154 #else
155  return my_mem_compare(s1, s2, m_size);
156 #endif
157  }
158 private:
159  size_t m_size;
160 };
161 
162 template <typename type>
163 size_t try_reserve(std::pair<type*, ptrdiff_t> *buf, ptrdiff_t size)
164 {
165  *buf= std::get_temporary_buffer<type>(size);
166  if (buf->second != size)
167  {
168  std::return_temporary_buffer(buf->first);
169  return 0;
170  }
171  return buf->second;
172 }
173 
174 } // namespace
175 
176 void Filesort_buffer::sort_buffer(const Sort_param *param, uint count)
177 {
178  if (count <= 1)
179  return;
180  if (param->sort_length == 0)
181  return;
182 
183  uchar **keys= get_sort_keys();
184  std::pair<uchar**, ptrdiff_t> buffer;
185  if (radixsort_is_appliccable(count, param->sort_length) &&
186  try_reserve(&buffer, count))
187  {
188  radixsort_for_str_ptr(keys, count, param->sort_length, buffer.first);
189  std::return_temporary_buffer(buffer.first);
190  return;
191  }
192  /*
193  std::stable_sort has some extra overhead in allocating the temp buffer,
194  which takes some time. The cutover point where it starts to get faster
195  than quicksort seems to be somewhere around 10 to 40 records.
196  So we're a bit conservative, and stay with quicksort up to 100 records.
197  */
198  if (count < 100)
199  {
200  size_t size= param->sort_length;
201  my_qsort2(keys, count, sizeof(uchar*), get_ptr_compare(size), &size);
202  return;
203  }
204  std::stable_sort(keys, keys + count, Mem_compare(param->sort_length));
205 }