OmniSciDB  0264ff685a
QueryMemoryDescriptor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018 MapD Technologies, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "QueryMemoryDescriptor.h"
18 
19 #include "../Execute.h"
20 #include "../ExpressionRewrite.h"
21 #include "../GroupByAndAggregate.h"
22 #include "../StreamingTopN.h"
23 #include "../UsedColumnsVisitor.h"
24 #include "ColSlotContext.h"
25 
27 extern bool g_enable_columnar_output;
28 
29 namespace {
30 
31 bool is_int_and_no_bigger_than(const SQLTypeInfo& ti, const size_t byte_width) {
32  if (!ti.is_integer()) {
33  return false;
34  }
35  return get_bit_width(ti) <= (byte_width * 8);
36 }
37 
38 std::vector<int64_t> target_expr_group_by_indices(
39  const std::list<std::shared_ptr<Analyzer::Expr>>& groupby_exprs,
40  const std::vector<Analyzer::Expr*>& target_exprs) {
41  std::vector<int64_t> indices(target_exprs.size(), -1);
42  for (size_t target_idx = 0; target_idx < target_exprs.size(); ++target_idx) {
43  const auto target_expr = target_exprs[target_idx];
44  if (dynamic_cast<const Analyzer::AggExpr*>(target_expr)) {
45  continue;
46  }
47  const auto var_expr = dynamic_cast<const Analyzer::Var*>(target_expr);
48  if (var_expr && var_expr->get_which_row() == Analyzer::Var::kGROUPBY) {
49  indices[target_idx] = var_expr->get_varno() - 1;
50  continue;
51  }
52  }
53  return indices;
54 }
55 
56 std::vector<int64_t> target_expr_proj_indices(const RelAlgExecutionUnit& ra_exe_unit,
58  if (ra_exe_unit.input_descs.size() > 1 ||
59  !ra_exe_unit.sort_info.order_entries.empty()) {
60  return {};
61  }
62  std::vector<int64_t> target_indices(ra_exe_unit.target_exprs.size(), -1);
63  UsedColumnsVisitor columns_visitor;
64  std::unordered_set<int> used_columns;
65  for (const auto& simple_qual : ra_exe_unit.simple_quals) {
66  const auto crt_used_columns = columns_visitor.visit(simple_qual.get());
67  used_columns.insert(crt_used_columns.begin(), crt_used_columns.end());
68  }
69  for (const auto& qual : ra_exe_unit.quals) {
70  const auto crt_used_columns = columns_visitor.visit(qual.get());
71  used_columns.insert(crt_used_columns.begin(), crt_used_columns.end());
72  }
73  for (const auto& target : ra_exe_unit.target_exprs) {
74  const auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(target);
75  if (col_var) {
76  const auto cd = get_column_descriptor_maybe(
77  col_var->get_column_id(), col_var->get_table_id(), cat);
78  if (!cd || !cd->isVirtualCol) {
79  continue;
80  }
81  }
82  const auto crt_used_columns = columns_visitor.visit(target);
83  used_columns.insert(crt_used_columns.begin(), crt_used_columns.end());
84  }
85  for (size_t target_idx = 0; target_idx < ra_exe_unit.target_exprs.size();
86  ++target_idx) {
87  const auto target_expr = ra_exe_unit.target_exprs[target_idx];
88  CHECK(target_expr);
89  const auto& ti = target_expr->get_type_info();
90  // TODO: add proper lazy fetch for varlen types in result set
91  if (ti.is_varlen()) {
92  continue;
93  }
94  const auto col_var = dynamic_cast<const Analyzer::ColumnVar*>(target_expr);
95  if (!col_var) {
96  continue;
97  }
98  if (!ti.is_varlen() &&
99  used_columns.find(col_var->get_column_id()) == used_columns.end()) {
100  // setting target index to be zero so that later it can be decoded properly (in lazy
101  // fetch, the zeroth target index indicates the corresponding rowid column for the
102  // projected entry)
103  target_indices[target_idx] = 0;
104  }
105  }
106  return target_indices;
107 }
108 
110  const size_t group_col_width) {
111  if (range.getType() == ExpressionRangeType::Invalid) {
112  return sizeof(int64_t);
113  }
114  switch (range.getType()) {
116  if (group_col_width == sizeof(int64_t) && range.hasNulls()) {
117  return sizeof(int64_t);
118  }
119  return range.getIntMax() < EMPTY_KEY_32 - 1 ? sizeof(int32_t) : sizeof(int64_t);
122  return sizeof(int64_t); // No compaction for floating point yet.
123  default:
124  UNREACHABLE();
125  }
126  return sizeof(int64_t);
127 }
128 
129 // TODO(miyu): make sure following setting of compact width is correct in all cases.
131  const std::vector<InputTableInfo>& query_infos,
132  const Executor* executor) {
133  int8_t compact_width{4};
134  for (const auto& groupby_expr : ra_exe_unit.groupby_exprs) {
135  const auto expr_range = getExpressionRange(groupby_expr.get(), query_infos, executor);
136  compact_width = std::max(compact_width,
138  expr_range, groupby_expr->get_type_info().get_size()));
139  }
140  return compact_width;
141 }
142 
143 bool use_streaming_top_n(const RelAlgExecutionUnit& ra_exe_unit,
144  const bool output_columnar) {
145  if (g_cluster) {
146  return false; // TODO(miyu)
147  }
148 
149  for (const auto target_expr : ra_exe_unit.target_exprs) {
150  if (dynamic_cast<const Analyzer::AggExpr*>(target_expr)) {
151  return false;
152  }
153  if (dynamic_cast<const Analyzer::WindowFunction*>(target_expr)) {
154  return false;
155  }
156  }
157 
158  // TODO: Allow streaming top n for columnar output
159  if (!output_columnar && ra_exe_unit.sort_info.order_entries.size() == 1 &&
160  ra_exe_unit.sort_info.limit &&
162  const auto only_order_entry = ra_exe_unit.sort_info.order_entries.front();
163  CHECK_GT(only_order_entry.tle_no, int(0));
164  CHECK_LE(static_cast<size_t>(only_order_entry.tle_no),
165  ra_exe_unit.target_exprs.size());
166  const auto order_entry_expr = ra_exe_unit.target_exprs[only_order_entry.tle_no - 1];
167  const auto n = ra_exe_unit.sort_info.offset + ra_exe_unit.sort_info.limit;
168  if ((order_entry_expr->get_type_info().is_number() ||
169  order_entry_expr->get_type_info().is_time()) &&
170  n <= 100000) { // TODO(miyu): relax?
171  return true;
172  }
173  }
174 
175  return false;
176 }
177 
178 } // namespace
179 
180 std::unique_ptr<QueryMemoryDescriptor> QueryMemoryDescriptor::init(
181  const Executor* executor,
182  const RelAlgExecutionUnit& ra_exe_unit,
183  const std::vector<InputTableInfo>& query_infos,
184  const ColRangeInfo& col_range_info,
185  const KeylessInfo& keyless_info,
186  const bool allow_multifrag,
187  const ExecutorDeviceType device_type,
188  const int8_t crt_min_byte_width,
189  const bool sort_on_gpu_hint,
190  const size_t shard_count,
191  const size_t max_groups_buffer_entry_count,
192  RenderInfo* render_info,
193  const CountDistinctDescriptors count_distinct_descriptors,
194  const bool must_use_baseline_sort,
195  const bool output_columnar_hint,
196  const bool streaming_top_n_hint) {
197  auto group_col_widths = get_col_byte_widths(ra_exe_unit.groupby_exprs);
198  const bool is_group_by{!group_col_widths.empty()};
199 
200  auto col_slot_context = ColSlotContext(ra_exe_unit.target_exprs, {});
201 
202  const auto min_slot_size = QueryMemoryDescriptor::pick_target_compact_width(
203  ra_exe_unit, query_infos, crt_min_byte_width);
204 
205  col_slot_context.setAllSlotsPaddedSize(min_slot_size);
206  col_slot_context.validate();
207 
208  if (!is_group_by) {
209  CHECK(!must_use_baseline_sort);
210 
211  return std::make_unique<QueryMemoryDescriptor>(
212  executor,
213  ra_exe_unit,
214  query_infos,
215  allow_multifrag,
216  false,
217  false,
218  -1,
219  ColRangeInfo{ra_exe_unit.estimator ? QueryDescriptionType::Estimator
221  0,
222  0,
223  0,
224  false},
225  col_slot_context,
226  std::vector<int8_t>{},
227  /*group_col_compact_width=*/0,
228  std::vector<int64_t>{},
229  /*entry_count=*/1,
230  count_distinct_descriptors,
231  false,
232  output_columnar_hint,
233  render_info && render_info->isPotentialInSituRender(),
234  must_use_baseline_sort,
235  /*use_streaming_top_n=*/false);
236  }
237 
238  size_t entry_count = 1;
239  auto actual_col_range_info = col_range_info;
240  bool interleaved_bins_on_gpu = false;
241  bool keyless_hash = false;
242  bool streaming_top_n = false;
243  int8_t group_col_compact_width = 0;
244  int32_t idx_target_as_key = -1;
245  auto output_columnar = output_columnar_hint;
246  std::vector<int64_t> target_groupby_indices;
247 
248  switch (col_range_info.hash_type_) {
250  if (render_info) {
251  render_info->setInSituDataIfUnset(false);
252  }
253  // keyless hash: whether or not group columns are stored at the beginning of the
254  // output buffer
255  keyless_hash =
256  (!sort_on_gpu_hint ||
258  col_range_info.max, col_range_info.min, col_range_info.bucket)) &&
259  !col_range_info.bucket && !must_use_baseline_sort && keyless_info.keyless;
260 
261  // if keyless, then this target index indicates wheter an entry is empty or not
262  // (acts as a key)
263  idx_target_as_key = keyless_info.target_index;
264 
265  if (group_col_widths.size() > 1) {
266  // col range info max contains the expected cardinality of the output
267  entry_count = static_cast<size_t>(actual_col_range_info.max);
268  actual_col_range_info.bucket = 0;
269  } else {
270  // single column perfect hash
271  entry_count = std::max(
272  GroupByAndAggregate::getBucketedCardinality(col_range_info), int64_t(1));
273  const size_t interleaved_max_threshold{512};
274 
275  if (must_use_baseline_sort) {
276  target_groupby_indices = target_expr_group_by_indices(ra_exe_unit.groupby_exprs,
277  ra_exe_unit.target_exprs);
278  col_slot_context =
279  ColSlotContext(ra_exe_unit.target_exprs, target_groupby_indices);
280  }
281 
282  bool has_varlen_sample_agg = false;
283  for (const auto& target_expr : ra_exe_unit.target_exprs) {
284  if (target_expr->get_contains_agg()) {
285  const auto agg_expr = dynamic_cast<Analyzer::AggExpr*>(target_expr);
286  CHECK(agg_expr);
287  if (agg_expr->get_aggtype() == kSAMPLE &&
288  agg_expr->get_type_info().is_varlen()) {
289  has_varlen_sample_agg = true;
290  break;
291  }
292  }
293  }
294 
295  interleaved_bins_on_gpu = keyless_hash && !has_varlen_sample_agg &&
296  (entry_count <= interleaved_max_threshold) &&
297  (device_type == ExecutorDeviceType::GPU) &&
299  count_distinct_descriptors) &&
300  !output_columnar;
301  }
302  break;
303  }
305  if (render_info) {
306  render_info->setInSituDataIfUnset(false);
307  }
308  entry_count = shard_count
309  ? (max_groups_buffer_entry_count + shard_count - 1) / shard_count
310  : max_groups_buffer_entry_count;
311  target_groupby_indices = target_expr_group_by_indices(ra_exe_unit.groupby_exprs,
312  ra_exe_unit.target_exprs);
313  col_slot_context = ColSlotContext(ra_exe_unit.target_exprs, target_groupby_indices);
314 
315  group_col_compact_width =
316  output_columnar ? 8
317  : pick_baseline_key_width(ra_exe_unit, query_infos, executor);
318 
319  actual_col_range_info =
321  break;
322  }
324  CHECK(!must_use_baseline_sort);
325 
326  if (streaming_top_n_hint && use_streaming_top_n(ra_exe_unit, output_columnar)) {
327  streaming_top_n = true;
328  entry_count = ra_exe_unit.sort_info.offset + ra_exe_unit.sort_info.limit;
329  } else {
330  if (ra_exe_unit.use_bump_allocator) {
331  output_columnar = false;
332  entry_count = 0;
333  } else {
334  entry_count = ra_exe_unit.scan_limit
335  ? static_cast<size_t>(ra_exe_unit.scan_limit)
336  : max_groups_buffer_entry_count;
337  }
338  }
339 
340  const auto catalog = executor->getCatalog();
341  CHECK(catalog);
342  target_groupby_indices = executor->plan_state_->allow_lazy_fetch_
343  ? target_expr_proj_indices(ra_exe_unit, *catalog)
344  : std::vector<int64_t>{};
345 
346  col_slot_context = ColSlotContext(ra_exe_unit.target_exprs, target_groupby_indices);
347  break;
348  }
349  default:
350  UNREACHABLE() << "Unknown query type";
351  }
352 
353  return std::make_unique<QueryMemoryDescriptor>(
354  executor,
355  ra_exe_unit,
356  query_infos,
357  allow_multifrag,
358  keyless_hash,
359  interleaved_bins_on_gpu,
360  idx_target_as_key,
361  actual_col_range_info,
362  col_slot_context,
363  group_col_widths,
364  group_col_compact_width,
365  target_groupby_indices,
366  entry_count,
367  count_distinct_descriptors,
368  sort_on_gpu_hint,
369  output_columnar,
370  render_info && render_info->isPotentialInSituRender(),
371  must_use_baseline_sort,
372  streaming_top_n);
373 }
374 
376  const Executor* executor,
377  const RelAlgExecutionUnit& ra_exe_unit,
378  const std::vector<InputTableInfo>& query_infos,
379  const bool allow_multifrag,
380  const bool keyless_hash,
381  const bool interleaved_bins_on_gpu,
382  const int32_t idx_target_as_key,
383  const ColRangeInfo& col_range_info,
384  const ColSlotContext& col_slot_context,
385  const std::vector<int8_t>& group_col_widths,
386  const int8_t group_col_compact_width,
387  const std::vector<int64_t>& target_groupby_indices,
388  const size_t entry_count,
389  const CountDistinctDescriptors count_distinct_descriptors,
390  const bool sort_on_gpu_hint,
391  const bool output_columnar_hint,
392  const bool render_output,
393  const bool must_use_baseline_sort,
394  const bool use_streaming_top_n)
395  : executor_(executor)
396  , allow_multifrag_(allow_multifrag)
397  , query_desc_type_(col_range_info.hash_type_)
398  , keyless_hash_(keyless_hash)
399  , interleaved_bins_on_gpu_(interleaved_bins_on_gpu)
400  , idx_target_as_key_(idx_target_as_key)
401  , group_col_widths_(group_col_widths)
402  , group_col_compact_width_(group_col_compact_width)
403  , target_groupby_indices_(target_groupby_indices)
404  , entry_count_(entry_count)
405  , min_val_(col_range_info.min)
406  , max_val_(col_range_info.max)
407  , bucket_(col_range_info.bucket)
408  , has_nulls_(col_range_info.has_nulls)
409  , count_distinct_descriptors_(count_distinct_descriptors)
410  , output_columnar_(false)
411  , render_output_(render_output)
412  , must_use_baseline_sort_(must_use_baseline_sort)
413  , is_table_function_(false)
414  , use_streaming_top_n_(use_streaming_top_n)
415  , force_4byte_float_(false)
416  , col_slot_context_(col_slot_context) {
419 
420  sort_on_gpu_ = sort_on_gpu_hint && canOutputColumnar() && !keyless_hash_;
421 
422  if (sort_on_gpu_) {
423  CHECK(!ra_exe_unit.use_bump_allocator);
424  output_columnar_ = true;
425  } else {
426  switch (query_desc_type_) {
428  output_columnar_ = output_columnar_hint;
429  break;
434  break;
436  output_columnar_ = output_columnar_hint;
437  break;
442  break;
443  default:
444  output_columnar_ = false;
445  break;
446  }
447  }
448 
450  // TODO(adb): Ensure fixed size buffer allocations are correct with all logical column
451  // sizes
452  CHECK(!ra_exe_unit.use_bump_allocator);
455  }
456 
457 #ifdef HAVE_CUDA
458  // Check Streaming Top N heap usage, bail if > max slab size, CUDA ONLY
459  if (use_streaming_top_n_ && executor->catalog_->getDataMgr().gpusPresent()) {
460  const auto thread_count = executor->blockSize() * executor->gridSize();
461  const auto total_buff_size =
463  if (total_buff_size > executor_->maxGpuSlabSize()) {
464  throw StreamingTopNOOM(total_buff_size);
465  }
466  }
467 #endif
468 }
469 
471  : executor_(nullptr)
472  , allow_multifrag_(false)
474  , keyless_hash_(false)
475  , interleaved_bins_on_gpu_(false)
476  , idx_target_as_key_(0)
478  , entry_count_(0)
479  , min_val_(0)
480  , max_val_(0)
481  , bucket_(0)
482  , has_nulls_(false)
483  , sort_on_gpu_(false)
484  , output_columnar_(false)
485  , render_output_(false)
486  , must_use_baseline_sort_(false)
487  , is_table_function_(false)
488  , use_streaming_top_n_(false)
489  , force_4byte_float_(false) {}
490 
492  const size_t entry_count,
493  const QueryDescriptionType query_desc_type,
494  const bool is_table_function)
495  : executor_(executor)
496  , allow_multifrag_(false)
497  , query_desc_type_(query_desc_type)
498  , keyless_hash_(false)
499  , interleaved_bins_on_gpu_(false)
500  , idx_target_as_key_(0)
502  , entry_count_(entry_count)
503  , min_val_(0)
504  , max_val_(0)
505  , bucket_(0)
506  , has_nulls_(false)
507  , sort_on_gpu_(false)
508  , output_columnar_(false)
509  , render_output_(false)
510  , must_use_baseline_sort_(false)
511  , is_table_function_(is_table_function)
512  , use_streaming_top_n_(false)
513  , force_4byte_float_(false) {}
514 
516  const int64_t min_val,
517  const int64_t max_val,
518  const bool has_nulls,
519  const std::vector<int8_t>& group_col_widths)
520  : executor_(nullptr)
521  , allow_multifrag_(false)
522  , query_desc_type_(query_desc_type)
523  , keyless_hash_(false)
524  , interleaved_bins_on_gpu_(false)
525  , idx_target_as_key_(0)
526  , group_col_widths_(group_col_widths)
528  , entry_count_(0)
529  , min_val_(min_val)
530  , max_val_(max_val)
531  , bucket_(0)
532  , has_nulls_(false)
533  , sort_on_gpu_(false)
534  , output_columnar_(false)
535  , render_output_(false)
536  , must_use_baseline_sort_(false)
537  , is_table_function_(false)
538  , use_streaming_top_n_(false)
539  , force_4byte_float_(false) {}
540 
542  // Note that this method does not check ptr reference members (e.g. executor_) or
543  // entry_count_
544  if (query_desc_type_ != other.query_desc_type_) {
545  return false;
546  }
547  if (keyless_hash_ != other.keyless_hash_) {
548  return false;
549  }
551  return false;
552  }
553  if (idx_target_as_key_ != other.idx_target_as_key_) {
554  return false;
555  }
556  if (force_4byte_float_ != other.force_4byte_float_) {
557  return false;
558  }
559  if (group_col_widths_ != other.group_col_widths_) {
560  return false;
561  }
563  return false;
564  }
566  return false;
567  }
568  if (min_val_ != other.min_val_) {
569  return false;
570  }
571  if (max_val_ != other.max_val_) {
572  return false;
573  }
574  if (bucket_ != other.bucket_) {
575  return false;
576  }
577  if (has_nulls_ != other.has_nulls_) {
578  return false;
579  }
581  return false;
582  } else {
583  // Count distinct descriptors can legitimately differ in device only.
584  for (size_t i = 0; i < count_distinct_descriptors_.size(); ++i) {
585  auto ref_count_distinct_desc = other.count_distinct_descriptors_[i];
586  auto count_distinct_desc = count_distinct_descriptors_[i];
587  count_distinct_desc.device_type = ref_count_distinct_desc.device_type;
588  if (ref_count_distinct_desc != count_distinct_desc) {
589  return false;
590  }
591  }
592  }
593  if (sort_on_gpu_ != other.sort_on_gpu_) {
594  return false;
595  }
596  if (output_columnar_ != other.output_columnar_) {
597  return false;
598  }
599  if (col_slot_context_ != other.col_slot_context_) {
600  return false;
601  }
602  return true;
603 }
604 
605 std::unique_ptr<QueryExecutionContext> QueryMemoryDescriptor::getQueryExecutionContext(
606  const RelAlgExecutionUnit& ra_exe_unit,
607  const Executor* executor,
608  const ExecutorDeviceType device_type,
609  const ExecutorDispatchMode dispatch_mode,
610  const int device_id,
611  const int64_t num_rows,
612  const std::vector<std::vector<const int8_t*>>& col_buffers,
613  const std::vector<std::vector<uint64_t>>& frag_offsets,
614  std::shared_ptr<RowSetMemoryOwner> row_set_mem_owner,
615  const bool output_columnar,
616  const bool sort_on_gpu,
617  RenderInfo* render_info) const {
618  auto timer = DEBUG_TIMER(__func__);
619  if (frag_offsets.empty()) {
620  return nullptr;
621  }
622  return std::unique_ptr<QueryExecutionContext>(
623  new QueryExecutionContext(ra_exe_unit,
624  *this,
625  executor,
626  device_type,
627  dispatch_mode,
628  device_id,
629  num_rows,
630  col_buffers,
631  frag_offsets,
632  row_set_mem_owner,
633  output_columnar,
634  sort_on_gpu,
635  render_info));
636 }
637 
639  const RelAlgExecutionUnit& ra_exe_unit,
640  const std::vector<InputTableInfo>& query_infos,
641  const int8_t crt_min_byte_width) {
642  if (g_bigint_count) {
643  return sizeof(int64_t);
644  }
645  int8_t compact_width{0};
646  auto col_it = ra_exe_unit.input_col_descs.begin();
647  int unnest_array_col_id{std::numeric_limits<int>::min()};
648  for (const auto& groupby_expr : ra_exe_unit.groupby_exprs) {
649  const auto uoper = dynamic_cast<Analyzer::UOper*>(groupby_expr.get());
650  if (uoper && uoper->get_optype() == kUNNEST) {
651  const auto& arg_ti = uoper->get_operand()->get_type_info();
652  CHECK(arg_ti.is_array());
653  const auto& elem_ti = arg_ti.get_elem_type();
654  if (elem_ti.is_string() && elem_ti.get_compression() == kENCODING_DICT) {
655  unnest_array_col_id = (*col_it)->getColId();
656  } else {
657  compact_width = crt_min_byte_width;
658  break;
659  }
660  }
661  ++col_it;
662  }
663  if (!compact_width &&
664  (ra_exe_unit.groupby_exprs.size() != 1 || !ra_exe_unit.groupby_exprs.front())) {
665  compact_width = crt_min_byte_width;
666  }
667  if (!compact_width) {
668  col_it = ra_exe_unit.input_col_descs.begin();
669  std::advance(col_it, ra_exe_unit.groupby_exprs.size());
670  for (const auto target : ra_exe_unit.target_exprs) {
671  const auto& ti = target->get_type_info();
672  const auto agg = dynamic_cast<const Analyzer::AggExpr*>(target);
673  if (agg && agg->get_arg()) {
674  compact_width = crt_min_byte_width;
675  break;
676  }
677 
678  if (agg) {
679  CHECK_EQ(kCOUNT, agg->get_aggtype());
680  CHECK(!agg->get_is_distinct());
681  ++col_it;
682  continue;
683  }
684 
685  if (is_int_and_no_bigger_than(ti, 4) ||
686  (ti.is_string() && ti.get_compression() == kENCODING_DICT)) {
687  ++col_it;
688  continue;
689  }
690 
691  const auto uoper = dynamic_cast<Analyzer::UOper*>(target);
692  if (uoper && uoper->get_optype() == kUNNEST &&
693  (*col_it)->getColId() == unnest_array_col_id) {
694  const auto arg_ti = uoper->get_operand()->get_type_info();
695  CHECK(arg_ti.is_array());
696  const auto& elem_ti = arg_ti.get_elem_type();
697  if (elem_ti.is_string() && elem_ti.get_compression() == kENCODING_DICT) {
698  ++col_it;
699  continue;
700  }
701  }
702 
703  compact_width = crt_min_byte_width;
704  break;
705  }
706  }
707  if (!compact_width) {
708  size_t total_tuples{0};
709  for (const auto& qi : query_infos) {
710  total_tuples += qi.info.getNumTuples();
711  }
712  return total_tuples <= static_cast<size_t>(std::numeric_limits<uint32_t>::max()) ||
713  unnest_array_col_id != std::numeric_limits<int>::min()
714  ? 4
715  : crt_min_byte_width;
716  } else {
717  // TODO(miyu): relax this condition to allow more cases just w/o padding
718  for (auto wid : get_col_byte_widths(ra_exe_unit.target_exprs)) {
719  compact_width = std::max(compact_width, wid);
720  }
721  return compact_width;
722  }
723 }
724 
727 }
728 
731  size_t total_bytes{0};
732  if (keyless_hash_) {
733  // ignore, there's no group column in the output buffer
735  } else {
736  total_bytes += group_col_widths_.size() * getEffectiveKeyWidth();
737  total_bytes = align_to_int64(total_bytes);
738  }
739  total_bytes += getColsSize();
740  return align_to_int64(total_bytes);
741 }
742 
744  return (interleaved_bins_on_gpu_ ? executor_->warpSize() : 1);
745 }
746 
749 }
750 
759 }
760 
766  const size_t num_entries_per_column) const {
767  return col_slot_context_.getTotalBytesOfColumnarBuffers(num_entries_per_column);
768 }
769 
780  const size_t projection_count) const {
781  constexpr size_t row_index_width = sizeof(int64_t);
782  return getTotalBytesOfColumnarBuffers(projection_count) +
783  row_index_width * projection_count;
784 }
785 
786 size_t QueryMemoryDescriptor::getColOnlyOffInBytes(const size_t col_idx) const {
787  return col_slot_context_.getColOnlyOffInBytes(col_idx);
788 }
789 
790 /*
791  * Returns the memory offset in bytes for a specific agg column in the output
792  * memory buffer. Depending on the query type, there may be some extra portion
793  * of memory prepended at the beginning of the buffer. A brief description of
794  * the memory layout is as follows:
795  * 1. projections: index column (64bit) + all target columns
796  * 2. group by: all group columns (64-bit each) + all agg columns
797  * 2a. if keyless, there is no prepending group column stored at the beginning
798  */
799 size_t QueryMemoryDescriptor::getColOffInBytes(const size_t col_idx) const {
800  const auto warp_count = getWarpCount();
801  if (output_columnar_) {
802  CHECK_EQ(size_t(1), warp_count);
803  size_t offset{0};
804  if (!keyless_hash_) {
806  }
807  for (size_t index = 0; index < col_idx; ++index) {
809  }
810  return offset;
811  }
812 
813  size_t offset{0};
814  if (keyless_hash_) {
815  // ignore, there's no group column in the output buffer
817  } else {
818  offset += group_col_widths_.size() * getEffectiveKeyWidth();
819  offset = align_to_int64(offset);
820  }
821  offset += getColOnlyOffInBytes(col_idx);
822  return offset;
823 }
824 
825 /*
826  * Returns the memory offset for a particular group column in the prepended group
827  * columns portion of the memory.
828  */
830  const size_t group_idx) const {
832  CHECK(group_idx < getGroupbyColCount());
833  size_t offset{0};
834  for (size_t col_idx = 0; col_idx < group_idx; col_idx++) {
835  // TODO(Saman): relax that int64_bit part immediately
836  offset += align_to_int64(
837  std::max(groupColWidth(col_idx), static_cast<int8_t>(sizeof(int64_t))) *
838  getEntryCount());
839  }
840  return offset;
841 }
842 
843 /*
844  * Returns total amount of memory prepended at the beginning of the output memory
845  * buffer.
846  */
849  size_t buffer_size{0};
850  for (size_t group_idx = 0; group_idx < getGroupbyColCount(); group_idx++) {
851  buffer_size += align_to_int64(
852  std::max(groupColWidth(group_idx), static_cast<int8_t>(sizeof(int64_t))) *
853  getEntryCount());
854  }
855  return buffer_size;
856 }
857 
858 size_t QueryMemoryDescriptor::getColOffInBytesInNextBin(const size_t col_idx) const {
859  auto warp_count = getWarpCount();
860  if (output_columnar_) {
861  CHECK_EQ(size_t(1), group_col_widths_.size());
862  CHECK_EQ(size_t(1), warp_count);
863  return getPaddedSlotWidthBytes(col_idx);
864  }
865 
866  return warp_count * getRowSize();
867 }
868 
869 size_t QueryMemoryDescriptor::getNextColOffInBytes(const int8_t* col_ptr,
870  const size_t bin,
871  const size_t col_idx) const {
873  size_t offset{0};
874  auto warp_count = getWarpCount();
875  const auto chosen_bytes = getPaddedSlotWidthBytes(col_idx);
876  const auto total_slot_count = getSlotCount();
877  if (col_idx + 1 == total_slot_count) {
878  if (output_columnar_) {
879  return (entry_count_ - bin) * chosen_bytes;
880  } else {
881  return static_cast<size_t>(align_to_int64(col_ptr + chosen_bytes) - col_ptr);
882  }
883  }
884 
885  const auto next_chosen_bytes = getPaddedSlotWidthBytes(col_idx + 1);
886  if (output_columnar_) {
887  CHECK_EQ(size_t(1), group_col_widths_.size());
888  CHECK_EQ(size_t(1), warp_count);
889 
890  offset = align_to_int64(entry_count_ * chosen_bytes);
891 
892  offset += bin * (next_chosen_bytes - chosen_bytes);
893  return offset;
894  }
895 
896  if (next_chosen_bytes == sizeof(int64_t)) {
897  return static_cast<size_t>(align_to_int64(col_ptr + chosen_bytes) - col_ptr);
898  } else {
899  return chosen_bytes;
900  }
901 }
902 
904  const RelAlgExecutionUnit& ra_exe_unit,
905  const unsigned thread_count,
906  const ExecutorDeviceType device_type) const {
907  if (use_streaming_top_n_) {
908  const size_t n = ra_exe_unit.sort_info.offset + ra_exe_unit.sort_info.limit;
909  return streaming_top_n::get_heap_size(getRowSize(), n, thread_count);
910  }
911  return getBufferSizeBytes(device_type, entry_count_);
912 }
913 
926  const size_t entry_count) const {
928  CHECK_GE(group_col_widths_.size(), size_t(1));
929  auto row_bytes = align_to_int64(getColsSize());
930 
931  return (interleavedBins(device_type) ? executor_->warpSize() : 1) * entry_count *
932  row_bytes;
933  }
934 
935  constexpr size_t row_index_width = sizeof(int64_t);
936  size_t total_bytes{0};
937  if (output_columnar_) {
939  ? row_index_width * entry_count
940  : sizeof(int64_t) * group_col_widths_.size() * entry_count) +
942  } else {
943  total_bytes = getRowSize() * entry_count;
944  }
945 
946  return total_bytes;
947 }
948 
950  const ExecutorDeviceType device_type) const {
951  return getBufferSizeBytes(device_type, entry_count_);
952 }
953 
955  output_columnar_ = val;
958  }
959 }
960 
961 /*
962  * Indicates the query types that are currently allowed to use the logical
963  * sized columns instead of padded sized ones.
964  */
966  // In distributed mode, result sets are serialized using rowwise iterators, so we use
967  // consistent slot widths for now
968  return output_columnar_ && !g_cluster &&
970 }
971 
973  size_t total_slot_count = col_slot_context_.getSlotCount();
974 
975  if (target_groupby_indices_.empty()) {
976  return total_slot_count;
977  }
978  return total_slot_count - std::count_if(target_groupby_indices_.begin(),
980  [](const int64_t i) { return i >= 0; });
981 }
982 
985  getGroupbyColCount() == 1);
986 }
987 
990 }
991 
993  if (g_cluster || is_table_function_) {
994  return true;
995  }
997  return true;
998  }
999  if (executor_->isCPUOnly() || render_output_ ||
1003  getGroupbyColCount() > 1)) {
1004  return true;
1005  }
1008 }
1009 
1011  return device_type == ExecutorDeviceType::GPU && !render_output_ &&
1013 }
1014 
1016  return interleaved_bins_on_gpu_ && device_type == ExecutorDeviceType::GPU;
1017 }
1018 
1019 // TODO(Saman): an implementation detail, so move this out of QMD
1021  const ExecutorDeviceType device_type) const {
1022  if (device_type != ExecutorDeviceType::GPU) {
1023  return false;
1024  } else {
1025  auto cuda_mgr = executor_->getCatalog()->getDataMgr().getCudaMgr();
1026  CHECK(cuda_mgr);
1027  return cuda_mgr->isArchVoltaOrGreaterForAll();
1028  }
1029 }
1030 
1032  return col_slot_context_.getColCount();
1033 }
1034 
1037 }
1038 
1039 const int8_t QueryMemoryDescriptor::getPaddedSlotWidthBytes(const size_t slot_idx) const {
1040  return col_slot_context_.getSlotInfo(slot_idx).padded_size;
1041 }
1042 
1044  const size_t slot_idx) const {
1045  return col_slot_context_.getSlotInfo(slot_idx).logical_size;
1046 }
1047 
1049  const size_t col_idx) const {
1050  const auto& col_slots = col_slot_context_.getSlotsForCol(col_idx);
1051  CHECK_EQ(col_slots.size(), size_t(1));
1052  return col_slots.front();
1053 }
1054 
1055 void QueryMemoryDescriptor::useConsistentSlotWidthSize(const int8_t slot_width_size) {
1056  col_slot_context_.setAllSlotsSize(slot_width_size);
1057 }
1058 
1060  // Note: Actual row size may include padding (see ResultSetBufferAccessors.h)
1062 }
1063 
1065  const int8_t actual_min_byte_width) const {
1066  return col_slot_context_.getMinPaddedByteSize(actual_min_byte_width);
1067 }
1068 
1070  const std::vector<std::tuple<int8_t, int8_t>>& slots_for_col) {
1071  col_slot_context_.addColumn(slots_for_col);
1072 }
1073 
1076 }
1077 
1080 }
1081 
1086 }
1087 
1089  switch (query_desc_type_) {
1091  return "Perfect Hash";
1093  return "Baseline Hash";
1095  return "Projection";
1097  return "Non-grouped Aggregate";
1099  return "Estimator";
1100  default:
1101  UNREACHABLE();
1102  }
1103  return "";
1104 }
1105 
1106 std::string QueryMemoryDescriptor::toString() const {
1107  auto str = reductionKey();
1108  str += "\tAllow Multifrag: " + ::toString(allow_multifrag_) + "\n";
1109  str += "\tInterleaved Bins on GPU: " + ::toString(interleaved_bins_on_gpu_) + "\n";
1110  str += "\tBlocks Share Memory: " + ::toString(blocksShareMemory()) + "\n";
1111  str += "\tThreads Share Memory: " + ::toString(threadsShareMemory()) + "\n";
1112  str += "\tUses Fast Group Values: " + ::toString(usesGetGroupValueFast()) + "\n";
1113  str +=
1114  "\tLazy Init Groups (GPU): " + ::toString(lazyInitGroups(ExecutorDeviceType::GPU)) +
1115  "\n";
1116  str += "\tEntry Count: " + std::to_string(entry_count_) + "\n";
1117  str += "\tMin Val (perfect hash only): " + std::to_string(min_val_) + "\n";
1118  str += "\tMax Val (perfect hash only): " + std::to_string(max_val_) + "\n";
1119  str += "\tBucket Val (perfect hash only): " + std::to_string(bucket_) + "\n";
1120  str += "\tSort on GPU: " + ::toString(sort_on_gpu_) + "\n";
1121  str += "\tUse Streaming Top N: " + ::toString(use_streaming_top_n_) + "\n";
1122  str += "\tOutput Columnar: " + ::toString(output_columnar_) + "\n";
1123  str += "\tRender Output: " + ::toString(render_output_) + "\n";
1124  str += "\tUse Baseline Sort: " + ::toString(must_use_baseline_sort_) + "\n";
1125  return str;
1126 }
1127 
1129  std::string str;
1130  str += "Query Memory Descriptor State\n";
1131  str += "\tQuery Type: " + queryDescTypeToString() + "\n";
1132  str +=
1133  "\tKeyless Hash: " + ::toString(keyless_hash_) +
1134  (keyless_hash_ ? ", target index for key: " + std::to_string(getTargetIdxForKey())
1135  : "") +
1136  "\n";
1137  str += "\tEffective key width: " + std::to_string(getEffectiveKeyWidth()) + "\n";
1138  str += "\tNumber of group columns: " + std::to_string(getGroupbyColCount()) + "\n";
1139  const auto group_indices_size = targetGroupbyIndicesSize();
1140  if (group_indices_size) {
1141  std::vector<std::string> group_indices_strings;
1142  for (size_t target_idx = 0; target_idx < group_indices_size; ++target_idx) {
1143  group_indices_strings.push_back(std::to_string(getTargetGroupbyIndex(target_idx)));
1144  }
1145  str += "\tTarget group by indices: " +
1146  boost::algorithm::join(group_indices_strings, ",") + "\n";
1147  }
1148  str += "\t" + col_slot_context_.toString();
1149  return str;
1150 }
1151 
1152 std::vector<TargetInfo> target_exprs_to_infos(
1153  const std::vector<Analyzer::Expr*>& targets,
1154  const QueryMemoryDescriptor& query_mem_desc) {
1155  std::vector<TargetInfo> target_infos;
1156  for (const auto target_expr : targets) {
1157  auto target = get_target_info(target_expr, g_bigint_count);
1158  if (query_mem_desc.getQueryDescriptionType() ==
1160  set_notnull(target, false);
1161  target.sql_type.set_notnull(false);
1162  }
1163  target_infos.push_back(target);
1164  }
1165  return target_infos;
1166 }
size_t getColCount() const
std::vector< Analyzer::Expr * > target_exprs
static bool many_entries(const int64_t max_val, const int64_t min_val, const int64_t bucket)
#define CHECK_EQ(x, y)
Definition: Logger.h:205
static int64_t getBucketedCardinality(const ColRangeInfo &col_range_info)
size_t getTotalBytesOfColumnarProjections(const size_t projection_count) const
std::vector< int64_t > target_expr_proj_indices(const RelAlgExecutionUnit &ra_exe_unit, const Catalog_Namespace::Catalog &cat)
void alignPaddedSlots(const bool sort_on_gpu)
int8_t updateActualMinByteWidth(const int8_t actual_min_byte_width) const
bool g_enable_smem_group_by
int8_t logical_size
size_t getPrependedGroupBufferSizeInBytes() const
class for a per-database catalog. also includes metadata for the current database and the current use...
Definition: Catalog.h:101
size_t getSlotCount() const
const bool keyless
bool is_integer() const
Definition: sqltypes.h:480
const int8_t getSlotIndexForSingleSlotCol(const size_t col_idx) const
ExecutorDeviceType
int8_t getMinPaddedByteSize(const int8_t actual_min_byte_width) const
TargetInfo get_target_info(const PointerType target_expr, const bool bigint_count)
Definition: TargetInfo.h:79
int8_t pick_baseline_key_component_width(const ExpressionRange &range, const size_t group_col_width)
const std::list< Analyzer::OrderEntry > order_entries
bool setInSituDataIfUnset(const bool is_in_situ_data)
Definition: RenderInfo.cpp:98
QueryDescriptionType hash_type_
std::string join(T const &container, std::string const &delim)
std::vector< InputDescriptor > input_descs
static std::unique_ptr< QueryMemoryDescriptor > init(const Executor *executor, const RelAlgExecutionUnit &ra_exe_unit, const std::vector< InputTableInfo > &query_infos, const ColRangeInfo &col_range_info, const KeylessInfo &keyless_info, const bool allow_multifrag, const ExecutorDeviceType device_type, const int8_t crt_min_byte_width, const bool sort_on_gpu_hint, const size_t shard_count, const size_t max_groups_buffer_entry_count, RenderInfo *render_info, const CountDistinctDescriptors count_distinct_descriptors, const bool must_use_baseline_sort, const bool output_columnar_hint, const bool streaming_top_n_hint)
#define UNREACHABLE()
Definition: Logger.h:241
void setOutputColumnar(const bool val)
const SortAlgorithm algorithm
#define CHECK_GE(x, y)
Definition: Logger.h:210
bool use_streaming_top_n(const RelAlgExecutionUnit &ra_exe_unit, const bool output_columnar)
const std::list< std::shared_ptr< Analyzer::Expr > > groupby_exprs
int64_t getIntMax() const
#define CHECK_GT(x, y)
Definition: Logger.h:209
ExpressionRangeType getType() const
void setAllSlotsSize(const int8_t slot_width_size)
std::vector< int64_t > target_expr_group_by_indices(const std::list< std::shared_ptr< Analyzer::Expr >> &groupby_exprs, const std::vector< Analyzer::Expr *> &target_exprs)
std::string to_string(char const *&&v)
const int8_t getPaddedSlotWidthBytes(const size_t slot_idx) const
const SlotSize & getSlotInfo(const size_t slot_idx) const
void useConsistentSlotWidthSize(const int8_t slot_width_size)
size_t getTotalBytesOfColumnarBuffers(const size_t entry_count) const
ExecutorDispatchMode
const size_t limit
bool g_enable_columnar_output
Definition: Execute.cpp:93
bool isPotentialInSituRender() const
Definition: RenderInfo.cpp:64
size_t getNextColOffInBytes(const int8_t *col_ptr, const size_t bin, const size_t col_idx) const
size_t get_bit_width(const SQLTypeInfo &ti)
size_t getAllSlotsPaddedSize() const
std::string cat(Ts &&... args)
size_t getTotalBytesOfColumnarBuffers() const
const ColumnDescriptor * get_column_descriptor_maybe(const int col_id, const int table_id, const Catalog_Namespace::Catalog &cat)
Definition: Execute.h:216
std::vector< CountDistinctDescriptor > CountDistinctDescriptors
Definition: CountDistinct.h:35
const SortInfo sort_info
Provides column info and slot info for the output buffer and some metadata helpers.
bool interleavedBins(const ExecutorDeviceType) const
bool isWarpSyncRequired(const ExecutorDeviceType) const
std::unique_ptr< QueryExecutionContext > getQueryExecutionContext(const RelAlgExecutionUnit &, const Executor *executor, const ExecutorDeviceType device_type, const ExecutorDispatchMode dispatch_mode, const int device_id, const int64_t num_rows, const std::vector< std::vector< const int8_t *>> &col_buffers, const std::vector< std::vector< uint64_t >> &frag_offsets, std::shared_ptr< RowSetMemoryOwner >, const bool output_columnar, const bool sort_on_gpu, RenderInfo *) const
std::vector< int64_t > target_groupby_indices_
static int8_t pick_target_compact_width(const RelAlgExecutionUnit &ra_exe_unit, const std::vector< InputTableInfo > &query_infos, const int8_t crt_min_byte_width)
size_t getBufferSizeBytes(const RelAlgExecutionUnit &ra_exe_unit, const unsigned thread_count, const ExecutorDeviceType device_type) const
bool g_bigint_count
CountDistinctDescriptors count_distinct_descriptors_
ExpressionRange getExpressionRange(const Analyzer::BinOper *expr, const std::vector< InputTableInfo > &query_infos, const Executor *, boost::optional< std::list< std::shared_ptr< Analyzer::Expr >>> simple_quals)
bool operator==(const QueryMemoryDescriptor &other) const
size_t targetGroupbyIndicesSize() const
const int32_t target_index
std::string queryDescTypeToString() const
int8_t groupColWidth(const size_t key_idx) const
int32_t getTargetIdxForKey() const
void addColumn(const std::vector< std::tuple< int8_t, int8_t >> &slots_for_col)
size_t getColOnlyOffInBytes(const size_t col_idx) const
std::vector< int8_t > get_col_byte_widths(const T &col_expr_list)
#define CHECK_LE(x, y)
Definition: Logger.h:208
const std::vector< size_t > & getSlotsForCol(const size_t col_idx) const
int64_t getTargetGroupbyIndex(const size_t target_idx) const
std::string toString() const
bool hasNulls() const
size_t getCompactByteWidth() const
QueryDescriptionType query_desc_type_
int8_t padded_size
size_t getColOffInBytesInNextBin(const size_t col_idx) const
Definition: sqldefs.h:76
size_t getAllSlotsAlignedPaddedSize() const
T visit(const Analyzer::Expr *expr) const
Descriptor for the result set buffer layout.
bool is_int_and_no_bigger_than(const SQLTypeInfo &ti, const size_t byte_width)
std::vector< TargetInfo > target_exprs_to_infos(const std::vector< Analyzer::Expr *> &targets, const QueryMemoryDescriptor &query_mem_desc)
int get_varno() const
Definition: Analyzer.h:275
std::list< std::shared_ptr< Analyzer::Expr > > quals
const SQLTypeInfo & get_type_info() const
Definition: Analyzer.h:78
std::string reductionKey() const
size_t get_heap_size(const size_t row_size, const size_t n, const size_t thread_count)
void setAllSlotsPaddedSizeToLogicalSize()
#define CHECK(condition)
Definition: Logger.h:197
size_t getColOnlyOffInBytes(const size_t slot_idx) const
#define DEBUG_TIMER(name)
Definition: Logger.h:313
std::vector< int8_t > group_col_widths_
#define EMPTY_KEY_32
QueryDescriptionType
Definition: Types.h:26
bool g_cluster
const int8_t getLogicalSlotWidthBytes(const size_t slot_idx) const
size_t getColOffInBytes(const size_t col_idx) const
std::list< std::shared_ptr< const InputColDescriptor > > input_col_descs
void addColSlotInfo(const std::vector< std::tuple< int8_t, int8_t >> &slots_for_col)
const size_t offset
static bool countDescriptorsLogicallyEmpty(const CountDistinctDescriptors &count_distinct_descriptors)
void setAllUnsetSlotsPaddedSize(const int8_t padded_size)
QueryDescriptionType getQueryDescriptionType() const
bool lazyInitGroups(const ExecutorDeviceType) const
FORCE_INLINE HOST DEVICE T align_to_int64(T addr)
int8_t pick_baseline_key_width(const RelAlgExecutionUnit &ra_exe_unit, const std::vector< InputTableInfo > &query_infos, const Executor *executor)
size_t getPrependedGroupColOffInBytes(const size_t group_idx) const
std::list< std::shared_ptr< Analyzer::Expr > > simple_quals
void set_notnull(TargetInfo &target, const bool not_null)
size_t getEffectiveKeyWidth() const
void validate() const
void sort_on_gpu(int64_t *val_buff, int32_t *key_buff, const uint64_t entry_count, const bool desc, const uint32_t chosen_bytes, ThrustAllocator &alloc)
const Expr * get_operand() const
Definition: Analyzer.h:371