| 1216 | } |
| 1217 | |
| 1218 | Status PhjBuilderPartition::BuildHashTable(bool* built) { |
| 1219 | SCOPED_TIMER(parent_->build_hash_table_timer_); |
| 1220 | DCHECK(build_rows_ != nullptr); |
| 1221 | *built = false; |
| 1222 | |
| 1223 | // Before building the hash table, we need to pin the rows in memory. |
| 1224 | RETURN_IF_ERROR(build_rows_->PinStream(built)); |
| 1225 | if (!*built) return Status::OK(); |
| 1226 | |
| 1227 | RuntimeState* state = parent_->runtime_state_; |
| 1228 | HashTableCtx* ctx = parent_->ht_ctx_.get(); |
| 1229 | ctx->set_level(level()); // Set the hash function for building the hash table. |
| 1230 | RowBatch batch(parent_->row_desc_, state->batch_size(), parent_->mem_tracker()); |
| 1231 | vector<BufferedTupleStream::FlatRowPtr> flat_rows; |
| 1232 | bool eos = false; |
| 1233 | |
| 1234 | // Allocate the partition-local hash table. Initialize the number of buckets based on |
| 1235 | // the number of build rows (the number of rows is known at this point). This assumes |
| 1236 | // there are no duplicates which can be wrong. However, the upside in the common case |
| 1237 | // (few/no duplicates) is large and the downside when there are is low (a bit more |
| 1238 | // memory; the bucket memory is small compared to the memory needed for all the build |
| 1239 | // side allocations). |
| 1240 | // One corner case is if the stream contains tuples with zero footprint (no materialized |
| 1241 | // slots). If the tuples occupy no space, this implies all rows will be duplicates, so |
| 1242 | // create a small hash table, IMPALA-2256. |
| 1243 | // |
| 1244 | // TODO: Try to allocate the hash table before pinning the stream to avoid needlessly |
| 1245 | // reading all of the spilled rows from disk when we won't succeed anyway. |
| 1246 | int64_t estimated_num_buckets = HashTable::EstimateNumBuckets(build_rows()->num_rows()); |
| 1247 | hash_tbl_.reset(HashTable::Create(parent_->ht_allocator_.get(), |
| 1248 | true /* store_duplicates */, parent_->row_desc_->tuple_descriptors().size(), |
| 1249 | build_rows(), 1 << (32 - PhjBuilder::NUM_PARTITIONING_BITS), |
| 1250 | estimated_num_buckets)); |
| 1251 | bool success; |
| 1252 | Status status = hash_tbl_->Init(&success); |
| 1253 | if (!status.ok() || !success) goto not_built; |
| 1254 | status = build_rows_->PrepareForRead(false, &success); |
| 1255 | if (!status.ok()) goto not_built; |
| 1256 | DCHECK(success) << "Stream was already pinned."; |
| 1257 | |
| 1258 | do { |
| 1259 | status = build_rows_->GetNext(&batch, &eos, &flat_rows); |
| 1260 | if (!status.ok()) goto not_built; |
| 1261 | DCHECK_EQ(batch.num_rows(), flat_rows.size()); |
| 1262 | DCHECK_LE(batch.num_rows(), hash_tbl_->EmptyBuckets()); |
| 1263 | TPrefetchMode::type prefetch_mode = state->query_options().prefetch_mode; |
| 1264 | |
| 1265 | InsertBatchFn insert_batch_fn; |
| 1266 | if (level() == 0) { |
| 1267 | insert_batch_fn = parent_->insert_batch_fn_level0_.load(); |
| 1268 | } else { |
| 1269 | insert_batch_fn = parent_->insert_batch_fn_.load(); |
| 1270 | } |
| 1271 | |
| 1272 | if (insert_batch_fn != nullptr) { |
| 1273 | if (UNLIKELY( |
| 1274 | !insert_batch_fn(this, prefetch_mode, ctx, &batch, flat_rows, &status))) { |
| 1275 | goto not_built; |
no test coverage detected