32 using namespace impala;
35 DEFINE_bool(enable_quadratic_probing,
false,
"Enable quadratic probing hash table");
84 const vector<ExprContext*>& probe_expr_ctxs,
bool stores_nulls,
bool finds_nulls,
85 int32_t initial_seed,
int max_levels,
int num_build_tuples)
86 : build_expr_ctxs_(build_expr_ctxs),
87 probe_expr_ctxs_(probe_expr_ctxs),
88 stores_nulls_(stores_nulls),
89 finds_nulls_(finds_nulls),
91 row_(reinterpret_cast<
TupleRow*>(malloc(sizeof(
Tuple*) * num_build_tuples))) {
102 DCHECK_GE(max_levels, 0);
104 DCHECK_NE(initial_seed, 0);
105 seeds_.resize(max_levels + 1);
107 for (
int i = 1; i <= max_levels; ++i) {
125 bool has_null =
false;
126 for (
int i = 0; i < ctxs.size(); ++i) {
128 void* val = ctxs[i]->GetValue(row);
196 block_mgr_client_(client),
197 tuple_stream_(stream),
198 data_page_pool_(NULL),
199 stores_tuples_(num_build_tuples == 1),
200 quadratic_probing_(FLAGS_enable_quadratic_probing),
201 total_data_page_size_(0),
203 node_remaining_current_page_(0),
204 num_duplicate_nodes_(0),
205 max_num_buckets_(max_num_buckets),
207 num_buckets_(num_buckets),
208 num_filled_buckets_(0),
209 num_buckets_with_duplicates_(0),
210 num_build_tuples_(num_build_tuples),
212 num_probes_(0), num_failed_probes_(0), travel_length_(0), num_hash_collisions_(0),
214 DCHECK_EQ((num_buckets & (num_buckets-1)), 0) <<
"num_buckets must be a power of 2";
215 DCHECK_GT(num_buckets, 0) <<
"num_buckets must be larger than 0";
221 block_mgr_client_(NULL),
223 data_page_pool_(pool),
224 stores_tuples_(true),
225 quadratic_probing_(quadratic_probing),
226 total_data_page_size_(0),
228 node_remaining_current_page_(0),
229 num_duplicate_nodes_(0),
230 max_num_buckets_(-1),
232 num_buckets_(num_buckets),
233 num_filled_buckets_(0),
234 num_buckets_with_duplicates_(0),
235 num_build_tuples_(1),
237 num_probes_(0), num_failed_probes_(0), travel_length_(0), num_hash_collisions_(0),
239 DCHECK_EQ((num_buckets & (num_buckets-1)), 0) <<
"num_buckets must be a power of 2";
240 DCHECK_GT(num_buckets, 0) <<
"num_buckets must be larger than 0";
253 memset(
buckets_, 0, buckets_byte_size);
260 const int64_t LARGE_HT = 128 * 1024;
261 const int64_t HEAVILY_USED = 1024 * 1024;
294 DCHECK_EQ((num_buckets & (num_buckets-1)), 0)
295 <<
"num_buckets=" << num_buckets <<
" must be a power of 2";
297 "smaller number of buckets than the number of filled buckets.";
298 VLOG(2) <<
"Resizing hash table from "
299 <<
num_buckets_ <<
" to " << num_buckets <<
" buckets.";
309 int64_t new_size = num_buckets *
sizeof(
Bucket);
314 Bucket* new_buckets =
reinterpret_cast<Bucket*
>(malloc(new_size));
315 DCHECK_NOTNULL(new_buckets);
316 memset(new_buckets, 0, new_size);
325 int64_t bucket_idx =
Probe(new_buckets, num_buckets, NULL, bucket_to_copy->
hash,
330 Bucket* dst_bucket = &new_buckets[bucket_idx];
331 *dst_bucket = *bucket_to_copy;
346 int64_t page_size = 0;
355 DCHECK(status.
ok() || block == NULL);
356 if (block == NULL)
return false;
376 ss <<
"(" << htdata.
tuple <<
")";
383 ss <<
" " <<
PrintRow(
GetRow(htdata, reinterpret_cast<TupleRow*>(row)), *desc);
392 if (skip_empty && !
buckets_[i].filled)
continue;
405 while (node != NULL) {
406 if (!first) ss <<
",";
430 << curr_fill_factor << endl;
449 Value* dst_ptr = builder->CreateStructGEP(dst, 0,
"string_ptr");
450 Value* dst_len = builder->CreateStructGEP(dst, 1,
"string_len");
452 Value* null_ptr = builder->CreateIntToPtr(null_len, codegen->
ptr_type());
453 builder->CreateStore(null_ptr, dst_ptr);
454 builder->CreateStore(null_len, dst_len);
456 Value* null_value = NULL;
461 dst = builder->CreateBitCast(dst, codegen->
ptr_type());
472 float fvn_seed_float = *
reinterpret_cast<float*
>(&fvn_seed);
473 null_value = ConstantFP::get(codegen->
context(), APFloat(fvn_seed_float));
478 double fvn_seed_double = *
reinterpret_cast<double*
>(&fvn_seed);
479 null_value = ConstantFP::get(codegen->
context(), APFloat(fvn_seed_double));
485 builder->CreateStore(null_value, dst);
522 for (
int i = 0; i < ctxs.size(); ++i) {
532 DCHECK(tuple_row_type != NULL);
533 PointerType* tuple_row_ptr_type = PointerType::get(tuple_row_type, 0);
536 DCHECK(this_type != NULL);
537 PointerType* this_ptr_type = PointerType::get(this_type, 0);
544 LLVMContext& context = codegen->
context();
547 Function* fn = prototype.GeneratePrototype(&builder, args);
549 Value*
row = args[1];
552 for (
int i = 0; i < ctxs.size(); ++i) {
559 codegen->
GetPtrType(ctxs[i]->root()->type()), loc);
561 BasicBlock* null_block = BasicBlock::Create(context,
"null", fn);
562 BasicBlock* not_null_block = BasicBlock::Create(context,
"not_null", fn);
563 BasicBlock* continue_block = BasicBlock::Create(context,
"continue", fn);
567 Status status = ctxs[i]->root()->GetCodegendComputeFn(state, &expr_fn);
570 fn->eraseFromParent();
576 Value* expr_fn_args[] = { ctx_arg, row };
578 codegen, &builder, ctxs[i]->root()->type(), expr_fn, expr_fn_args,
"result");
584 Value* llvm_null_byte_loc =
586 builder.CreateStore(null_byte, llvm_null_byte_loc);
588 builder.CreateCondBr(is_null, null_block, not_null_block);
591 builder.SetInsertPoint(null_block);
597 builder.CreateBr(continue_block);
601 builder.SetInsertPoint(not_null_block);
603 builder.CreateBr(continue_block);
606 builder.SetInsertPoint(continue_block);
609 PHINode* is_null_phi = builder.CreatePHI(codegen->
boolean_type(), 2,
"is_null_phi");
610 is_null_phi->addIncoming(codegen->
true_value(), null_block);
611 is_null_phi->addIncoming(codegen->
false_value(), not_null_block);
612 has_null = builder.CreateOr(has_null, is_null_phi,
"has_null");
615 builder.CreateRet(has_null);
658 DCHECK(this_type != NULL);
659 PointerType* this_ptr_type = PointerType::get(this_type, 0);
662 (use_murmur ?
"MurmurHashCurrentRow" :
"HashCurrentRow"),
666 LLVMContext& context = codegen->
context();
669 Function* fn = prototype.GeneratePrototype(&builder, &this_arg);
672 Function* get_hash_seed_fn = codegen->
GetFunction(IRFunction::HASH_TABLE_GET_HASH_SEED);
673 Value*
seed = builder.CreateCall(get_hash_seed_fn, this_arg,
"seed");
675 Value* hash_result =
seed;
680 Function* hash_fn = use_murmur ?
684 hash_result = builder.CreateCall3(hash_fn, data, len, hash_result,
"hash");
688 Function* hash_fn = use_murmur ?
692 hash_result = builder.CreateCall3(hash_fn, data, len, hash_result,
"hash");
700 BasicBlock* null_block = NULL;
701 BasicBlock* not_null_block = NULL;
702 BasicBlock* continue_block = NULL;
703 Value* str_null_result = NULL;
710 null_block = BasicBlock::Create(context,
"null", fn);
711 not_null_block = BasicBlock::Create(context,
"not_null", fn);
712 continue_block = BasicBlock::Create(context,
"continue", fn);
715 Value* llvm_null_byte_loc =
717 Value* null_byte = builder.CreateLoad(llvm_null_byte_loc,
"null_byte");
718 Value* is_null = builder.CreateICmpNE(null_byte,
720 builder.CreateCondBr(is_null, null_block, not_null_block);
724 builder.SetInsertPoint(null_block);
725 Function* null_hash_fn = use_murmur ?
731 builder.CreateCall3(null_hash_fn, llvm_loc, len, hash_result,
"str_null");
732 builder.CreateBr(continue_block);
734 builder.SetInsertPoint(not_null_block);
740 Value* ptr = builder.CreateStructGEP(str_val, 0);
741 Value* len = builder.CreateStructGEP(str_val, 1);
742 ptr = builder.CreateLoad(ptr,
"ptr");
743 len = builder.CreateLoad(len,
"len");
748 Value* string_hash_result =
749 builder.CreateCall3(general_hash_fn, ptr, len, hash_result,
"string_hash");
752 builder.CreateBr(continue_block);
753 builder.SetInsertPoint(continue_block);
756 PHINode* phi_node = builder.CreatePHI(codegen->
GetType(
TYPE_INT), 2,
"hash_phi");
757 phi_node->addIncoming(string_hash_result, not_null_block);
758 phi_node->addIncoming(str_null_result, null_block);
759 hash_result = phi_node;
761 hash_result = string_hash_result;
766 builder.CreateRet(hash_result);
830 DCHECK(tuple_row_type != NULL);
831 PointerType* tuple_row_ptr_type = PointerType::get(tuple_row_type, 0);
834 DCHECK(this_type != NULL);
835 PointerType* this_ptr_type = PointerType::get(this_type, 0);
841 LLVMContext& context = codegen->
context();
844 Function* fn = prototype.GeneratePrototype(&builder, args);
845 Value*
row = args[1];
847 BasicBlock* false_block = BasicBlock::Create(context,
"false_block", fn);
849 BasicBlock* null_block = BasicBlock::Create(context,
"null", fn);
850 BasicBlock* not_null_block = BasicBlock::Create(context,
"not_null", fn);
851 BasicBlock* continue_block = BasicBlock::Create(context,
"continue", fn);
858 fn->eraseFromParent();
864 Value* expr_fn_args[] = { ctx_arg, row };
874 Value* llvm_null_byte_loc =
876 Value* null_byte = builder.CreateLoad(llvm_null_byte_loc);
877 probe_is_null = builder.CreateICmpNE(null_byte,
887 builder.CreateCondBr(is_null, null_block, not_null_block);
890 builder.SetInsertPoint(null_block);
891 builder.CreateCondBr(probe_is_null, continue_block, false_block);
894 builder.SetInsertPoint(not_null_block);
896 BasicBlock* cmp_block = BasicBlock::Create(context,
"cmp", fn);
898 builder.CreateCondBr(probe_is_null, false_block, cmp_block);
899 builder.SetInsertPoint(cmp_block);
903 builder.CreateCondBr(is_equal, continue_block, false_block);
905 builder.SetInsertPoint(continue_block);
909 builder.SetInsertPoint(false_block);
uint32_t Hash(const void *input, int len, int32_t hash)
Wrapper function for calling correct HashUtil function in non-codegen'd case.
stl-like iterator interface.
static uint32_t SEED_PRIMES[]
static const int64_t TEST_PAGE_SIZE
The underlying memory management is done by the BufferedBlockMgr.
bool AtEnd() const
Returns true if this iterator is at the end, i.e. GetRow() cannot be called.
std::string PrintStats() const
Update and print some statistics that can be used for performance debugging.
bool IR_NO_INLINE Equals(TupleRow *build_row)
bool GrowNodeArray()
Grow the node array. Returns false on OOM.
const std::string GetDetail() const
static CodegenAnyVal CreateCallWrapped(LlvmCodeGen *cg, LlvmCodeGen::LlvmBuilder *builder, const ColumnType &type, llvm::Function *fn, llvm::ArrayRef< llvm::Value * > args, const char *name="", llvm::Value *result_ptr=NULL)
Same as above but wraps the result in a CodegenAnyVal.
llvm::PointerType * GetPtrType(llvm::Type *type)
Return a pointer type to 'type'.
void DebugStringTuple(std::stringstream &ss, HtData &htdata, const RowDescriptor *desc)
Print the content of a bucket or node.
bool EvalRow(TupleRow *row, const std::vector< ExprContext * > &ctxs)
static bool Eq(const void *v1, const void *v2, const ColumnType &type)
BufferedBlockMgr * block_mgr()
Utility struct that wraps a variable name and llvm type.
llvm::Function * CodegenHashCurrentRow(RuntimeState *state, bool use_murmur)
uint8_t * expr_values_buffer_
A tuple with 0 materialised slots is represented as NULL.
T * Allocate(int size)
Allocates the specified number of bytes from this block.
const StringSearch UrlParser::hash_search & hash
BufferedBlockMgr::Client * block_mgr_client_
Client to allocate data pages with.
static const double MAX_FILL_FACTOR
void ReleaseMemory(Client *client, int64_t size)
const std::vector< ExprContext * > & build_expr_ctxs_
Status GetNewBlock(Client *client, Block *unpin_block, Block **block, int64_t len=-1)
DuplicateNode * duplicates
static const int64_t BUCKET_NOT_FOUND
Bucket index value when probe is not successful.
bool Init()
Allocates the initial bucket structure. Returns false if OOM.
Iterator Begin(HashTableCtx *ht_ctx)
bool ConsumeMemory(Client *client, int64_t size)
static const char * LLVM_CLASS_NAME
int64_t max_block_size() const
union impala::HashTable::Bucket::@6 bucketData
Either the data for this bucket or the linked list of duplicates.
bool CheckAndResize(uint64_t buckets_to_fill, HashTableCtx *ht_ctx)
llvm::Type * boolean_type()
Simple wrappers to reduce code verbosity.
std::vector< BufferedBlockMgr::Block * > data_pages_
Data pages for all nodes. These are always pinned.
Either the row in the tuple stream or a pointer to the single tuple of this row.
const std::vector< ExprContext * > & probe_expr_ctxs_
int64_t num_duplicate_nodes_
Number of duplicate nodes.
Linked list of entries used for duplicates.
int64_t num_filled_buckets_
Number of non-empty buckets. Used to determine when to resize.
LLVM code generator. This is the top level object to generate jitted code.
static const char * LLVM_CLASS_NAME
static const char * LLVM_CLASS_NAME
llvm::Value * CastPtrToLlvmPtr(llvm::Type *type, const void *ptr)
void AddArgument(const NamedVariable &var)
Add argument.
std::vector< int > expr_values_buffer_offsets_
const bool stores_tuples_
MemTracker * mem_tracker()
llvm::Function * GetHashFunction(int num_bytes=-1)
llvm::Function * CodegenEquals(RuntimeState *state)
BufferedTupleStream::RowIdx idx
llvm::Function * GetFunction(IRFunction::Type)
static int64_t NULL_VALUE[]
static void Write(const void *value, Tuple *tuple, const SlotDescriptor *slot_desc, MemPool *pool)
int64_t num_resizes_
How many times this table has resized so far.
llvm::Function * CodegenEvalRow(RuntimeState *state, bool build_row)
bool ResizeBuckets(int64_t num_buckets, HashTableCtx *ht_ctx)
Resize the hash table to 'num_buckets'. Returns false on OOM.
static int ComputeResultsLayout(const std::vector< Expr * > &exprs, std::vector< int > *offsets, int *var_result_begin)
int64_t CurrentMemSize() const
llvm::Value * true_value()
Returns true/false constants (bool type)
TupleRow * GetRow(HtData &htdata, TupleRow *row) const
Return the TupleRow pointed by 'htdata'.
int64_t num_buckets_with_duplicates_
static const uint32_t FNV_SEED
static void CodegenAssignNullValue(LlvmCodeGen *codegen, LlvmCodeGen::LlvmBuilder *builder, Value *dst, const ColumnType &type)
int64_t num_hash_collisions_
static const int64_t INITIAL_DATA_PAGE_SIZES[]
int64_t num_buckets_
Total number of buckets (filled and empty).
HashTableCtx(const std::vector< ExprContext * > &build_expr_ctxs, const std::vector< ExprContext * > &probe_expr_ctxs, bool stores_nulls, bool finds_nulls, int32_t initial_seed, int max_levels, int num_build_tuples)
int node_remaining_current_page_
Number of nodes left in the current page.
uint8_t * expr_value_null_bits_
llvm::Value * EqToNativePtr(llvm::Value *native_ptr)
MemPool * data_page_pool_
Only used for tests to allocate data pages instead of the block mgr.
static const int NUM_SMALL_DATA_PAGES
std::vector< uint32_t > seeds_
The seeds to use for hashing. Indexed by the level.
TupleRow * row_
Scratch buffer to generate rows on the fly.
int64_t num_buckets() const
Returns the number of buckets.
DuplicateNode * next_node_
Next duplicate node to insert. Vaild when node_remaining_current_page_ > 0.
llvm::Value * false_value()
int64_t total_data_page_size_
Byte size of all buffers in data_pages_.
HashTable(RuntimeState *state, BufferedBlockMgr::Client *client, int num_build_tuples, BufferedTupleStream *tuple_stream, int64_t max_num_buckets, int64_t initial_num_buckets=1024)
llvm::Type * GetType(const ColumnType &type)
Returns llvm type for the column type.
uint32_t HashVariableLenRow()
Status GetCodegen(LlvmCodeGen **codegen, bool initialize=true)
llvm::Value * GetIsNull(const char *name="is_null")
Gets the 'is_null' field of the *Val.
void NextFilledBucket(int64_t *bucket_idx, DuplicateNode **node)
llvm::Value * GetIntConstant(PrimitiveType type, int64_t val)
Returns the constant 'val' of 'type'.
llvm::Function * FinalizeFunction(llvm::Function *function)
int64_t IR_ALWAYS_INLINE Probe(Bucket *buckets, int64_t num_buckets, HashTableCtx *ht_ctx, uint32_t hash, bool *found)
There are wrappers of this function that perform the Find and Insert logic.
void Close()
Call to cleanup any resources.
static IntGauge * HASH_TABLE_TOTAL_BYTES
void Close()
Call to cleanup any resources. Must be called once.
const int64_t max_num_buckets_
llvm::Function * GetMurmurHashFunction(int num_bytes=-1)
string PrintRow(TupleRow *row, const RowDescriptor &d)
DEFINE_bool(enable_quadratic_probing, false,"Enable quadratic probing hash table")
llvm::LLVMContext & context()
void ToNativePtr(llvm::Value *native_ptr)
std::string DebugString(bool skip_empty, bool show_match, const RowDescriptor *build_desc)
uint8_t * Allocate(int size)
llvm::PointerType * ptr_type()
int64_t num_failed_probes_
Number of probes that failed and had to fall back to linear probing without cap.