diff --git a/be/src/benchmarks/hash-benchmark.cc b/be/src/benchmarks/hash-benchmark.cc index 4f6d7f1dc..125310b90 100644 --- a/be/src/benchmarks/hash-benchmark.cc +++ b/be/src/benchmarks/hash-benchmark.cc @@ -429,7 +429,7 @@ int main(int argc, char **argv) { Status status; scoped_ptr codegen; - status = LlvmCodeGen::CreateImpalaCodegen(&obj_pool, "test", &codegen); + status = LlvmCodeGen::CreateImpalaCodegen(&obj_pool, NULL, "test", &codegen); if (!status.ok()) { cout << "Could not start codegen."; return -1; diff --git a/be/src/codegen/llvm-codegen-test.cc b/be/src/codegen/llvm-codegen-test.cc index 178baa2ef..1238dde64 100644 --- a/be/src/codegen/llvm-codegen-test.cc +++ b/be/src/codegen/llvm-codegen-test.cc @@ -42,9 +42,9 @@ class LlvmCodeGenTest : public testing:: Test { ObjectPool pool; Status status; for (int i = 0; i < 10; ++i) { - LlvmCodeGen object1(&pool, "Test"); - LlvmCodeGen object2(&pool, "Test"); - LlvmCodeGen object3(&pool, "Test"); + LlvmCodeGen object1(&pool, NULL, "Test"); + LlvmCodeGen object2(&pool, NULL, "Test"); + LlvmCodeGen object3(&pool, NULL, "Test"); ASSERT_OK(object1.Init(unique_ptr(new Module("Test", object1.context())))); ASSERT_OK(object2.Init(unique_ptr(new Module("Test", object2.context())))); @@ -53,16 +53,16 @@ class LlvmCodeGenTest : public testing:: Test { } // Wrapper to call private test-only methods on LlvmCodeGen object - static Status CreateFromFile(ObjectPool* pool, const string& filename, - scoped_ptr* codegen) { - return LlvmCodeGen::CreateFromFile(pool, filename, "test", codegen); + static Status CreateFromFile( + ObjectPool* pool, const string& filename, scoped_ptr* codegen) { + return LlvmCodeGen::CreateFromFile(pool, NULL, filename, "test", codegen); } static LlvmCodeGen* CreateCodegen(ObjectPool* pool) { - LlvmCodeGen* codegen = pool->Add(new LlvmCodeGen(pool, "Test")); + LlvmCodeGen* codegen = pool->Add(new LlvmCodeGen(pool, NULL, "Test")); if (codegen != NULL) { - Status status = codegen->Init( - unique_ptr(new Module("Test", codegen->context()))); + Status status = + codegen->Init(unique_ptr(new Module("Test", codegen->context()))); if (!status.ok()) return NULL; } return codegen; @@ -82,10 +82,7 @@ class LlvmCodeGenTest : public testing:: Test { return codegen->VerifyFunction(fn); } - static Status FinalizeModule(LlvmCodeGen* codegen) { - return codegen->FinalizeModule(); - } - + static Status FinalizeModule(LlvmCodeGen* codegen) { return codegen->FinalizeModule(); } }; // Simple test to just make and destroy llvmcodegen objects. LLVM @@ -109,7 +106,8 @@ TEST_F(LlvmCodeGenTest, BadIRFile) { ObjectPool pool; string module_file = "NonExistentFile.ir"; scoped_ptr codegen; - EXPECT_FALSE(LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen).ok()); + EXPECT_FALSE( + LlvmCodeGenTest::CreateFromFile(&pool, module_file.c_str(), &codegen).ok()); } // IR for the generated linner loop @@ -289,7 +287,7 @@ TEST_F(LlvmCodeGenTest, StringValue) { ObjectPool pool; scoped_ptr codegen; - ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, "test", &codegen)); + ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen)); EXPECT_TRUE(codegen.get() != NULL); string str("Test"); @@ -332,7 +330,7 @@ TEST_F(LlvmCodeGenTest, MemcpyTest) { ObjectPool pool; scoped_ptr codegen; - ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, "test", &codegen)); + ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen)); ASSERT_TRUE(codegen.get() != NULL); LlvmCodeGen::FnPrototype prototype(codegen.get(), "MemcpyTest", codegen->void_type()); @@ -379,13 +377,13 @@ TEST_F(LlvmCodeGenTest, HashTest) { // Loop to test both the sse4 on/off paths for (int i = 0; i < 2; ++i) { scoped_ptr codegen; - ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, "test", &codegen)); + ASSERT_OK(LlvmCodeGen::CreateImpalaCodegen(&pool, NULL, "test", &codegen)); ASSERT_TRUE(codegen.get() != NULL); - Value* llvm_data1 = codegen->CastPtrToLlvmPtr(codegen->ptr_type(), - const_cast(data1)); - Value* llvm_data2 = codegen->CastPtrToLlvmPtr(codegen->ptr_type(), - const_cast(data2)); + Value* llvm_data1 = + codegen->CastPtrToLlvmPtr(codegen->ptr_type(), const_cast(data1)); + Value* llvm_data2 = + codegen->CastPtrToLlvmPtr(codegen->ptr_type(), const_cast(data2)); Value* llvm_len1 = codegen->GetIntConstant(TYPE_INT, strlen(data1)); Value* llvm_len2 = codegen->GetIntConstant(TYPE_INT, strlen(data2)); diff --git a/be/src/codegen/llvm-codegen.cc b/be/src/codegen/llvm-codegen.cc index f6e7e972a..596f48aba 100644 --- a/be/src/codegen/llvm-codegen.cc +++ b/be/src/codegen/llvm-codegen.cc @@ -25,9 +25,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -52,17 +52,18 @@ #include #include -#include "common/logging.h" #include "codegen/codegen-anyval.h" #include "codegen/codegen-symbol-emitter.h" #include "codegen/impala-ir-data.h" #include "codegen/instruction-counter.h" #include "codegen/mcjit-mem-mgr.h" +#include "common/logging.h" #include "impala-ir/impala-ir-names.h" #include "runtime/descriptors.h" #include "runtime/hdfs-fs-cache.h" #include "runtime/lib-cache.h" #include "runtime/mem-pool.h" +#include "runtime/mem-tracker.h" #include "runtime/string-value.h" #include "runtime/timestamp-value.h" #include "util/cpu-info.h" @@ -76,7 +77,7 @@ using namespace llvm; using namespace strings; using std::fstream; -using std::unique_ptr; +using std::move; DEFINE_bool(print_llvm_ir_instruction_count, false, "if true, prints the instruction counts of all JIT'd functions"); @@ -200,7 +201,7 @@ void LlvmCodeGen::InitializeLlvm(bool load_backend) { ObjectPool init_pool; scoped_ptr init_codegen; - Status status = LlvmCodeGen::CreateFromMemory(&init_pool, "init", &init_codegen); + Status status = LlvmCodeGen::CreateFromMemory(&init_pool, NULL, "init", &init_codegen); ParseGVForFunctions(init_codegen->module_, &gv_ref_ir_fns_); // Validate the module by verifying that functions for all IRFunction::Type @@ -213,16 +214,18 @@ void LlvmCodeGen::InitializeLlvm(bool load_backend) { } } -LlvmCodeGen::LlvmCodeGen(ObjectPool* pool, const string& id) : - id_(id), - profile_(pool, "CodeGen"), - optimizations_enabled_(false), - is_corrupt_(false), - is_compiled_(false), - context_(new llvm::LLVMContext()), - module_(NULL), - loaded_functions_(IRFunction::FN_END, NULL) { - +LlvmCodeGen::LlvmCodeGen( + ObjectPool* pool, MemTracker* parent_mem_tracker, const string& id) + : id_(id), + profile_(pool, "CodeGen"), + mem_tracker_(new MemTracker(&profile_, -1, "CodeGen", parent_mem_tracker)), + optimizations_enabled_(false), + is_corrupt_(false), + is_compiled_(false), + context_(new llvm::LLVMContext()), + module_(NULL), + memory_manager_(NULL), + loaded_functions_(IRFunction::FN_END, NULL) { DCHECK(llvm_initialized_) << "Must call LlvmCodeGen::InitializeLlvm first."; load_module_timer_ = ADD_TIMER(&profile_, "LoadTime"); @@ -235,9 +238,9 @@ LlvmCodeGen::LlvmCodeGen(ObjectPool* pool, const string& id) : num_instructions_ = ADD_COUNTER(&profile_, "NumInstructions", TUnit::UNIT); } -Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, +Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, MemTracker* parent_mem_tracker, const string& file, const string& id, scoped_ptr* codegen) { - codegen->reset(new LlvmCodeGen(pool, id)); + codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id)); SCOPED_TIMER((*codegen)->profile_.total_time_counter()); unique_ptr loaded_module; @@ -246,9 +249,9 @@ Status LlvmCodeGen::CreateFromFile(ObjectPool* pool, return (*codegen)->Init(std::move(loaded_module)); } -Status LlvmCodeGen::CreateFromMemory(ObjectPool* pool, const string& id, - scoped_ptr* codegen) { - codegen->reset(new LlvmCodeGen(pool, id)); +Status LlvmCodeGen::CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker, + const string& id, scoped_ptr* codegen) { + codegen->reset(new LlvmCodeGen(pool, parent_mem_tracker, id)); SCOPED_TIMER((*codegen)->profile_.total_time_counter()); // Select the appropriate IR version. We cannot use LLVM IR with SSE4.2 instructions on @@ -375,9 +378,9 @@ void LlvmCodeGen::StripGlobalCtorsDtors(llvm::Module* module) { if (destructors != NULL) destructors->eraseFromParent(); } -Status LlvmCodeGen::CreateImpalaCodegen( - ObjectPool* pool, const string& id, scoped_ptr* codegen_ret) { - RETURN_IF_ERROR(CreateFromMemory(pool, id, codegen_ret)); +Status LlvmCodeGen::CreateImpalaCodegen(ObjectPool* pool, MemTracker* parent_mem_tracker, + const string& id, scoped_ptr* codegen_ret) { + RETURN_IF_ERROR(CreateFromMemory(pool, parent_mem_tracker, id, codegen_ret)); LlvmCodeGen* codegen = codegen_ret->get(); // Parse module for cross compiled functions and types @@ -422,8 +425,9 @@ Status LlvmCodeGen::Init(unique_ptr module) { EngineBuilder builder(std::move(module)); builder.setEngineKind(EngineKind::JIT); builder.setOptLevel(opt_level); - builder.setMCJITMemoryManager( - unique_ptr(new ImpalaMCJITMemoryManager())); + unique_ptr memory_manager(new ImpalaMCJITMemoryManager); + memory_manager_ = memory_manager.get(); + builder.setMCJITMemoryManager(move(memory_manager)); builder.setMCPU(cpu_name_); builder.setMAttrs(cpu_attrs_); builder.setErrorStr(&error_string_); @@ -464,6 +468,10 @@ void LlvmCodeGen::SetupJITListeners() { } LlvmCodeGen::~LlvmCodeGen() { + if (memory_manager_ != NULL) mem_tracker_->Release(memory_manager_->bytes_tracked()); + if (mem_tracker_->parent() != NULL) mem_tracker_->UnregisterFromParent(); + mem_tracker_.reset(); + // Execution engine executes callback on event listener, so tear down engine first. execution_engine_.reset(); symbol_emitter_.reset(); @@ -753,8 +761,14 @@ bool LlvmCodeGen::VerifyFunction(Function* fn) { return true; } -LlvmCodeGen::FnPrototype::FnPrototype(LlvmCodeGen* codegen, const string& name, - Type* ret_type) : codegen_(codegen), name_(name), ret_type_(ret_type) { +void LlvmCodeGen::SetNoInline(llvm::Function* function) const { + function->removeFnAttr(llvm::Attribute::AlwaysInline); + function->addFnAttr(llvm::Attribute::NoInline); +} + +LlvmCodeGen::FnPrototype::FnPrototype( + LlvmCodeGen* codegen, const string& name, Type* ret_type) + : codegen_(codegen), name_(name), ret_type_(ret_type) { DCHECK(!codegen_->is_compiled_) << "Not valid to add additional functions"; } @@ -927,10 +941,15 @@ Status LlvmCodeGen::FinalizeModule() { // Don't waste time optimizing module if there are no functions to JIT. This can happen // if the codegen object is created but no functions are successfully codegen'd. - if (fns_to_jit_compile_.empty()) return Status::OK(); + if (fns_to_jit_compile_.empty()) { + DestroyModule(); + return Status::OK(); + } RETURN_IF_ERROR(FinalizeLazyMaterialization()); - if (optimizations_enabled_ && !FLAGS_disable_optimization_passes) OptimizeModule(); + if (optimizations_enabled_ && !FLAGS_disable_optimization_passes) { + RETURN_IF_ERROR(OptimizeModule()); + } if (FLAGS_opt_module_dir.size() != 0) { string path = FLAGS_opt_module_dir + "/" + id_ + "_opt.ll"; @@ -949,17 +968,28 @@ Status LlvmCodeGen::FinalizeModule() { execution_engine_->finalizeObject(); } - // Get pointers to all codegen'd functions. + // Get pointers to all codegen'd functions for (int i = 0; i < fns_to_jit_compile_.size(); ++i) { Function* function = fns_to_jit_compile_[i].first; void* jitted_function = execution_engine_->getPointerToFunction(function); DCHECK(jitted_function != NULL) << "Failed to jit " << function->getName().data(); *fns_to_jit_compile_[i].second = jitted_function; } + + DestroyModule(); + + // Track the memory consumed by the compiled code. + int64_t bytes_allocated = memory_manager_->bytes_allocated(); + if (!mem_tracker_->TryConsume(bytes_allocated)) { + const string& msg = Substitute( + "Failed to allocate '$0' bytes for compiled code module", bytes_allocated); + return mem_tracker_->MemLimitExceeded(NULL, msg, bytes_allocated); + } + memory_manager_->set_bytes_tracked(bytes_allocated); return Status::OK(); } -void LlvmCodeGen::OptimizeModule() { +Status LlvmCodeGen::OptimizeModule() { SCOPED_TIMER(optimization_timer_); // This pass manager will construct optimizations passes that are "typical" for @@ -1003,6 +1033,14 @@ void LlvmCodeGen::OptimizeModule() { COUNTER_SET(num_functions_, counter.GetCount(InstructionCounter::TOTAL_FUNCTIONS)); COUNTER_SET(num_instructions_, counter.GetCount(InstructionCounter::TOTAL_INSTS)); + int64_t estimated_memory = ESTIMATED_OPTIMIZER_BYTES_PER_INST + * counter.GetCount(InstructionCounter::TOTAL_INSTS); + if (!mem_tracker_->TryConsume(estimated_memory)) { + const string& msg = Substitute( + "Codegen failed to reserve '$0' bytes for optimization", estimated_memory); + return mem_tracker_->MemLimitExceeded(NULL, msg, estimated_memory); + } + // Create and run function pass manager unique_ptr fn_pass_manager( new legacy::FunctionPassManager(module_)); @@ -1027,6 +1065,22 @@ void LlvmCodeGen::OptimizeModule() { VLOG(1) << counter.PrintCounters(); } } + + mem_tracker_->Release(estimated_memory); + return Status::OK(); +} + +void LlvmCodeGen::DestroyModule() { + // Clear all references to LLVM objects owned by the module. + loaded_functions_.clear(); + codegend_functions_.clear(); + registered_exprs_map_.clear(); + registered_exprs_.clear(); + llvm_intrinsics_.clear(); + hash_fns_.clear(); + fns_to_jit_compile_.clear(); + execution_engine_->removeModule(module_); + module_ = NULL; } void LlvmCodeGen::AddFunctionToJit(Function* fn, void** fn_ptr) { diff --git a/be/src/codegen/llvm-codegen.h b/be/src/codegen/llvm-codegen.h index 21f9f7188..8853d7ed8 100644 --- a/be/src/codegen/llvm-codegen.h +++ b/be/src/codegen/llvm-codegen.h @@ -72,6 +72,7 @@ namespace llvm { namespace impala { class CodegenSymbolEmitter; +class ImpalaMCJITMemoryManager; class SubExprElimination; class TupleDescriptor; @@ -124,6 +125,14 @@ class LlvmBuilder : public llvm::IRBuilder<> { /// instructions attached to the function object. Functions reachable by the function /// are also materialized recursively. // +/// Memory used for codegen is tracked via the MemTracker hierarchy. Codegen can use +/// significant memory for the IR module and for the optimization and compilation +/// algorithms. LLVM provides no way to directly track this transient memory - instead +/// the memory consumption is estimated based on the size of the IR module and released +/// once compilation finishes. Once compilation finishes, the size of the compiled +/// machine code is obtained from LLVM and and is tracked until the LlvmCodeGen object +/// is torn down and the compiled code is freed. +// class LlvmCodeGen { public: /// This function must be called once per process before any llvm API calls are @@ -138,13 +147,15 @@ class LlvmCodeGen { /// Creates a codegen instance for Impala initialized with the cross-compiled Impala IR. /// 'codegen' will contain the created object on success. + /// 'parent_mem_tracker' - if non-NULL, the CodeGen MemTracker is created under this. /// 'id' is used for outputting the IR module for debugging. - static Status CreateImpalaCodegen( - ObjectPool*, const std::string& id, boost::scoped_ptr* codegen); + static Status CreateImpalaCodegen(ObjectPool*, MemTracker* parent_mem_tracker, + const std::string& id, boost::scoped_ptr* codegen); /// Creates a LlvmCodeGen instance initialized with the module bitcode from 'file'. /// 'codegen' will contain the created object on success. - static Status CreateFromFile(ObjectPool*, const std::string& file, const std::string& id, + static Status CreateFromFile(ObjectPool*, MemTracker* parent_mem_tracker, + const std::string& file, const std::string& id, boost::scoped_ptr* codegen); /// Removes all jit compiled dynamically linked functions from the process. @@ -202,8 +213,8 @@ class LlvmCodeGen { /// If 'print_ir' is true, the generated llvm::Function's IR will be printed when /// GetIR() is called. Avoid doing so for IR function prototypes generated for /// externally defined native function. - llvm::Function* GeneratePrototype(LlvmBuilder* builder = NULL, - llvm::Value** params = NULL, bool print_ir = true); + llvm::Function* GeneratePrototype( + LlvmBuilder* builder = NULL, llvm::Value** params = NULL, bool print_ir = true); private: friend class LlvmCodeGen; @@ -266,7 +277,9 @@ class LlvmCodeGen { /// Optimize and compile the module. This should be called after all functions to JIT /// have been added to the module via AddFunctionToJit(). If optimizations_enabled_ is - /// false, the module will not be optimized before compilation. + /// false, the module will not be optimized before compilation. After FinalizeModule() + /// is called, the LLVM module is destroyed and it is invalid to call any LlvmCodegen + /// functions. Status FinalizeModule(); /// Replaces all instructions in 'caller' that call 'target_name' with a call @@ -357,6 +370,10 @@ class LlvmCodeGen { llvm::Function* GetFnvHashFunction(int num_bytes = -1); llvm::Function* GetMurmurHashFunction(int num_bytes = -1); + /// Set the NoInline attribute on 'function' and remove the AlwaysInline attribute if + /// present. + void SetNoInline(llvm::Function* function) const; + /// Allocate stack storage for local variables. This is similar to traditional c, where /// all the variables must be declared at the top of the function. This helper can be /// called from anywhere and will add a stack allocation for 'var' at the beginning of @@ -454,6 +471,15 @@ class LlvmCodeGen { /// this LlvmCodeGen object. The module must be on the local filesystem. Status LinkModule(const std::string& file); + /// If there are more than this number of expr trees (or functions that evaluate + /// expressions), avoid inlining avoid inlining for the exprs exceeding this threshold. + static const int CODEGEN_INLINE_EXPRS_THRESHOLD = 100; + + /// If there are more than this number of expr trees (or functions that evaluate + /// expressions), avoid inlining the function that evaluates the expression batch + /// into the calling function. + static const int CODEGEN_INLINE_EXPR_BATCH_THRESHOLD = 25; + private: friend class ExprCodegenTest; friend class LlvmCodeGenTest; @@ -471,11 +497,12 @@ class LlvmCodeGen { /// Parses all the global variables in 'module' and adds any functions referenced by /// them to the set 'ref_fns' if they are not defined in the Impalad native code. /// These functions need to be materialized to avoid linking error. - static void ParseGVForFunctions(llvm::Module* module, - boost::unordered_set* ref_fns); + static void ParseGVForFunctions( + llvm::Module* module, boost::unordered_set* ref_fns); /// Top level codegen object. 'module_id' is used for debugging when outputting the IR. - LlvmCodeGen(ObjectPool* pool, const std::string& module_id); + LlvmCodeGen( + ObjectPool* pool, MemTracker* parent_mem_tracker, const std::string& module_id); /// Initializes the jitter and execution engine with the given module. Status Init(std::unique_ptr module); @@ -484,8 +511,8 @@ class LlvmCodeGen { /// 'codegen' will contain the created object on success. Note that the functions /// are not materialized. Getting a reference to the function via GetFunction() /// will materialize the function and its callees recursively. - static Status CreateFromMemory(ObjectPool* pool, const std::string& id, - boost::scoped_ptr* codegen); + static Status CreateFromMemory(ObjectPool* pool, MemTracker* parent_mem_tracker, + const std::string& id, boost::scoped_ptr* codegen); /// Loads an LLVM module from 'file' which is the local path to the LLVM bitcode file. /// The functions in the module are not materialized. Getting a reference to the @@ -524,7 +551,7 @@ class LlvmCodeGen { void ResetVerification() { is_corrupt_ = false; } /// Optimizes the module. This includes pruning the module of any unused functions. - void OptimizeModule(); + Status OptimizeModule(); /// Clears generated hash fns. This is only used for testing. void ClearHashFns(); @@ -570,6 +597,10 @@ class LlvmCodeGen { /// there is error in materializing the module. Status FinalizeLazyMaterialization(); + /// Destroy the IR module, freeing memory used by the IR. Any machine code that was + /// generated is retained by the execution engine. + void DestroyModule(); + /// Whether InitializeLlvm() has been called. static bool llvm_initialized_; @@ -589,6 +620,10 @@ class LlvmCodeGen { /// Codegen counters RuntimeProfile profile_; + /// MemTracker used for tracking memory consumed by codegen. Connected to a parent + /// MemTracker if one was provided during initialization. + boost::scoped_ptr mem_tracker_; + /// Time spent reading the .ir file from the file system. RuntimeProfile::Counter* load_module_timer_; @@ -639,6 +674,9 @@ class LlvmCodeGen { /// Execution/Jitting engine. std::unique_ptr execution_engine_; + /// The memory manager used by 'execution_engine_'. Owned by 'execution_engine_'. + ImpalaMCJITMemoryManager* memory_manager_; + /// Functions parsed from pre-compiled module. Indexed by ImpalaIR::Function enum std::vector loaded_functions_; @@ -686,8 +724,28 @@ class LlvmCodeGen { /// 'symbol_emitter_' are called by 'execution_engine_' when code is emitted or freed. /// The lifetime of the symbol emitter must be longer than 'execution_engine_'. boost::scoped_ptr symbol_emitter_; -}; + /// Very rough estimate of memory in bytes that the IR and the intermediate data + /// structures used by the optimizer may consume per LLVM IR instruction to be + /// optimized (after dead code is removed). The number is chosen to avoid pathological + /// behaviour at either extreme: failing queries unnecessarily because the memory + /// estimate is too high versus having large amounts of untracked memory because the + /// estimate is too low. + /// + /// This was chosen by looking at the behaviour of TPC-H queries. Using the heap growth + /// profile from gperftools reveal that LLVM allocated ~9mb of memory for fragments with + /// ~17k total instructions in TPC-H Q2. Inspection of other TPC-H queries revealed + /// that a typical fragment from a TPC-H query is < 5,000 instructions, which translates + /// to 2.5MB, which is almost always lower than the runtime memory requirement of the + /// fragment - so we are unlikely to fail queries unnecessarily. + /// + /// This assumes optimizer memory usage scales linearly with instruction count. This is + /// true only if the size of functions is bounded, because some optimization passes + /// (e.g. global value numbering) use time and memory that is super-linear in relation + /// to the # of instructions in a function. So codegen should avoid generating + /// arbitrarily large function. + static constexpr int64_t ESTIMATED_OPTIMIZER_BYTES_PER_INST = 512; +}; } #endif diff --git a/be/src/codegen/mcjit-mem-mgr.h b/be/src/codegen/mcjit-mem-mgr.h index 313d17593..6982cef0d 100644 --- a/be/src/codegen/mcjit-mem-mgr.h +++ b/be/src/codegen/mcjit-mem-mgr.h @@ -25,19 +25,53 @@ extern void *__dso_handle __attribute__ ((__visibility__ ("hidden"))); namespace impala { -/// Custom memory manager to resolve references to __dso_handle in cross-compiled IR. +/// Custom memory manager. It is needed for a couple of purposes. +/// +/// We use it as a way to resolve references to __dso_handle in cross-compiled IR. /// This uses the same approach as the legacy llvm JIT to handle __dso_handle. MCJIT /// doesn't handle those for us: see LLVM issue 18062. /// TODO: get rid of this by purging the cross-compiled IR of references to __dso_handle, /// which come from global variables with destructors. +/// +/// We also use it to track how much memory is allocated for compiled code. class ImpalaMCJITMemoryManager : public llvm::SectionMemoryManager { public: + ImpalaMCJITMemoryManager() : bytes_allocated_(0), bytes_tracked_(0){}; + virtual uint64_t getSymbolAddress(const std::string& name) override { if (name == "__dso_handle") return reinterpret_cast(&__dso_handle); return SectionMemoryManager::getSymbolAddress(name); } -}; + virtual uint8_t* allocateCodeSection(uintptr_t size, unsigned alignment, + unsigned section_id, llvm::StringRef section_name) override { + bytes_allocated_ += size; + return llvm::SectionMemoryManager::allocateCodeSection( + size, alignment, section_id, section_name); + } + + virtual uint8_t* allocateDataSection(uintptr_t size, unsigned alignment, + unsigned section_id, llvm::StringRef section_name, bool is_read_only) override { + bytes_allocated_ += size; + return llvm::SectionMemoryManager::allocateDataSection( + size, alignment, section_id, section_name, is_read_only); + } + + int64_t bytes_allocated() const { return bytes_allocated_; } + int64_t bytes_tracked() const { return bytes_tracked_; } + void set_bytes_tracked(int64_t bytes_tracked) { + DCHECK_LE(bytes_tracked, bytes_allocated_); + bytes_tracked_ = bytes_tracked; + } + + private: + /// Total bytes allocated for the compiled code. + int64_t bytes_allocated_; + + /// Total bytes already tracked by MemTrackers. <= 'bytes_allocated_'. + /// Needed to release the correct amount from the MemTracker when done. + int64_t bytes_tracked_; +}; } #endif diff --git a/be/src/exec/aggregation-node.cc b/be/src/exec/aggregation-node.cc index f3b054e58..822fc5da6 100644 --- a/be/src/exec/aggregation-node.cc +++ b/be/src/exec/aggregation-node.cc @@ -164,6 +164,9 @@ Status AggregationNode::Prepare(RuntimeState* state) { void AggregationNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + bool codegen_enabled = false; LlvmCodeGen* codegen = state->codegen(); DCHECK(codegen != NULL); @@ -178,7 +181,6 @@ void AggregationNode::Codegen(RuntimeState* state) { } } runtime_profile()->AddCodegenMsg(codegen_enabled); - ExecNode::Codegen(state); } Status AggregationNode::Open(RuntimeState* state) { diff --git a/be/src/exec/exchange-node.cc b/be/src/exec/exchange-node.cc index 2b2b03383..0bbd42b04 100644 --- a/be/src/exec/exchange-node.cc +++ b/be/src/exec/exchange-node.cc @@ -92,11 +92,13 @@ Status ExchangeNode::Prepare(RuntimeState* state) { void ExchangeNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + if (is_merging_) { Status codegen_status = less_than_->Codegen(state); runtime_profile()->AddCodegenMsg(codegen_status.ok(), codegen_status); } - ExecNode::Codegen(state); } Status ExchangeNode::Open(RuntimeState* state) { diff --git a/be/src/exec/exec-node.cc b/be/src/exec/exec-node.cc index 95306ec97..518a0dd90 100644 --- a/be/src/exec/exec-node.cc +++ b/be/src/exec/exec-node.cc @@ -129,6 +129,7 @@ ExecNode::ExecNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl rows_returned_counter_(NULL), rows_returned_rate_(NULL), containing_subplan_(NULL), + disable_codegen_(tnode.disable_codegen), is_closed_(false) { InitRuntimeProfile(PrintPlanNodeType(tnode.node_type)); } @@ -465,6 +466,10 @@ void ExecNode::AddCodegenDisabledMessage(RuntimeState* state) { } } +bool ExecNode::IsNodeCodegenDisabled() const { + return disable_codegen_; +} + // Codegen for EvalConjuncts. The generated signature is // For a node with two conjunct predicates // define i1 @EvalConjuncts(%"class.impala::ExprContext"** %ctxs, i32 %num_ctxs, @@ -507,6 +512,10 @@ Status ExecNode::CodegenEvalConjuncts(LlvmCodeGen* codegen, for (int i = 0; i < conjunct_ctxs.size(); ++i) { RETURN_IF_ERROR( conjunct_ctxs[i]->root()->GetCodegendComputeFn(codegen, &conjunct_fns[i])); + if (i >= LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) { + // Avoid bloating EvalConjuncts by inlining everything into it. + codegen->SetNoInline(conjunct_fns[i]); + } } // Construct function signature to match @@ -566,12 +575,16 @@ Status ExecNode::CodegenEvalConjuncts(LlvmCodeGen* codegen, builder.CreateRet(codegen->true_value()); } + // Avoid inlining EvalConjuncts into caller if it is large. + if (conjunct_ctxs.size() > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(*fn); + } + *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { return Status("ExecNode::CodegenEvalConjuncts(): codegen'd EvalConjuncts() function " - "failed verification, see log"); + "failed verification, see log"); } return Status::OK(); } - } diff --git a/be/src/exec/exec-node.h b/be/src/exec/exec-node.h index 1dd2d3252..6a7737bbe 100644 --- a/be/src/exec/exec-node.h +++ b/be/src/exec/exec-node.h @@ -184,6 +184,10 @@ class ExecNode { MemTracker* mem_tracker() { return mem_tracker_.get(); } MemTracker* expr_mem_tracker() { return expr_mem_tracker_.get(); } + /// Return true if codegen was disabled by the planner for this ExecNode. Does not + /// check to see if codegen was enabled for the enclosing fragment. + bool IsNodeCodegenDisabled() const; + /// Add codegen disabled message if codegen is disabled for this ExecNode. void AddCodegenDisabledMessage(RuntimeState* state); @@ -276,6 +280,9 @@ class ExecNode { /// Valid to call in or after Prepare(). bool IsInSubplan() const { return containing_subplan_ != NULL; } + /// If true, codegen should be disabled for this exec node. + const bool disable_codegen_; + /// Create a single exec node derived from thrift node; place exec node in 'pool'. static Status CreateNode(ObjectPool* pool, const TPlanNode& tnode, const DescriptorTbl& descs, ExecNode** node, RuntimeState* state); diff --git a/be/src/exec/hash-join-node.cc b/be/src/exec/hash-join-node.cc index a18f35640..77807f6ce 100644 --- a/be/src/exec/hash-join-node.cc +++ b/be/src/exec/hash-join-node.cc @@ -134,14 +134,13 @@ Status HashJoinNode::Prepare(RuntimeState* state) { AddExprCtxsToFree(other_join_conjunct_ctxs_); // TODO: default buckets - const bool stores_nulls = join_op_ == TJoinOp::RIGHT_OUTER_JOIN || - join_op_ == TJoinOp::FULL_OUTER_JOIN || - std::accumulate(is_not_distinct_from_.begin(), is_not_distinct_from_.end(), false, - std::logical_or()); + const bool stores_nulls = join_op_ == TJoinOp::RIGHT_OUTER_JOIN + || join_op_ == TJoinOp::FULL_OUTER_JOIN + || std::accumulate(is_not_distinct_from_.begin(), is_not_distinct_from_.end(), + false, std::logical_or()); hash_tbl_.reset(new OldHashTable(state, build_expr_ctxs_, probe_expr_ctxs_, - filter_expr_ctxs_, - child(1)->row_desc().tuple_descriptors().size(), stores_nulls, - is_not_distinct_from_, state->fragment_hash_seed(), mem_tracker(), filters_)); + filter_expr_ctxs_, child(1)->row_desc().tuple_descriptors().size(), stores_nulls, + is_not_distinct_from_, state->fragment_hash_seed(), mem_tracker(), filters_)); build_pool_.reset(new MemPool(mem_tracker())); AddCodegenDisabledMessage(state); return Status::OK(); @@ -149,6 +148,9 @@ Status HashJoinNode::Prepare(RuntimeState* state) { void HashJoinNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + LlvmCodeGen* codegen = state->codegen(); DCHECK(codegen != NULL); bool build_codegen_enabled = false; @@ -178,7 +180,6 @@ void HashJoinNode::Codegen(RuntimeState* state) { } runtime_profile()->AddCodegenMsg(build_codegen_enabled, "", "Build Side"); runtime_profile()->AddCodegenMsg(probe_codegen_enabled, "", "Probe Side"); - ExecNode::Codegen(state); } Status HashJoinNode::Reset(RuntimeState* state) { diff --git a/be/src/exec/hash-table.cc b/be/src/exec/hash-table.cc index 8d45ec7bb..f4ecb67b9 100644 --- a/be/src/exec/hash-table.cc +++ b/be/src/exec/hash-table.cc @@ -755,13 +755,18 @@ Status HashTableCtx::CodegenEvalRow(LlvmCodeGen* codegen, bool build, Function** if (!status.ok()) { (*fn)->eraseFromParent(); // deletes function *fn = NULL; - return Status(Substitute("Problem with HashTableCtx::CodegenEvalRow(): $0", - status.GetDetail())); + return Status(Substitute( + "Problem with HashTableCtx::CodegenEvalRow(): $0", status.GetDetail())); } - Value* get_expr_ctx_args[] = { this_ptr, codegen->GetIntConstant(TYPE_INT, i) }; + // Avoid bloating function by inlining too many exprs into it. + if (i >= LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) { + codegen->SetNoInline(expr_fn); + } + + Value* get_expr_ctx_args[] = {this_ptr, codegen->GetIntConstant(TYPE_INT, i)}; Value* ctx_arg = builder.CreateCall(get_expr_ctx_fn, get_expr_ctx_args, "expr_ctx"); - Value* expr_fn_args[] = { ctx_arg, row }; + Value* expr_fn_args[] = {ctx_arg, row}; CodegenAnyVal result = CodegenAnyVal::CreateCallWrapped( codegen, &builder, ctxs[i]->root()->type(), expr_fn, expr_fn_args, "result"); Value* is_null = result.GetIsNull(); @@ -800,10 +805,15 @@ Status HashTableCtx::CodegenEvalRow(LlvmCodeGen* codegen, bool build, Function** } builder.CreateRet(has_null); + // Avoid inlining a large EvalRow() function into caller. + if (ctxs.size() > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(*fn); + } + *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { return Status("Codegen'd HashTableCtx::EvalRow() function failed verification, " - "see log"); + "see log"); } return Status::OK(); } @@ -972,6 +982,11 @@ Status HashTableCtx::CodegenHashRow(LlvmCodeGen* codegen, bool use_murmur, Funct } builder.CreateRet(hash_result); + + // Avoid inlining into caller if there are many exprs. + if (build_expr_ctxs_.size() > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(*fn); + } *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { return Status( @@ -1095,12 +1110,16 @@ Status HashTableCtx::CodegenEquals(LlvmCodeGen* codegen, bool force_null_equalit if (!status.ok()) { (*fn)->eraseFromParent(); // deletes function *fn = NULL; - return Status(Substitute("Problem with HashTableCtx::CodegenEquals: $0", - status.GetDetail())); + return Status( + Substitute("Problem with HashTableCtx::CodegenEquals: $0", status.GetDetail())); + } + if (build_expr_ctxs_.size() > LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) { + // Avoid bloating function by inlining too many exprs into it. + codegen->SetNoInline(expr_fn); } // Load ExprContext* from 'build_expr_ctxs_'. - Value* get_expr_ctx_args[] = { this_ptr, codegen->GetIntConstant(TYPE_INT, i) }; + Value* get_expr_ctx_args[] = {this_ptr, codegen->GetIntConstant(TYPE_INT, i)}; Value* ctx_arg = builder.CreateCall(get_expr_ctx_fn, get_expr_ctx_args, "expr_ctx"); // Evaluate the expression. @@ -1156,10 +1175,14 @@ Status HashTableCtx::CodegenEquals(LlvmCodeGen* codegen, bool force_null_equalit builder.SetInsertPoint(false_block); builder.CreateRet(codegen->false_value()); + // Avoid inlining into caller if it is large. + if (build_expr_ctxs_.size() > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(*fn); + } *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { return Status("Codegen'd HashTableCtx::Equals() function failed verification, " - "see log"); + "see log"); } return Status::OK(); } diff --git a/be/src/exec/hdfs-scan-node-base.cc b/be/src/exec/hdfs-scan-node-base.cc index e616fafe0..5b97e76f9 100644 --- a/be/src/exec/hdfs-scan-node-base.cc +++ b/be/src/exec/hdfs-scan-node-base.cc @@ -297,6 +297,10 @@ Status HdfsScanNodeBase::Prepare(RuntimeState* state) { } void HdfsScanNodeBase::Codegen(RuntimeState* state) { + DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + // Create codegen'd functions for (int format = THdfsFileFormat::TEXT; format <= THdfsFileFormat::PARQUET; ++format) { vector& file_descs = @@ -343,7 +347,6 @@ void HdfsScanNodeBase::Codegen(RuntimeState* state) { } runtime_profile()->AddCodegenMsg(status.ok(), status, format_name); } - ExecNode::Codegen(state); } Status HdfsScanNodeBase::Open(RuntimeState* state) { diff --git a/be/src/exec/hdfs-scanner.cc b/be/src/exec/hdfs-scanner.cc index 390085fc2..c6a5f3729 100644 --- a/be/src/exec/hdfs-scanner.cc +++ b/be/src/exec/hdfs-scanner.cc @@ -332,6 +332,7 @@ Status HdfsScanner::CodegenWriteCompleteTuple(HdfsScanNodeBase* node, node->hdfs_table()->null_column_value().data(), node->hdfs_table()->null_column_value().size(), true, state->strict_mode()); if (fn == NULL) return Status("CodegenWriteSlot failed."); + if (i >= LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) codegen->SetNoInline(fn); slot_fns.push_back(fn); } @@ -487,6 +488,10 @@ Status HdfsScanner::CodegenWriteCompleteTuple(HdfsScanNodeBase* node, fn->eraseFromParent(); return status; } + if (node->materialized_slots().size() + conjunct_idx + >= LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) { + codegen->SetNoInline(conjunct_fn); + } Function* get_ctx_fn = codegen->GetFunction(IRFunction::HDFS_SCANNER_GET_CONJUNCT_CTX, false); @@ -505,6 +510,10 @@ Status HdfsScanner::CodegenWriteCompleteTuple(HdfsScanNodeBase* node, builder.SetInsertPoint(eval_fail_block); builder.CreateRet(codegen->false_value()); + if (node->materialized_slots().size() + conjunct_ctxs.size() + > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(fn); + } *write_complete_tuple_fn = codegen->FinalizeFunction(fn); if (*write_complete_tuple_fn == NULL) { return Status("Failed to finalize write_complete_tuple_fn."); diff --git a/be/src/exec/partitioned-aggregation-node.cc b/be/src/exec/partitioned-aggregation-node.cc index 01622b9ee..6cc36bece 100644 --- a/be/src/exec/partitioned-aggregation-node.cc +++ b/be/src/exec/partitioned-aggregation-node.cc @@ -281,14 +281,16 @@ Status PartitionedAggregationNode::Prepare(RuntimeState* state) { void PartitionedAggregationNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + LlvmCodeGen* codegen = state->codegen(); DCHECK(codegen != NULL); TPrefetchMode::type prefetch_mode = state_->query_options().prefetch_mode; - Status codegen_status = - is_streaming_preagg_ ? CodegenProcessBatchStreaming(codegen, prefetch_mode) : - CodegenProcessBatch(codegen, prefetch_mode); + Status codegen_status = is_streaming_preagg_ ? + CodegenProcessBatchStreaming(codegen, prefetch_mode) : + CodegenProcessBatch(codegen, prefetch_mode); runtime_profile()->AddCodegenMsg(codegen_status.ok(), codegen_status); - ExecNode::Codegen(state); } Status PartitionedAggregationNode::Open(RuntimeState* state) { @@ -1553,7 +1555,8 @@ Status PartitionedAggregationNode::QueryMaintenance(RuntimeState* state) { // } // Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen, - AggFnEvaluator* evaluator, SlotDescriptor* slot_desc, Function** fn) { + AggFnEvaluator* evaluator, int evaluator_idx, SlotDescriptor* slot_desc, + Function** fn) { PointerType* fn_ctx_type = codegen->GetPtrType(FunctionContextImpl::LLVM_FUNCTIONCONTEXT_NAME); PointerType* expr_ctxs_type = @@ -1697,10 +1700,18 @@ Status PartitionedAggregationNode::CodegenUpdateSlot(LlvmCodeGen* codegen, builder.SetInsertPoint(ret_block); builder.CreateRetVoid(); + // Avoid producing huge UpdateTuple() function after inlining - LLVM's optimiser + // memory/CPU usage scales super-linearly with function size. + // E.g. compute stats on all columns of a 1000-column table previously took 4 minutes to + // codegen because all the UpdateSlot() functions were inlined. + if (evaluator_idx >= LlvmCodeGen::CODEGEN_INLINE_EXPRS_THRESHOLD) { + codegen->SetNoInline(*fn); + } + *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { return Status("PartitionedAggregationNode::CodegenUpdateSlot(): codegen'd " - "UpdateSlot() function failed verification, see log"); + "UpdateSlot() function failed verification, see log"); } return Status::OK(); } @@ -1874,7 +1885,8 @@ Status PartitionedAggregationNode::CodegenUpdateTuple( builder.CreateStore(count_inc, slot_ptr); } else { Function* update_slot_fn; - RETURN_IF_ERROR(CodegenUpdateSlot(codegen, evaluator, slot_desc, &update_slot_fn)); + RETURN_IF_ERROR( + CodegenUpdateSlot(codegen, evaluator, i, slot_desc, &update_slot_fn)); Value* agg_fn_ctx_ptr = builder.CreateConstGEP1_32(agg_fn_ctxs_arg, i); Value* agg_fn_ctx = builder.CreateLoad(agg_fn_ctx_ptr, "agg_fn_ctx"); // Call GetExprCtx() to get the expression context. @@ -1887,6 +1899,12 @@ Status PartitionedAggregationNode::CodegenUpdateTuple( } builder.CreateRetVoid(); + // Avoid inlining big UpdateTuple function into outer loop - we're unlikely to get + // any benefit from it since the function call overhead will be amortized. + if (aggregate_evaluators_.size() > LlvmCodeGen::CODEGEN_INLINE_EXPR_BATCH_THRESHOLD) { + codegen->SetNoInline(*fn); + } + // CodegenProcessBatch() does the final optimizations. *fn = codegen->FinalizeFunction(*fn); if (*fn == NULL) { diff --git a/be/src/exec/partitioned-aggregation-node.h b/be/src/exec/partitioned-aggregation-node.h index 4287ceaf0..54840c74c 100644 --- a/be/src/exec/partitioned-aggregation-node.h +++ b/be/src/exec/partitioned-aggregation-node.h @@ -643,7 +643,7 @@ class PartitionedAggregationNode : public ExecNode { /// Codegen UpdateSlot(). Returns non-OK status if codegen is unsuccessful. /// Assumes is_merge = false; Status CodegenUpdateSlot(LlvmCodeGen* codegen, AggFnEvaluator* evaluator, - SlotDescriptor* slot_desc, llvm::Function** fn); + int evaluator_idx, SlotDescriptor* slot_desc, llvm::Function** fn); /// Codegen a call to a function implementing the UDA interface with input values /// from 'input_vals'. 'dst_val' should contain the previous value of the aggregate diff --git a/be/src/exec/partitioned-hash-join-node.cc b/be/src/exec/partitioned-hash-join-node.cc index 61b7eb7e1..607348629 100644 --- a/be/src/exec/partitioned-hash-join-node.cc +++ b/be/src/exec/partitioned-hash-join-node.cc @@ -141,6 +141,10 @@ Status PartitionedHashJoinNode::Prepare(RuntimeState* state) { void PartitionedHashJoinNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + // Codegen the children node; + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + LlvmCodeGen* codegen = state->codegen(); DCHECK(codegen != NULL); @@ -150,11 +154,8 @@ void PartitionedHashJoinNode::Codegen(RuntimeState* state) { // Codegen the probe side. TPrefetchMode::type prefetch_mode = state->query_options().prefetch_mode; Status probe_codegen_status = CodegenProcessProbeBatch(codegen, prefetch_mode); - runtime_profile()->AddCodegenMsg(probe_codegen_status.ok(), probe_codegen_status, - "Probe Side"); - - // Codegen the children node; - ExecNode::Codegen(state); + runtime_profile()->AddCodegenMsg( + probe_codegen_status.ok(), probe_codegen_status, "Probe Side"); } Status PartitionedHashJoinNode::Open(RuntimeState* state) { diff --git a/be/src/exec/sort-node.cc b/be/src/exec/sort-node.cc index 751822337..1a8de8349 100644 --- a/be/src/exec/sort-node.cc +++ b/be/src/exec/sort-node.cc @@ -51,8 +51,9 @@ Status SortNode::Prepare(RuntimeState* state) { state, child(0)->row_desc(), row_descriptor_, expr_mem_tracker())); AddExprCtxsToFree(sort_exec_exprs_); less_than_.reset(new TupleRowComparator(sort_exec_exprs_, is_asc_order_, nulls_first_)); - sorter_.reset(new Sorter(*less_than_.get(), sort_exec_exprs_.sort_tuple_slot_expr_ctxs(), - &row_descriptor_, mem_tracker(), runtime_profile(), state)); + sorter_.reset( + new Sorter(*less_than_.get(), sort_exec_exprs_.sort_tuple_slot_expr_ctxs(), + &row_descriptor_, mem_tracker(), runtime_profile(), state)); RETURN_IF_ERROR(sorter_->Init()); AddCodegenDisabledMessage(state); return Status::OK(); @@ -60,9 +61,11 @@ Status SortNode::Prepare(RuntimeState* state) { void SortNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + Status codegen_status = less_than_->Codegen(state); runtime_profile()->AddCodegenMsg(codegen_status.ok(), codegen_status); - ExecNode::Codegen(state); } Status SortNode::Open(RuntimeState* state) { diff --git a/be/src/exec/topn-node.cc b/be/src/exec/topn-node.cc index bd4caec47..a7f11bdf6 100644 --- a/be/src/exec/topn-node.cc +++ b/be/src/exec/topn-node.cc @@ -84,6 +84,9 @@ Status TopNNode::Prepare(RuntimeState* state) { void TopNNode::Codegen(RuntimeState* state) { DCHECK(state->ShouldCodegen()); + ExecNode::Codegen(state); + if (IsNodeCodegenDisabled()) return; + LlvmCodeGen* codegen = state->codegen(); DCHECK(codegen != NULL); @@ -126,7 +129,6 @@ void TopNNode::Codegen(RuntimeState* state) { } } runtime_profile()->AddCodegenMsg(codegen_status.ok(), codegen_status); - ExecNode::Codegen(state); } Status TopNNode::Open(RuntimeState* state) { diff --git a/be/src/exprs/aggregate-functions-ir.cc b/be/src/exprs/aggregate-functions-ir.cc index 7ed638634..f6a91f454 100644 --- a/be/src/exprs/aggregate-functions-ir.cc +++ b/be/src/exprs/aggregate-functions-ir.cc @@ -1190,8 +1190,26 @@ void AggregateFunctions::HllUpdate(FunctionContext* ctx, const T& src, StringVal } } -void AggregateFunctions::HllMerge(FunctionContext* ctx, const StringVal& src, - StringVal* dst) { +// Specialize for DecimalVal to allow substituting decimal size. +template <> +void AggregateFunctions::HllUpdate( + FunctionContext* ctx, const DecimalVal& src, StringVal* dst) { + if (src.is_null) return; + DCHECK(!dst->is_null); + DCHECK_EQ(dst->len, HLL_LEN); + uint64_t hash_value = AnyValUtil::HashDecimal64( + src, Expr::GetConstantInt(*ctx, Expr::ARG_TYPE_SIZE, 0), HashUtil::FNV64_SEED); + if (hash_value != 0) { + // Use the lower bits to index into the number of streams and then + // find the first 1 bit after the index bits. + int idx = hash_value & (HLL_LEN - 1); + uint8_t first_one_bit = __builtin_ctzl(hash_value >> HLL_PRECISION) + 1; + dst->ptr[idx] = ::max(dst->ptr[idx], first_one_bit); + } +} + +void AggregateFunctions::HllMerge( + FunctionContext* ctx, const StringVal& src, StringVal* dst) { DCHECK(!dst->is_null); DCHECK(!src.is_null); DCHECK_EQ(dst->len, HLL_LEN); diff --git a/be/src/exprs/anyval-util.h b/be/src/exprs/anyval-util.h index 8c7524734..470326650 100644 --- a/be/src/exprs/anyval-util.h +++ b/be/src/exprs/anyval-util.h @@ -133,12 +133,19 @@ class AnyValUtil { return HashUtil::MurmurHash2_64(&tv, 12, seed); } - static uint64_t Hash64(const DecimalVal& v, const FunctionContext::TypeDesc& t, - int64_t seed) { - switch (ColumnType::GetDecimalByteSize(t.precision)) { - case 4: return HashUtil::MurmurHash2_64(&v.val4, 4, seed); - case 8: return HashUtil::MurmurHash2_64(&v.val8, 8, seed); - case 16: return HashUtil::MurmurHash2_64(&v.val16, 16, seed); + static uint64_t Hash64( + const DecimalVal& v, const FunctionContext::TypeDesc& t, int64_t seed) { + return HashDecimal64(v, ColumnType::GetDecimalByteSize(t.precision), seed); + } + + static uint64_t HashDecimal64(const DecimalVal& v, int byte_size, int64_t seed) { + switch (byte_size) { + case 4: + return HashUtil::MurmurHash2_64(&v.val4, 4, seed); + case 8: + return HashUtil::MurmurHash2_64(&v.val8, 8, seed); + case 16: + return HashUtil::MurmurHash2_64(&v.val16, 16, seed); default: DCHECK(false); return 0; diff --git a/be/src/exprs/expr-codegen-test.cc b/be/src/exprs/expr-codegen-test.cc index 6ccbbf43a..cbb56176a 100644 --- a/be/src/exprs/expr-codegen-test.cc +++ b/be/src/exprs/expr-codegen-test.cc @@ -251,7 +251,8 @@ TEST_F(ExprCodegenTest, TestInlineConstants) { stringstream test_udf_file; test_udf_file << getenv("IMPALA_HOME") << "/be/build/latest/exprs/expr-codegen-test.ll"; scoped_ptr codegen; - ASSERT_OK(LlvmCodeGen::CreateFromFile(&pool, test_udf_file.str(), "test", &codegen)); + ASSERT_OK( + LlvmCodeGen::CreateFromFile(&pool, NULL, test_udf_file.str(), "test", &codegen)); Function* fn = codegen->GetFunction(TEST_GET_CONSTANT_SYMBOL, false); ASSERT_TRUE(fn != NULL); diff --git a/be/src/exprs/expr.h b/be/src/exprs/expr.h index 552fee91d..942eec0db 100644 --- a/be/src/exprs/expr.h +++ b/be/src/exprs/expr.h @@ -361,6 +361,7 @@ class Expr { /// recognize if this node is a slotref in order to speed up GetValue() const bool is_slotref_; + /// analysis is done, types are fixed at this point const ColumnType type_; std::vector children_; diff --git a/be/src/runtime/lib-cache.cc b/be/src/runtime/lib-cache.cc index fe8d18098..00c1159a1 100644 --- a/be/src/runtime/lib-cache.cc +++ b/be/src/runtime/lib-cache.cc @@ -403,8 +403,8 @@ Status LibCache::GetCacheEntryInternal(const string& hdfs_lib_file, LibType type ObjectPool pool; scoped_ptr codegen; string module_id = filesystem::path((*entry)->local_path).stem().string(); - RETURN_IF_ERROR( - LlvmCodeGen::CreateFromFile(&pool, (*entry)->local_path, module_id, &codegen)); + RETURN_IF_ERROR(LlvmCodeGen::CreateFromFile( + &pool, NULL, (*entry)->local_path, module_id, &codegen)); codegen->GetSymbols(&(*entry)->symbols); } else { DCHECK_EQ(type, TYPE_JAR); diff --git a/be/src/runtime/runtime-state.cc b/be/src/runtime/runtime-state.cc index 40a494698..37d5fb0a9 100644 --- a/be/src/runtime/runtime-state.cc +++ b/be/src/runtime/runtime-state.cc @@ -96,16 +96,16 @@ RuntimeState::RuntimeState(const TQueryCtx& query_ctx) RuntimeState::~RuntimeState() { block_mgr_.reset(); + // Release codegen memory before tearing down trackers. + codegen_.reset(); + // query_mem_tracker_ must be valid as long as instance_mem_tracker_ is so // delete instance_mem_tracker_ first. // LogUsage() walks the MemTracker tree top-down when the memory limit is exceeded. // Break the link between the instance_mem_tracker and its parent (query_mem_tracker_) // before the instance_mem_tracker_ and its children are destroyed. - if (instance_mem_tracker_.get() != NULL) { - // May be NULL if InitMemTrackers() is not called, for example from tests. - instance_mem_tracker_->UnregisterFromParent(); - } - + // May be NULL if InitMemTrackers() is not called, for example from tests. + if (instance_mem_tracker_ != NULL) instance_mem_tracker_->UnregisterFromParent(); instance_mem_tracker_.reset(); query_mem_tracker_.reset(); } @@ -184,8 +184,8 @@ Status RuntimeState::CreateBlockMgr() { Status RuntimeState::CreateCodegen() { if (codegen_.get() != NULL) return Status::OK(); // TODO: add the fragment ID to the codegen ID as well - RETURN_IF_ERROR(LlvmCodeGen::CreateImpalaCodegen( - obj_pool_.get(), PrintId(fragment_instance_id()), &codegen_)); + RETURN_IF_ERROR(LlvmCodeGen::CreateImpalaCodegen(obj_pool_.get(), + instance_mem_tracker_.get(), PrintId(fragment_instance_id()), &codegen_)); codegen_->EnableOptimizations(true); profile_.AddChild(codegen_->runtime_profile()); return Status::OK(); diff --git a/common/thrift/PlanNodes.thrift b/common/thrift/PlanNodes.thrift index 6f863b26f..233fbb2c5 100644 --- a/common/thrift/PlanNodes.thrift +++ b/common/thrift/PlanNodes.thrift @@ -463,32 +463,36 @@ struct TPlanNode { 6: required list nullable_tuples 7: optional list conjuncts + // Set to true if codegen should be disabled for this plan node. Otherwise the plan + // node is codegen'd if the backend supports it. + 8: required bool disable_codegen + // one field per PlanNode subclass - 8: optional THdfsScanNode hdfs_scan_node - 9: optional THBaseScanNode hbase_scan_node - 23: optional TKuduScanNode kudu_scan_node - 10: optional TDataSourceScanNode data_source_node - 11: optional THashJoinNode hash_join_node - 12: optional TNestedLoopJoinNode nested_loop_join_node - 13: optional TAggregationNode agg_node - 14: optional TSortNode sort_node - 15: optional TUnionNode union_node - 16: optional TExchangeNode exchange_node - 17: optional TAnalyticNode analytic_node - 21: optional TUnnestNode unnest_node + 9: optional THdfsScanNode hdfs_scan_node + 10: optional THBaseScanNode hbase_scan_node + 11: optional TKuduScanNode kudu_scan_node + 12: optional TDataSourceScanNode data_source_node + 13: optional THashJoinNode hash_join_node + 14: optional TNestedLoopJoinNode nested_loop_join_node + 15: optional TAggregationNode agg_node + 16: optional TSortNode sort_node + 17: optional TUnionNode union_node + 18: optional TExchangeNode exchange_node + 19: optional TAnalyticNode analytic_node + 20: optional TUnnestNode unnest_node // Label that should be used to print this node to the user. - 18: optional string label + 21: optional string label // Additional details that should be printed to the user. This is node specific // e.g. table name, join strategy, etc. - 19: optional string label_detail + 22: optional string label_detail // Estimated execution stats generated by the planner. - 20: optional ExecStats.TExecStats estimated_stats + 23: optional ExecStats.TExecStats estimated_stats // Runtime filters assigned to this plan node - 22: optional list runtime_filters + 24: optional list runtime_filters } // A flattened representation of a tree of PlanNodes, obtained by depth-first diff --git a/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java b/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java index 620ea5608..24d7caadb 100644 --- a/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java +++ b/fe/src/main/java/org/apache/impala/planner/DistributedPlanner.java @@ -818,6 +818,12 @@ public class DistributedPlanner { mergeFragment.getPlanRoot(), node.getAggInfo().getMergeAggInfo()); mergeAggNode.init(ctx_.getRootAnalyzer()); mergeAggNode.setLimit(limit); + // Merge of non-grouping agg only processes one tuple per Impala daemon - codegen + // will cost more than benefit. + if (!hasGrouping) { + mergeFragment.getPlanRoot().setDisableCodegen(true); + mergeAggNode.setDisableCodegen(true); + } // HAVING predicates can only be evaluated after the merge agg step node.transferConjuncts(mergeAggNode); diff --git a/fe/src/main/java/org/apache/impala/planner/PlanNode.java b/fe/src/main/java/org/apache/impala/planner/PlanNode.java index 141464e0a..312ea5bde 100644 --- a/fe/src/main/java/org/apache/impala/planner/PlanNode.java +++ b/fe/src/main/java/org/apache/impala/planner/PlanNode.java @@ -120,6 +120,9 @@ abstract public class PlanNode extends TreeNode { // set in computeCosts(); invalid: -1 protected long perHostMemCost_ = -1; + // If true, disable codegen for this plan node. + protected boolean disableCodegen_; + // Runtime filters assigned to this node. protected List runtimeFilters_ = Lists.newArrayList(); @@ -144,6 +147,7 @@ abstract public class PlanNode extends TreeNode { cardinality_ = -1; numNodes_ = -1; displayName_ = displayName; + disableCodegen_ = false; } /** @@ -159,6 +163,7 @@ abstract public class PlanNode extends TreeNode { cardinality_ = -1; numNodes_ = -1; displayName_ = displayName; + disableCodegen_ = node.disableCodegen_; } /** @@ -393,9 +398,10 @@ abstract public class PlanNode extends TreeNode { msg.addToConjuncts(e.treeToThrift()); } // Serialize any runtime filters - for (RuntimeFilter filter: runtimeFilters_) { + for (RuntimeFilter filter : runtimeFilters_) { msg.addToRuntime_filters(filter.toThrift()); } + msg.setDisable_codegen(disableCodegen_); toThrift(msg); container.addToNodes(msg); // For the purpose of the BE consider ExchangeNodes to have no children. @@ -718,4 +724,8 @@ abstract public class PlanNode extends TreeNode { return sortedConjuncts; } + + public void setDisableCodegen(boolean disableCodegen) { + disableCodegen_ = disableCodegen; + } } diff --git a/testdata/workloads/functional-query/queries/QueryTest/compute-stats.test b/testdata/workloads/functional-query/queries/QueryTest/compute-stats.test index 817dd4d2f..b741c5acc 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/compute-stats.test +++ b/testdata/workloads/functional-query/queries/QueryTest/compute-stats.test @@ -988,3 +988,1019 @@ show column stats alltypes_no_coldef ---- TYPES STRING, STRING, BIGINT, BIGINT, INT, DOUBLE ==== +---- QUERY +# Test that compute stats works on wide tables. +create table widetable_1000_cols +stored as parquet +as +select * from functional_parquet.widetable_1000_cols +==== +---- QUERY +compute stats widetable_1000_cols +==== +---- QUERY +show column stats widetable_1000_cols +---- RESULTS +'bool_col1','BOOLEAN',2,-1,1,1 +'tinyint_col1','TINYINT',5,-1,1,1 +'smallint_col1','SMALLINT',5,-1,2,2 +'int_col1','INT',5,-1,4,4 +'bigint_col1','BIGINT',5,-1,8,8 +'float_col1','FLOAT',5,-1,4,4 +'double_col1','DOUBLE',5,-1,8,8 +'string_col1','STRING',5,-1,1,1 +'bool_col2','BOOLEAN',2,-1,1,1 +'tinyint_col2','TINYINT',5,-1,1,1 +'smallint_col2','SMALLINT',5,-1,2,2 +'int_col2','INT',5,-1,4,4 +'bigint_col2','BIGINT',5,-1,8,8 +'float_col2','FLOAT',5,-1,4,4 +'double_col2','DOUBLE',5,-1,8,8 +'string_col2','STRING',5,-1,1,1 +'bool_col3','BOOLEAN',2,-1,1,1 +'tinyint_col3','TINYINT',5,-1,1,1 +'smallint_col3','SMALLINT',5,-1,2,2 +'int_col3','INT',5,-1,4,4 +'bigint_col3','BIGINT',5,-1,8,8 +'float_col3','FLOAT',5,-1,4,4 +'double_col3','DOUBLE',5,-1,8,8 +'string_col3','STRING',5,-1,1,1 +'bool_col4','BOOLEAN',2,-1,1,1 +'tinyint_col4','TINYINT',5,-1,1,1 +'smallint_col4','SMALLINT',5,-1,2,2 +'int_col4','INT',5,-1,4,4 +'bigint_col4','BIGINT',5,-1,8,8 +'float_col4','FLOAT',5,-1,4,4 +'double_col4','DOUBLE',5,-1,8,8 +'string_col4','STRING',5,-1,1,1 +'bool_col5','BOOLEAN',2,-1,1,1 +'tinyint_col5','TINYINT',5,-1,1,1 +'smallint_col5','SMALLINT',5,-1,2,2 +'int_col5','INT',5,-1,4,4 +'bigint_col5','BIGINT',5,-1,8,8 +'float_col5','FLOAT',5,-1,4,4 +'double_col5','DOUBLE',5,-1,8,8 +'string_col5','STRING',5,-1,1,1 +'bool_col6','BOOLEAN',2,-1,1,1 +'tinyint_col6','TINYINT',5,-1,1,1 +'smallint_col6','SMALLINT',5,-1,2,2 +'int_col6','INT',5,-1,4,4 +'bigint_col6','BIGINT',5,-1,8,8 +'float_col6','FLOAT',5,-1,4,4 +'double_col6','DOUBLE',5,-1,8,8 +'string_col6','STRING',5,-1,1,1 +'bool_col7','BOOLEAN',2,-1,1,1 +'tinyint_col7','TINYINT',5,-1,1,1 +'smallint_col7','SMALLINT',5,-1,2,2 +'int_col7','INT',5,-1,4,4 +'bigint_col7','BIGINT',5,-1,8,8 +'float_col7','FLOAT',5,-1,4,4 +'double_col7','DOUBLE',5,-1,8,8 +'string_col7','STRING',5,-1,1,1 +'bool_col8','BOOLEAN',2,-1,1,1 +'tinyint_col8','TINYINT',5,-1,1,1 +'smallint_col8','SMALLINT',5,-1,2,2 +'int_col8','INT',5,-1,4,4 +'bigint_col8','BIGINT',5,-1,8,8 +'float_col8','FLOAT',5,-1,4,4 +'double_col8','DOUBLE',5,-1,8,8 +'string_col8','STRING',5,-1,1,1 +'bool_col9','BOOLEAN',2,-1,1,1 +'tinyint_col9','TINYINT',5,-1,1,1 +'smallint_col9','SMALLINT',5,-1,2,2 +'int_col9','INT',5,-1,4,4 +'bigint_col9','BIGINT',5,-1,8,8 +'float_col9','FLOAT',5,-1,4,4 +'double_col9','DOUBLE',5,-1,8,8 +'string_col9','STRING',5,-1,1,1 +'bool_col10','BOOLEAN',2,-1,1,1 +'tinyint_col10','TINYINT',5,-1,1,1 +'smallint_col10','SMALLINT',5,-1,2,2 +'int_col10','INT',5,-1,4,4 +'bigint_col10','BIGINT',5,-1,8,8 +'float_col10','FLOAT',5,-1,4,4 +'double_col10','DOUBLE',5,-1,8,8 +'string_col10','STRING',5,-1,1,1 +'bool_col11','BOOLEAN',2,-1,1,1 +'tinyint_col11','TINYINT',5,-1,1,1 +'smallint_col11','SMALLINT',5,-1,2,2 +'int_col11','INT',5,-1,4,4 +'bigint_col11','BIGINT',5,-1,8,8 +'float_col11','FLOAT',5,-1,4,4 +'double_col11','DOUBLE',5,-1,8,8 +'string_col11','STRING',5,-1,1,1 +'bool_col12','BOOLEAN',2,-1,1,1 +'tinyint_col12','TINYINT',5,-1,1,1 +'smallint_col12','SMALLINT',5,-1,2,2 +'int_col12','INT',5,-1,4,4 +'bigint_col12','BIGINT',5,-1,8,8 +'float_col12','FLOAT',5,-1,4,4 +'double_col12','DOUBLE',5,-1,8,8 +'string_col12','STRING',5,-1,1,1 +'bool_col13','BOOLEAN',2,-1,1,1 +'tinyint_col13','TINYINT',5,-1,1,1 +'smallint_col13','SMALLINT',5,-1,2,2 +'int_col13','INT',5,-1,4,4 +'bigint_col13','BIGINT',5,-1,8,8 +'float_col13','FLOAT',5,-1,4,4 +'double_col13','DOUBLE',5,-1,8,8 +'string_col13','STRING',5,-1,1,1 +'bool_col14','BOOLEAN',2,-1,1,1 +'tinyint_col14','TINYINT',5,-1,1,1 +'smallint_col14','SMALLINT',5,-1,2,2 +'int_col14','INT',5,-1,4,4 +'bigint_col14','BIGINT',5,-1,8,8 +'float_col14','FLOAT',5,-1,4,4 +'double_col14','DOUBLE',5,-1,8,8 +'string_col14','STRING',5,-1,1,1 +'bool_col15','BOOLEAN',2,-1,1,1 +'tinyint_col15','TINYINT',5,-1,1,1 +'smallint_col15','SMALLINT',5,-1,2,2 +'int_col15','INT',5,-1,4,4 +'bigint_col15','BIGINT',5,-1,8,8 +'float_col15','FLOAT',5,-1,4,4 +'double_col15','DOUBLE',5,-1,8,8 +'string_col15','STRING',5,-1,1,1 +'bool_col16','BOOLEAN',2,-1,1,1 +'tinyint_col16','TINYINT',5,-1,1,1 +'smallint_col16','SMALLINT',5,-1,2,2 +'int_col16','INT',5,-1,4,4 +'bigint_col16','BIGINT',5,-1,8,8 +'float_col16','FLOAT',5,-1,4,4 +'double_col16','DOUBLE',5,-1,8,8 +'string_col16','STRING',5,-1,1,1 +'bool_col17','BOOLEAN',2,-1,1,1 +'tinyint_col17','TINYINT',5,-1,1,1 +'smallint_col17','SMALLINT',5,-1,2,2 +'int_col17','INT',5,-1,4,4 +'bigint_col17','BIGINT',5,-1,8,8 +'float_col17','FLOAT',5,-1,4,4 +'double_col17','DOUBLE',5,-1,8,8 +'string_col17','STRING',5,-1,1,1 +'bool_col18','BOOLEAN',2,-1,1,1 +'tinyint_col18','TINYINT',5,-1,1,1 +'smallint_col18','SMALLINT',5,-1,2,2 +'int_col18','INT',5,-1,4,4 +'bigint_col18','BIGINT',5,-1,8,8 +'float_col18','FLOAT',5,-1,4,4 +'double_col18','DOUBLE',5,-1,8,8 +'string_col18','STRING',5,-1,1,1 +'bool_col19','BOOLEAN',2,-1,1,1 +'tinyint_col19','TINYINT',5,-1,1,1 +'smallint_col19','SMALLINT',5,-1,2,2 +'int_col19','INT',5,-1,4,4 +'bigint_col19','BIGINT',5,-1,8,8 +'float_col19','FLOAT',5,-1,4,4 +'double_col19','DOUBLE',5,-1,8,8 +'string_col19','STRING',5,-1,1,1 +'bool_col20','BOOLEAN',2,-1,1,1 +'tinyint_col20','TINYINT',5,-1,1,1 +'smallint_col20','SMALLINT',5,-1,2,2 +'int_col20','INT',5,-1,4,4 +'bigint_col20','BIGINT',5,-1,8,8 +'float_col20','FLOAT',5,-1,4,4 +'double_col20','DOUBLE',5,-1,8,8 +'string_col20','STRING',5,-1,1,1 +'bool_col21','BOOLEAN',2,-1,1,1 +'tinyint_col21','TINYINT',5,-1,1,1 +'smallint_col21','SMALLINT',5,-1,2,2 +'int_col21','INT',5,-1,4,4 +'bigint_col21','BIGINT',5,-1,8,8 +'float_col21','FLOAT',5,-1,4,4 +'double_col21','DOUBLE',5,-1,8,8 +'string_col21','STRING',5,-1,1,1 +'bool_col22','BOOLEAN',2,-1,1,1 +'tinyint_col22','TINYINT',5,-1,1,1 +'smallint_col22','SMALLINT',5,-1,2,2 +'int_col22','INT',5,-1,4,4 +'bigint_col22','BIGINT',5,-1,8,8 +'float_col22','FLOAT',5,-1,4,4 +'double_col22','DOUBLE',5,-1,8,8 +'string_col22','STRING',5,-1,1,1 +'bool_col23','BOOLEAN',2,-1,1,1 +'tinyint_col23','TINYINT',5,-1,1,1 +'smallint_col23','SMALLINT',5,-1,2,2 +'int_col23','INT',5,-1,4,4 +'bigint_col23','BIGINT',5,-1,8,8 +'float_col23','FLOAT',5,-1,4,4 +'double_col23','DOUBLE',5,-1,8,8 +'string_col23','STRING',5,-1,1,1 +'bool_col24','BOOLEAN',2,-1,1,1 +'tinyint_col24','TINYINT',5,-1,1,1 +'smallint_col24','SMALLINT',5,-1,2,2 +'int_col24','INT',5,-1,4,4 +'bigint_col24','BIGINT',5,-1,8,8 +'float_col24','FLOAT',5,-1,4,4 +'double_col24','DOUBLE',5,-1,8,8 +'string_col24','STRING',5,-1,1,1 +'bool_col25','BOOLEAN',2,-1,1,1 +'tinyint_col25','TINYINT',5,-1,1,1 +'smallint_col25','SMALLINT',5,-1,2,2 +'int_col25','INT',5,-1,4,4 +'bigint_col25','BIGINT',5,-1,8,8 +'float_col25','FLOAT',5,-1,4,4 +'double_col25','DOUBLE',5,-1,8,8 +'string_col25','STRING',5,-1,1,1 +'bool_col26','BOOLEAN',2,-1,1,1 +'tinyint_col26','TINYINT',5,-1,1,1 +'smallint_col26','SMALLINT',5,-1,2,2 +'int_col26','INT',5,-1,4,4 +'bigint_col26','BIGINT',5,-1,8,8 +'float_col26','FLOAT',5,-1,4,4 +'double_col26','DOUBLE',5,-1,8,8 +'string_col26','STRING',5,-1,1,1 +'bool_col27','BOOLEAN',2,-1,1,1 +'tinyint_col27','TINYINT',5,-1,1,1 +'smallint_col27','SMALLINT',5,-1,2,2 +'int_col27','INT',5,-1,4,4 +'bigint_col27','BIGINT',5,-1,8,8 +'float_col27','FLOAT',5,-1,4,4 +'double_col27','DOUBLE',5,-1,8,8 +'string_col27','STRING',5,-1,1,1 +'bool_col28','BOOLEAN',2,-1,1,1 +'tinyint_col28','TINYINT',5,-1,1,1 +'smallint_col28','SMALLINT',5,-1,2,2 +'int_col28','INT',5,-1,4,4 +'bigint_col28','BIGINT',5,-1,8,8 +'float_col28','FLOAT',5,-1,4,4 +'double_col28','DOUBLE',5,-1,8,8 +'string_col28','STRING',5,-1,1,1 +'bool_col29','BOOLEAN',2,-1,1,1 +'tinyint_col29','TINYINT',5,-1,1,1 +'smallint_col29','SMALLINT',5,-1,2,2 +'int_col29','INT',5,-1,4,4 +'bigint_col29','BIGINT',5,-1,8,8 +'float_col29','FLOAT',5,-1,4,4 +'double_col29','DOUBLE',5,-1,8,8 +'string_col29','STRING',5,-1,1,1 +'bool_col30','BOOLEAN',2,-1,1,1 +'tinyint_col30','TINYINT',5,-1,1,1 +'smallint_col30','SMALLINT',5,-1,2,2 +'int_col30','INT',5,-1,4,4 +'bigint_col30','BIGINT',5,-1,8,8 +'float_col30','FLOAT',5,-1,4,4 +'double_col30','DOUBLE',5,-1,8,8 +'string_col30','STRING',5,-1,1,1 +'bool_col31','BOOLEAN',2,-1,1,1 +'tinyint_col31','TINYINT',5,-1,1,1 +'smallint_col31','SMALLINT',5,-1,2,2 +'int_col31','INT',5,-1,4,4 +'bigint_col31','BIGINT',5,-1,8,8 +'float_col31','FLOAT',5,-1,4,4 +'double_col31','DOUBLE',5,-1,8,8 +'string_col31','STRING',5,-1,1,1 +'bool_col32','BOOLEAN',2,-1,1,1 +'tinyint_col32','TINYINT',5,-1,1,1 +'smallint_col32','SMALLINT',5,-1,2,2 +'int_col32','INT',5,-1,4,4 +'bigint_col32','BIGINT',5,-1,8,8 +'float_col32','FLOAT',5,-1,4,4 +'double_col32','DOUBLE',5,-1,8,8 +'string_col32','STRING',5,-1,1,1 +'bool_col33','BOOLEAN',2,-1,1,1 +'tinyint_col33','TINYINT',5,-1,1,1 +'smallint_col33','SMALLINT',5,-1,2,2 +'int_col33','INT',5,-1,4,4 +'bigint_col33','BIGINT',5,-1,8,8 +'float_col33','FLOAT',5,-1,4,4 +'double_col33','DOUBLE',5,-1,8,8 +'string_col33','STRING',5,-1,1,1 +'bool_col34','BOOLEAN',2,-1,1,1 +'tinyint_col34','TINYINT',5,-1,1,1 +'smallint_col34','SMALLINT',5,-1,2,2 +'int_col34','INT',5,-1,4,4 +'bigint_col34','BIGINT',5,-1,8,8 +'float_col34','FLOAT',5,-1,4,4 +'double_col34','DOUBLE',5,-1,8,8 +'string_col34','STRING',5,-1,1,1 +'bool_col35','BOOLEAN',2,-1,1,1 +'tinyint_col35','TINYINT',5,-1,1,1 +'smallint_col35','SMALLINT',5,-1,2,2 +'int_col35','INT',5,-1,4,4 +'bigint_col35','BIGINT',5,-1,8,8 +'float_col35','FLOAT',5,-1,4,4 +'double_col35','DOUBLE',5,-1,8,8 +'string_col35','STRING',5,-1,1,1 +'bool_col36','BOOLEAN',2,-1,1,1 +'tinyint_col36','TINYINT',5,-1,1,1 +'smallint_col36','SMALLINT',5,-1,2,2 +'int_col36','INT',5,-1,4,4 +'bigint_col36','BIGINT',5,-1,8,8 +'float_col36','FLOAT',5,-1,4,4 +'double_col36','DOUBLE',5,-1,8,8 +'string_col36','STRING',5,-1,1,1 +'bool_col37','BOOLEAN',2,-1,1,1 +'tinyint_col37','TINYINT',5,-1,1,1 +'smallint_col37','SMALLINT',5,-1,2,2 +'int_col37','INT',5,-1,4,4 +'bigint_col37','BIGINT',5,-1,8,8 +'float_col37','FLOAT',5,-1,4,4 +'double_col37','DOUBLE',5,-1,8,8 +'string_col37','STRING',5,-1,1,1 +'bool_col38','BOOLEAN',2,-1,1,1 +'tinyint_col38','TINYINT',5,-1,1,1 +'smallint_col38','SMALLINT',5,-1,2,2 +'int_col38','INT',5,-1,4,4 +'bigint_col38','BIGINT',5,-1,8,8 +'float_col38','FLOAT',5,-1,4,4 +'double_col38','DOUBLE',5,-1,8,8 +'string_col38','STRING',5,-1,1,1 +'bool_col39','BOOLEAN',2,-1,1,1 +'tinyint_col39','TINYINT',5,-1,1,1 +'smallint_col39','SMALLINT',5,-1,2,2 +'int_col39','INT',5,-1,4,4 +'bigint_col39','BIGINT',5,-1,8,8 +'float_col39','FLOAT',5,-1,4,4 +'double_col39','DOUBLE',5,-1,8,8 +'string_col39','STRING',5,-1,1,1 +'bool_col40','BOOLEAN',2,-1,1,1 +'tinyint_col40','TINYINT',5,-1,1,1 +'smallint_col40','SMALLINT',5,-1,2,2 +'int_col40','INT',5,-1,4,4 +'bigint_col40','BIGINT',5,-1,8,8 +'float_col40','FLOAT',5,-1,4,4 +'double_col40','DOUBLE',5,-1,8,8 +'string_col40','STRING',5,-1,1,1 +'bool_col41','BOOLEAN',2,-1,1,1 +'tinyint_col41','TINYINT',5,-1,1,1 +'smallint_col41','SMALLINT',5,-1,2,2 +'int_col41','INT',5,-1,4,4 +'bigint_col41','BIGINT',5,-1,8,8 +'float_col41','FLOAT',5,-1,4,4 +'double_col41','DOUBLE',5,-1,8,8 +'string_col41','STRING',5,-1,1,1 +'bool_col42','BOOLEAN',2,-1,1,1 +'tinyint_col42','TINYINT',5,-1,1,1 +'smallint_col42','SMALLINT',5,-1,2,2 +'int_col42','INT',5,-1,4,4 +'bigint_col42','BIGINT',5,-1,8,8 +'float_col42','FLOAT',5,-1,4,4 +'double_col42','DOUBLE',5,-1,8,8 +'string_col42','STRING',5,-1,1,1 +'bool_col43','BOOLEAN',2,-1,1,1 +'tinyint_col43','TINYINT',5,-1,1,1 +'smallint_col43','SMALLINT',5,-1,2,2 +'int_col43','INT',5,-1,4,4 +'bigint_col43','BIGINT',5,-1,8,8 +'float_col43','FLOAT',5,-1,4,4 +'double_col43','DOUBLE',5,-1,8,8 +'string_col43','STRING',5,-1,1,1 +'bool_col44','BOOLEAN',2,-1,1,1 +'tinyint_col44','TINYINT',5,-1,1,1 +'smallint_col44','SMALLINT',5,-1,2,2 +'int_col44','INT',5,-1,4,4 +'bigint_col44','BIGINT',5,-1,8,8 +'float_col44','FLOAT',5,-1,4,4 +'double_col44','DOUBLE',5,-1,8,8 +'string_col44','STRING',5,-1,1,1 +'bool_col45','BOOLEAN',2,-1,1,1 +'tinyint_col45','TINYINT',5,-1,1,1 +'smallint_col45','SMALLINT',5,-1,2,2 +'int_col45','INT',5,-1,4,4 +'bigint_col45','BIGINT',5,-1,8,8 +'float_col45','FLOAT',5,-1,4,4 +'double_col45','DOUBLE',5,-1,8,8 +'string_col45','STRING',5,-1,1,1 +'bool_col46','BOOLEAN',2,-1,1,1 +'tinyint_col46','TINYINT',5,-1,1,1 +'smallint_col46','SMALLINT',5,-1,2,2 +'int_col46','INT',5,-1,4,4 +'bigint_col46','BIGINT',5,-1,8,8 +'float_col46','FLOAT',5,-1,4,4 +'double_col46','DOUBLE',5,-1,8,8 +'string_col46','STRING',5,-1,1,1 +'bool_col47','BOOLEAN',2,-1,1,1 +'tinyint_col47','TINYINT',5,-1,1,1 +'smallint_col47','SMALLINT',5,-1,2,2 +'int_col47','INT',5,-1,4,4 +'bigint_col47','BIGINT',5,-1,8,8 +'float_col47','FLOAT',5,-1,4,4 +'double_col47','DOUBLE',5,-1,8,8 +'string_col47','STRING',5,-1,1,1 +'bool_col48','BOOLEAN',2,-1,1,1 +'tinyint_col48','TINYINT',5,-1,1,1 +'smallint_col48','SMALLINT',5,-1,2,2 +'int_col48','INT',5,-1,4,4 +'bigint_col48','BIGINT',5,-1,8,8 +'float_col48','FLOAT',5,-1,4,4 +'double_col48','DOUBLE',5,-1,8,8 +'string_col48','STRING',5,-1,1,1 +'bool_col49','BOOLEAN',2,-1,1,1 +'tinyint_col49','TINYINT',5,-1,1,1 +'smallint_col49','SMALLINT',5,-1,2,2 +'int_col49','INT',5,-1,4,4 +'bigint_col49','BIGINT',5,-1,8,8 +'float_col49','FLOAT',5,-1,4,4 +'double_col49','DOUBLE',5,-1,8,8 +'string_col49','STRING',5,-1,1,1 +'bool_col50','BOOLEAN',2,-1,1,1 +'tinyint_col50','TINYINT',5,-1,1,1 +'smallint_col50','SMALLINT',5,-1,2,2 +'int_col50','INT',5,-1,4,4 +'bigint_col50','BIGINT',5,-1,8,8 +'float_col50','FLOAT',5,-1,4,4 +'double_col50','DOUBLE',5,-1,8,8 +'string_col50','STRING',5,-1,1,1 +'bool_col51','BOOLEAN',2,-1,1,1 +'tinyint_col51','TINYINT',5,-1,1,1 +'smallint_col51','SMALLINT',5,-1,2,2 +'int_col51','INT',5,-1,4,4 +'bigint_col51','BIGINT',5,-1,8,8 +'float_col51','FLOAT',5,-1,4,4 +'double_col51','DOUBLE',5,-1,8,8 +'string_col51','STRING',5,-1,1,1 +'bool_col52','BOOLEAN',2,-1,1,1 +'tinyint_col52','TINYINT',5,-1,1,1 +'smallint_col52','SMALLINT',5,-1,2,2 +'int_col52','INT',5,-1,4,4 +'bigint_col52','BIGINT',5,-1,8,8 +'float_col52','FLOAT',5,-1,4,4 +'double_col52','DOUBLE',5,-1,8,8 +'string_col52','STRING',5,-1,1,1 +'bool_col53','BOOLEAN',2,-1,1,1 +'tinyint_col53','TINYINT',5,-1,1,1 +'smallint_col53','SMALLINT',5,-1,2,2 +'int_col53','INT',5,-1,4,4 +'bigint_col53','BIGINT',5,-1,8,8 +'float_col53','FLOAT',5,-1,4,4 +'double_col53','DOUBLE',5,-1,8,8 +'string_col53','STRING',5,-1,1,1 +'bool_col54','BOOLEAN',2,-1,1,1 +'tinyint_col54','TINYINT',5,-1,1,1 +'smallint_col54','SMALLINT',5,-1,2,2 +'int_col54','INT',5,-1,4,4 +'bigint_col54','BIGINT',5,-1,8,8 +'float_col54','FLOAT',5,-1,4,4 +'double_col54','DOUBLE',5,-1,8,8 +'string_col54','STRING',5,-1,1,1 +'bool_col55','BOOLEAN',2,-1,1,1 +'tinyint_col55','TINYINT',5,-1,1,1 +'smallint_col55','SMALLINT',5,-1,2,2 +'int_col55','INT',5,-1,4,4 +'bigint_col55','BIGINT',5,-1,8,8 +'float_col55','FLOAT',5,-1,4,4 +'double_col55','DOUBLE',5,-1,8,8 +'string_col55','STRING',5,-1,1,1 +'bool_col56','BOOLEAN',2,-1,1,1 +'tinyint_col56','TINYINT',5,-1,1,1 +'smallint_col56','SMALLINT',5,-1,2,2 +'int_col56','INT',5,-1,4,4 +'bigint_col56','BIGINT',5,-1,8,8 +'float_col56','FLOAT',5,-1,4,4 +'double_col56','DOUBLE',5,-1,8,8 +'string_col56','STRING',5,-1,1,1 +'bool_col57','BOOLEAN',2,-1,1,1 +'tinyint_col57','TINYINT',5,-1,1,1 +'smallint_col57','SMALLINT',5,-1,2,2 +'int_col57','INT',5,-1,4,4 +'bigint_col57','BIGINT',5,-1,8,8 +'float_col57','FLOAT',5,-1,4,4 +'double_col57','DOUBLE',5,-1,8,8 +'string_col57','STRING',5,-1,1,1 +'bool_col58','BOOLEAN',2,-1,1,1 +'tinyint_col58','TINYINT',5,-1,1,1 +'smallint_col58','SMALLINT',5,-1,2,2 +'int_col58','INT',5,-1,4,4 +'bigint_col58','BIGINT',5,-1,8,8 +'float_col58','FLOAT',5,-1,4,4 +'double_col58','DOUBLE',5,-1,8,8 +'string_col58','STRING',5,-1,1,1 +'bool_col59','BOOLEAN',2,-1,1,1 +'tinyint_col59','TINYINT',5,-1,1,1 +'smallint_col59','SMALLINT',5,-1,2,2 +'int_col59','INT',5,-1,4,4 +'bigint_col59','BIGINT',5,-1,8,8 +'float_col59','FLOAT',5,-1,4,4 +'double_col59','DOUBLE',5,-1,8,8 +'string_col59','STRING',5,-1,1,1 +'bool_col60','BOOLEAN',2,-1,1,1 +'tinyint_col60','TINYINT',5,-1,1,1 +'smallint_col60','SMALLINT',5,-1,2,2 +'int_col60','INT',5,-1,4,4 +'bigint_col60','BIGINT',5,-1,8,8 +'float_col60','FLOAT',5,-1,4,4 +'double_col60','DOUBLE',5,-1,8,8 +'string_col60','STRING',5,-1,1,1 +'bool_col61','BOOLEAN',2,-1,1,1 +'tinyint_col61','TINYINT',5,-1,1,1 +'smallint_col61','SMALLINT',5,-1,2,2 +'int_col61','INT',5,-1,4,4 +'bigint_col61','BIGINT',5,-1,8,8 +'float_col61','FLOAT',5,-1,4,4 +'double_col61','DOUBLE',5,-1,8,8 +'string_col61','STRING',5,-1,1,1 +'bool_col62','BOOLEAN',2,-1,1,1 +'tinyint_col62','TINYINT',5,-1,1,1 +'smallint_col62','SMALLINT',5,-1,2,2 +'int_col62','INT',5,-1,4,4 +'bigint_col62','BIGINT',5,-1,8,8 +'float_col62','FLOAT',5,-1,4,4 +'double_col62','DOUBLE',5,-1,8,8 +'string_col62','STRING',5,-1,1,1 +'bool_col63','BOOLEAN',2,-1,1,1 +'tinyint_col63','TINYINT',5,-1,1,1 +'smallint_col63','SMALLINT',5,-1,2,2 +'int_col63','INT',5,-1,4,4 +'bigint_col63','BIGINT',5,-1,8,8 +'float_col63','FLOAT',5,-1,4,4 +'double_col63','DOUBLE',5,-1,8,8 +'string_col63','STRING',5,-1,1,1 +'bool_col64','BOOLEAN',2,-1,1,1 +'tinyint_col64','TINYINT',5,-1,1,1 +'smallint_col64','SMALLINT',5,-1,2,2 +'int_col64','INT',5,-1,4,4 +'bigint_col64','BIGINT',5,-1,8,8 +'float_col64','FLOAT',5,-1,4,4 +'double_col64','DOUBLE',5,-1,8,8 +'string_col64','STRING',5,-1,1,1 +'bool_col65','BOOLEAN',2,-1,1,1 +'tinyint_col65','TINYINT',5,-1,1,1 +'smallint_col65','SMALLINT',5,-1,2,2 +'int_col65','INT',5,-1,4,4 +'bigint_col65','BIGINT',5,-1,8,8 +'float_col65','FLOAT',5,-1,4,4 +'double_col65','DOUBLE',5,-1,8,8 +'string_col65','STRING',5,-1,1,1 +'bool_col66','BOOLEAN',2,-1,1,1 +'tinyint_col66','TINYINT',5,-1,1,1 +'smallint_col66','SMALLINT',5,-1,2,2 +'int_col66','INT',5,-1,4,4 +'bigint_col66','BIGINT',5,-1,8,8 +'float_col66','FLOAT',5,-1,4,4 +'double_col66','DOUBLE',5,-1,8,8 +'string_col66','STRING',5,-1,1,1 +'bool_col67','BOOLEAN',2,-1,1,1 +'tinyint_col67','TINYINT',5,-1,1,1 +'smallint_col67','SMALLINT',5,-1,2,2 +'int_col67','INT',5,-1,4,4 +'bigint_col67','BIGINT',5,-1,8,8 +'float_col67','FLOAT',5,-1,4,4 +'double_col67','DOUBLE',5,-1,8,8 +'string_col67','STRING',5,-1,1,1 +'bool_col68','BOOLEAN',2,-1,1,1 +'tinyint_col68','TINYINT',5,-1,1,1 +'smallint_col68','SMALLINT',5,-1,2,2 +'int_col68','INT',5,-1,4,4 +'bigint_col68','BIGINT',5,-1,8,8 +'float_col68','FLOAT',5,-1,4,4 +'double_col68','DOUBLE',5,-1,8,8 +'string_col68','STRING',5,-1,1,1 +'bool_col69','BOOLEAN',2,-1,1,1 +'tinyint_col69','TINYINT',5,-1,1,1 +'smallint_col69','SMALLINT',5,-1,2,2 +'int_col69','INT',5,-1,4,4 +'bigint_col69','BIGINT',5,-1,8,8 +'float_col69','FLOAT',5,-1,4,4 +'double_col69','DOUBLE',5,-1,8,8 +'string_col69','STRING',5,-1,1,1 +'bool_col70','BOOLEAN',2,-1,1,1 +'tinyint_col70','TINYINT',5,-1,1,1 +'smallint_col70','SMALLINT',5,-1,2,2 +'int_col70','INT',5,-1,4,4 +'bigint_col70','BIGINT',5,-1,8,8 +'float_col70','FLOAT',5,-1,4,4 +'double_col70','DOUBLE',5,-1,8,8 +'string_col70','STRING',5,-1,1,1 +'bool_col71','BOOLEAN',2,-1,1,1 +'tinyint_col71','TINYINT',5,-1,1,1 +'smallint_col71','SMALLINT',5,-1,2,2 +'int_col71','INT',5,-1,4,4 +'bigint_col71','BIGINT',5,-1,8,8 +'float_col71','FLOAT',5,-1,4,4 +'double_col71','DOUBLE',5,-1,8,8 +'string_col71','STRING',5,-1,1,1 +'bool_col72','BOOLEAN',2,-1,1,1 +'tinyint_col72','TINYINT',5,-1,1,1 +'smallint_col72','SMALLINT',5,-1,2,2 +'int_col72','INT',5,-1,4,4 +'bigint_col72','BIGINT',5,-1,8,8 +'float_col72','FLOAT',5,-1,4,4 +'double_col72','DOUBLE',5,-1,8,8 +'string_col72','STRING',5,-1,1,1 +'bool_col73','BOOLEAN',2,-1,1,1 +'tinyint_col73','TINYINT',5,-1,1,1 +'smallint_col73','SMALLINT',5,-1,2,2 +'int_col73','INT',5,-1,4,4 +'bigint_col73','BIGINT',5,-1,8,8 +'float_col73','FLOAT',5,-1,4,4 +'double_col73','DOUBLE',5,-1,8,8 +'string_col73','STRING',5,-1,1,1 +'bool_col74','BOOLEAN',2,-1,1,1 +'tinyint_col74','TINYINT',5,-1,1,1 +'smallint_col74','SMALLINT',5,-1,2,2 +'int_col74','INT',5,-1,4,4 +'bigint_col74','BIGINT',5,-1,8,8 +'float_col74','FLOAT',5,-1,4,4 +'double_col74','DOUBLE',5,-1,8,8 +'string_col74','STRING',5,-1,1,1 +'bool_col75','BOOLEAN',2,-1,1,1 +'tinyint_col75','TINYINT',5,-1,1,1 +'smallint_col75','SMALLINT',5,-1,2,2 +'int_col75','INT',5,-1,4,4 +'bigint_col75','BIGINT',5,-1,8,8 +'float_col75','FLOAT',5,-1,4,4 +'double_col75','DOUBLE',5,-1,8,8 +'string_col75','STRING',5,-1,1,1 +'bool_col76','BOOLEAN',2,-1,1,1 +'tinyint_col76','TINYINT',5,-1,1,1 +'smallint_col76','SMALLINT',5,-1,2,2 +'int_col76','INT',5,-1,4,4 +'bigint_col76','BIGINT',5,-1,8,8 +'float_col76','FLOAT',5,-1,4,4 +'double_col76','DOUBLE',5,-1,8,8 +'string_col76','STRING',5,-1,1,1 +'bool_col77','BOOLEAN',2,-1,1,1 +'tinyint_col77','TINYINT',5,-1,1,1 +'smallint_col77','SMALLINT',5,-1,2,2 +'int_col77','INT',5,-1,4,4 +'bigint_col77','BIGINT',5,-1,8,8 +'float_col77','FLOAT',5,-1,4,4 +'double_col77','DOUBLE',5,-1,8,8 +'string_col77','STRING',5,-1,1,1 +'bool_col78','BOOLEAN',2,-1,1,1 +'tinyint_col78','TINYINT',5,-1,1,1 +'smallint_col78','SMALLINT',5,-1,2,2 +'int_col78','INT',5,-1,4,4 +'bigint_col78','BIGINT',5,-1,8,8 +'float_col78','FLOAT',5,-1,4,4 +'double_col78','DOUBLE',5,-1,8,8 +'string_col78','STRING',5,-1,1,1 +'bool_col79','BOOLEAN',2,-1,1,1 +'tinyint_col79','TINYINT',5,-1,1,1 +'smallint_col79','SMALLINT',5,-1,2,2 +'int_col79','INT',5,-1,4,4 +'bigint_col79','BIGINT',5,-1,8,8 +'float_col79','FLOAT',5,-1,4,4 +'double_col79','DOUBLE',5,-1,8,8 +'string_col79','STRING',5,-1,1,1 +'bool_col80','BOOLEAN',2,-1,1,1 +'tinyint_col80','TINYINT',5,-1,1,1 +'smallint_col80','SMALLINT',5,-1,2,2 +'int_col80','INT',5,-1,4,4 +'bigint_col80','BIGINT',5,-1,8,8 +'float_col80','FLOAT',5,-1,4,4 +'double_col80','DOUBLE',5,-1,8,8 +'string_col80','STRING',5,-1,1,1 +'bool_col81','BOOLEAN',2,-1,1,1 +'tinyint_col81','TINYINT',5,-1,1,1 +'smallint_col81','SMALLINT',5,-1,2,2 +'int_col81','INT',5,-1,4,4 +'bigint_col81','BIGINT',5,-1,8,8 +'float_col81','FLOAT',5,-1,4,4 +'double_col81','DOUBLE',5,-1,8,8 +'string_col81','STRING',5,-1,1,1 +'bool_col82','BOOLEAN',2,-1,1,1 +'tinyint_col82','TINYINT',5,-1,1,1 +'smallint_col82','SMALLINT',5,-1,2,2 +'int_col82','INT',5,-1,4,4 +'bigint_col82','BIGINT',5,-1,8,8 +'float_col82','FLOAT',5,-1,4,4 +'double_col82','DOUBLE',5,-1,8,8 +'string_col82','STRING',5,-1,1,1 +'bool_col83','BOOLEAN',2,-1,1,1 +'tinyint_col83','TINYINT',5,-1,1,1 +'smallint_col83','SMALLINT',5,-1,2,2 +'int_col83','INT',5,-1,4,4 +'bigint_col83','BIGINT',5,-1,8,8 +'float_col83','FLOAT',5,-1,4,4 +'double_col83','DOUBLE',5,-1,8,8 +'string_col83','STRING',5,-1,1,1 +'bool_col84','BOOLEAN',2,-1,1,1 +'tinyint_col84','TINYINT',5,-1,1,1 +'smallint_col84','SMALLINT',5,-1,2,2 +'int_col84','INT',5,-1,4,4 +'bigint_col84','BIGINT',5,-1,8,8 +'float_col84','FLOAT',5,-1,4,4 +'double_col84','DOUBLE',5,-1,8,8 +'string_col84','STRING',5,-1,1,1 +'bool_col85','BOOLEAN',2,-1,1,1 +'tinyint_col85','TINYINT',5,-1,1,1 +'smallint_col85','SMALLINT',5,-1,2,2 +'int_col85','INT',5,-1,4,4 +'bigint_col85','BIGINT',5,-1,8,8 +'float_col85','FLOAT',5,-1,4,4 +'double_col85','DOUBLE',5,-1,8,8 +'string_col85','STRING',5,-1,1,1 +'bool_col86','BOOLEAN',2,-1,1,1 +'tinyint_col86','TINYINT',5,-1,1,1 +'smallint_col86','SMALLINT',5,-1,2,2 +'int_col86','INT',5,-1,4,4 +'bigint_col86','BIGINT',5,-1,8,8 +'float_col86','FLOAT',5,-1,4,4 +'double_col86','DOUBLE',5,-1,8,8 +'string_col86','STRING',5,-1,1,1 +'bool_col87','BOOLEAN',2,-1,1,1 +'tinyint_col87','TINYINT',5,-1,1,1 +'smallint_col87','SMALLINT',5,-1,2,2 +'int_col87','INT',5,-1,4,4 +'bigint_col87','BIGINT',5,-1,8,8 +'float_col87','FLOAT',5,-1,4,4 +'double_col87','DOUBLE',5,-1,8,8 +'string_col87','STRING',5,-1,1,1 +'bool_col88','BOOLEAN',2,-1,1,1 +'tinyint_col88','TINYINT',5,-1,1,1 +'smallint_col88','SMALLINT',5,-1,2,2 +'int_col88','INT',5,-1,4,4 +'bigint_col88','BIGINT',5,-1,8,8 +'float_col88','FLOAT',5,-1,4,4 +'double_col88','DOUBLE',5,-1,8,8 +'string_col88','STRING',5,-1,1,1 +'bool_col89','BOOLEAN',2,-1,1,1 +'tinyint_col89','TINYINT',5,-1,1,1 +'smallint_col89','SMALLINT',5,-1,2,2 +'int_col89','INT',5,-1,4,4 +'bigint_col89','BIGINT',5,-1,8,8 +'float_col89','FLOAT',5,-1,4,4 +'double_col89','DOUBLE',5,-1,8,8 +'string_col89','STRING',5,-1,1,1 +'bool_col90','BOOLEAN',2,-1,1,1 +'tinyint_col90','TINYINT',5,-1,1,1 +'smallint_col90','SMALLINT',5,-1,2,2 +'int_col90','INT',5,-1,4,4 +'bigint_col90','BIGINT',5,-1,8,8 +'float_col90','FLOAT',5,-1,4,4 +'double_col90','DOUBLE',5,-1,8,8 +'string_col90','STRING',5,-1,1,1 +'bool_col91','BOOLEAN',2,-1,1,1 +'tinyint_col91','TINYINT',5,-1,1,1 +'smallint_col91','SMALLINT',5,-1,2,2 +'int_col91','INT',5,-1,4,4 +'bigint_col91','BIGINT',5,-1,8,8 +'float_col91','FLOAT',5,-1,4,4 +'double_col91','DOUBLE',5,-1,8,8 +'string_col91','STRING',5,-1,1,1 +'bool_col92','BOOLEAN',2,-1,1,1 +'tinyint_col92','TINYINT',5,-1,1,1 +'smallint_col92','SMALLINT',5,-1,2,2 +'int_col92','INT',5,-1,4,4 +'bigint_col92','BIGINT',5,-1,8,8 +'float_col92','FLOAT',5,-1,4,4 +'double_col92','DOUBLE',5,-1,8,8 +'string_col92','STRING',5,-1,1,1 +'bool_col93','BOOLEAN',2,-1,1,1 +'tinyint_col93','TINYINT',5,-1,1,1 +'smallint_col93','SMALLINT',5,-1,2,2 +'int_col93','INT',5,-1,4,4 +'bigint_col93','BIGINT',5,-1,8,8 +'float_col93','FLOAT',5,-1,4,4 +'double_col93','DOUBLE',5,-1,8,8 +'string_col93','STRING',5,-1,1,1 +'bool_col94','BOOLEAN',2,-1,1,1 +'tinyint_col94','TINYINT',5,-1,1,1 +'smallint_col94','SMALLINT',5,-1,2,2 +'int_col94','INT',5,-1,4,4 +'bigint_col94','BIGINT',5,-1,8,8 +'float_col94','FLOAT',5,-1,4,4 +'double_col94','DOUBLE',5,-1,8,8 +'string_col94','STRING',5,-1,1,1 +'bool_col95','BOOLEAN',2,-1,1,1 +'tinyint_col95','TINYINT',5,-1,1,1 +'smallint_col95','SMALLINT',5,-1,2,2 +'int_col95','INT',5,-1,4,4 +'bigint_col95','BIGINT',5,-1,8,8 +'float_col95','FLOAT',5,-1,4,4 +'double_col95','DOUBLE',5,-1,8,8 +'string_col95','STRING',5,-1,1,1 +'bool_col96','BOOLEAN',2,-1,1,1 +'tinyint_col96','TINYINT',5,-1,1,1 +'smallint_col96','SMALLINT',5,-1,2,2 +'int_col96','INT',5,-1,4,4 +'bigint_col96','BIGINT',5,-1,8,8 +'float_col96','FLOAT',5,-1,4,4 +'double_col96','DOUBLE',5,-1,8,8 +'string_col96','STRING',5,-1,1,1 +'bool_col97','BOOLEAN',2,-1,1,1 +'tinyint_col97','TINYINT',5,-1,1,1 +'smallint_col97','SMALLINT',5,-1,2,2 +'int_col97','INT',5,-1,4,4 +'bigint_col97','BIGINT',5,-1,8,8 +'float_col97','FLOAT',5,-1,4,4 +'double_col97','DOUBLE',5,-1,8,8 +'string_col97','STRING',5,-1,1,1 +'bool_col98','BOOLEAN',2,-1,1,1 +'tinyint_col98','TINYINT',5,-1,1,1 +'smallint_col98','SMALLINT',5,-1,2,2 +'int_col98','INT',5,-1,4,4 +'bigint_col98','BIGINT',5,-1,8,8 +'float_col98','FLOAT',5,-1,4,4 +'double_col98','DOUBLE',5,-1,8,8 +'string_col98','STRING',5,-1,1,1 +'bool_col99','BOOLEAN',2,-1,1,1 +'tinyint_col99','TINYINT',5,-1,1,1 +'smallint_col99','SMALLINT',5,-1,2,2 +'int_col99','INT',5,-1,4,4 +'bigint_col99','BIGINT',5,-1,8,8 +'float_col99','FLOAT',5,-1,4,4 +'double_col99','DOUBLE',5,-1,8,8 +'string_col99','STRING',5,-1,1,1 +'bool_col100','BOOLEAN',2,-1,1,1 +'tinyint_col100','TINYINT',5,-1,1,1 +'smallint_col100','SMALLINT',5,-1,2,2 +'int_col100','INT',5,-1,4,4 +'bigint_col100','BIGINT',5,-1,8,8 +'float_col100','FLOAT',5,-1,4,4 +'double_col100','DOUBLE',5,-1,8,8 +'string_col100','STRING',5,-1,1,1 +'bool_col101','BOOLEAN',2,-1,1,1 +'tinyint_col101','TINYINT',5,-1,1,1 +'smallint_col101','SMALLINT',5,-1,2,2 +'int_col101','INT',5,-1,4,4 +'bigint_col101','BIGINT',5,-1,8,8 +'float_col101','FLOAT',5,-1,4,4 +'double_col101','DOUBLE',5,-1,8,8 +'string_col101','STRING',5,-1,1,1 +'bool_col102','BOOLEAN',2,-1,1,1 +'tinyint_col102','TINYINT',5,-1,1,1 +'smallint_col102','SMALLINT',5,-1,2,2 +'int_col102','INT',5,-1,4,4 +'bigint_col102','BIGINT',5,-1,8,8 +'float_col102','FLOAT',5,-1,4,4 +'double_col102','DOUBLE',5,-1,8,8 +'string_col102','STRING',5,-1,1,1 +'bool_col103','BOOLEAN',2,-1,1,1 +'tinyint_col103','TINYINT',5,-1,1,1 +'smallint_col103','SMALLINT',5,-1,2,2 +'int_col103','INT',5,-1,4,4 +'bigint_col103','BIGINT',5,-1,8,8 +'float_col103','FLOAT',5,-1,4,4 +'double_col103','DOUBLE',5,-1,8,8 +'string_col103','STRING',5,-1,1,1 +'bool_col104','BOOLEAN',2,-1,1,1 +'tinyint_col104','TINYINT',5,-1,1,1 +'smallint_col104','SMALLINT',5,-1,2,2 +'int_col104','INT',5,-1,4,4 +'bigint_col104','BIGINT',5,-1,8,8 +'float_col104','FLOAT',5,-1,4,4 +'double_col104','DOUBLE',5,-1,8,8 +'string_col104','STRING',5,-1,1,1 +'bool_col105','BOOLEAN',2,-1,1,1 +'tinyint_col105','TINYINT',5,-1,1,1 +'smallint_col105','SMALLINT',5,-1,2,2 +'int_col105','INT',5,-1,4,4 +'bigint_col105','BIGINT',5,-1,8,8 +'float_col105','FLOAT',5,-1,4,4 +'double_col105','DOUBLE',5,-1,8,8 +'string_col105','STRING',5,-1,1,1 +'bool_col106','BOOLEAN',2,-1,1,1 +'tinyint_col106','TINYINT',5,-1,1,1 +'smallint_col106','SMALLINT',5,-1,2,2 +'int_col106','INT',5,-1,4,4 +'bigint_col106','BIGINT',5,-1,8,8 +'float_col106','FLOAT',5,-1,4,4 +'double_col106','DOUBLE',5,-1,8,8 +'string_col106','STRING',5,-1,1,1 +'bool_col107','BOOLEAN',2,-1,1,1 +'tinyint_col107','TINYINT',5,-1,1,1 +'smallint_col107','SMALLINT',5,-1,2,2 +'int_col107','INT',5,-1,4,4 +'bigint_col107','BIGINT',5,-1,8,8 +'float_col107','FLOAT',5,-1,4,4 +'double_col107','DOUBLE',5,-1,8,8 +'string_col107','STRING',5,-1,1,1 +'bool_col108','BOOLEAN',2,-1,1,1 +'tinyint_col108','TINYINT',5,-1,1,1 +'smallint_col108','SMALLINT',5,-1,2,2 +'int_col108','INT',5,-1,4,4 +'bigint_col108','BIGINT',5,-1,8,8 +'float_col108','FLOAT',5,-1,4,4 +'double_col108','DOUBLE',5,-1,8,8 +'string_col108','STRING',5,-1,1,1 +'bool_col109','BOOLEAN',2,-1,1,1 +'tinyint_col109','TINYINT',5,-1,1,1 +'smallint_col109','SMALLINT',5,-1,2,2 +'int_col109','INT',5,-1,4,4 +'bigint_col109','BIGINT',5,-1,8,8 +'float_col109','FLOAT',5,-1,4,4 +'double_col109','DOUBLE',5,-1,8,8 +'string_col109','STRING',5,-1,1,1 +'bool_col110','BOOLEAN',2,-1,1,1 +'tinyint_col110','TINYINT',5,-1,1,1 +'smallint_col110','SMALLINT',5,-1,2,2 +'int_col110','INT',5,-1,4,4 +'bigint_col110','BIGINT',5,-1,8,8 +'float_col110','FLOAT',5,-1,4,4 +'double_col110','DOUBLE',5,-1,8,8 +'string_col110','STRING',5,-1,1,1 +'bool_col111','BOOLEAN',2,-1,1,1 +'tinyint_col111','TINYINT',5,-1,1,1 +'smallint_col111','SMALLINT',5,-1,2,2 +'int_col111','INT',5,-1,4,4 +'bigint_col111','BIGINT',5,-1,8,8 +'float_col111','FLOAT',5,-1,4,4 +'double_col111','DOUBLE',5,-1,8,8 +'string_col111','STRING',5,-1,1,1 +'bool_col112','BOOLEAN',2,-1,1,1 +'tinyint_col112','TINYINT',5,-1,1,1 +'smallint_col112','SMALLINT',5,-1,2,2 +'int_col112','INT',5,-1,4,4 +'bigint_col112','BIGINT',5,-1,8,8 +'float_col112','FLOAT',5,-1,4,4 +'double_col112','DOUBLE',5,-1,8,8 +'string_col112','STRING',5,-1,1,1 +'bool_col113','BOOLEAN',2,-1,1,1 +'tinyint_col113','TINYINT',5,-1,1,1 +'smallint_col113','SMALLINT',5,-1,2,2 +'int_col113','INT',5,-1,4,4 +'bigint_col113','BIGINT',5,-1,8,8 +'float_col113','FLOAT',5,-1,4,4 +'double_col113','DOUBLE',5,-1,8,8 +'string_col113','STRING',5,-1,1,1 +'bool_col114','BOOLEAN',2,-1,1,1 +'tinyint_col114','TINYINT',5,-1,1,1 +'smallint_col114','SMALLINT',5,-1,2,2 +'int_col114','INT',5,-1,4,4 +'bigint_col114','BIGINT',5,-1,8,8 +'float_col114','FLOAT',5,-1,4,4 +'double_col114','DOUBLE',5,-1,8,8 +'string_col114','STRING',5,-1,1,1 +'bool_col115','BOOLEAN',2,-1,1,1 +'tinyint_col115','TINYINT',5,-1,1,1 +'smallint_col115','SMALLINT',5,-1,2,2 +'int_col115','INT',5,-1,4,4 +'bigint_col115','BIGINT',5,-1,8,8 +'float_col115','FLOAT',5,-1,4,4 +'double_col115','DOUBLE',5,-1,8,8 +'string_col115','STRING',5,-1,1,1 +'bool_col116','BOOLEAN',2,-1,1,1 +'tinyint_col116','TINYINT',5,-1,1,1 +'smallint_col116','SMALLINT',5,-1,2,2 +'int_col116','INT',5,-1,4,4 +'bigint_col116','BIGINT',5,-1,8,8 +'float_col116','FLOAT',5,-1,4,4 +'double_col116','DOUBLE',5,-1,8,8 +'string_col116','STRING',5,-1,1,1 +'bool_col117','BOOLEAN',2,-1,1,1 +'tinyint_col117','TINYINT',5,-1,1,1 +'smallint_col117','SMALLINT',5,-1,2,2 +'int_col117','INT',5,-1,4,4 +'bigint_col117','BIGINT',5,-1,8,8 +'float_col117','FLOAT',5,-1,4,4 +'double_col117','DOUBLE',5,-1,8,8 +'string_col117','STRING',5,-1,1,1 +'bool_col118','BOOLEAN',2,-1,1,1 +'tinyint_col118','TINYINT',5,-1,1,1 +'smallint_col118','SMALLINT',5,-1,2,2 +'int_col118','INT',5,-1,4,4 +'bigint_col118','BIGINT',5,-1,8,8 +'float_col118','FLOAT',5,-1,4,4 +'double_col118','DOUBLE',5,-1,8,8 +'string_col118','STRING',5,-1,1,1 +'bool_col119','BOOLEAN',2,-1,1,1 +'tinyint_col119','TINYINT',5,-1,1,1 +'smallint_col119','SMALLINT',5,-1,2,2 +'int_col119','INT',5,-1,4,4 +'bigint_col119','BIGINT',5,-1,8,8 +'float_col119','FLOAT',5,-1,4,4 +'double_col119','DOUBLE',5,-1,8,8 +'string_col119','STRING',5,-1,1,1 +'bool_col120','BOOLEAN',2,-1,1,1 +'tinyint_col120','TINYINT',5,-1,1,1 +'smallint_col120','SMALLINT',5,-1,2,2 +'int_col120','INT',5,-1,4,4 +'bigint_col120','BIGINT',5,-1,8,8 +'float_col120','FLOAT',5,-1,4,4 +'double_col120','DOUBLE',5,-1,8,8 +'string_col120','STRING',5,-1,1,1 +'bool_col121','BOOLEAN',2,-1,1,1 +'tinyint_col121','TINYINT',5,-1,1,1 +'smallint_col121','SMALLINT',5,-1,2,2 +'int_col121','INT',5,-1,4,4 +'bigint_col121','BIGINT',5,-1,8,8 +'float_col121','FLOAT',5,-1,4,4 +'double_col121','DOUBLE',5,-1,8,8 +'string_col121','STRING',5,-1,1,1 +'bool_col122','BOOLEAN',2,-1,1,1 +'tinyint_col122','TINYINT',5,-1,1,1 +'smallint_col122','SMALLINT',5,-1,2,2 +'int_col122','INT',5,-1,4,4 +'bigint_col122','BIGINT',5,-1,8,8 +'float_col122','FLOAT',5,-1,4,4 +'double_col122','DOUBLE',5,-1,8,8 +'string_col122','STRING',5,-1,1,1 +'bool_col123','BOOLEAN',2,-1,1,1 +'tinyint_col123','TINYINT',5,-1,1,1 +'smallint_col123','SMALLINT',5,-1,2,2 +'int_col123','INT',5,-1,4,4 +'bigint_col123','BIGINT',5,-1,8,8 +'float_col123','FLOAT',5,-1,4,4 +'double_col123','DOUBLE',5,-1,8,8 +'string_col123','STRING',5,-1,1,1 +'bool_col124','BOOLEAN',2,-1,1,1 +'tinyint_col124','TINYINT',5,-1,1,1 +'smallint_col124','SMALLINT',5,-1,2,2 +'int_col124','INT',5,-1,4,4 +'bigint_col124','BIGINT',5,-1,8,8 +'float_col124','FLOAT',5,-1,4,4 +'double_col124','DOUBLE',5,-1,8,8 +'string_col124','STRING',5,-1,1,1 +'bool_col125','BOOLEAN',2,-1,1,1 +'tinyint_col125','TINYINT',5,-1,1,1 +'smallint_col125','SMALLINT',5,-1,2,2 +'int_col125','INT',5,-1,4,4 +'bigint_col125','BIGINT',5,-1,8,8 +'float_col125','FLOAT',5,-1,4,4 +'double_col125','DOUBLE',5,-1,8,8 +'string_col125','STRING',5,-1,1,1 +---- TYPES +STRING, STRING, BIGINT, BIGINT, INT, DOUBLE +==== diff --git a/tests/query_test/test_aggregation.py b/tests/query_test/test_aggregation.py index c44c78481..38900e64c 100644 --- a/tests/query_test/test_aggregation.py +++ b/tests/query_test/test_aggregation.py @@ -19,13 +19,19 @@ # import pytest +from testdata.common import widetable from tests.common.environ import USING_OLD_AGGS_JOINS from tests.common.impala_test_suite import ImpalaTestSuite from tests.common.skip import SkipIfOldAggsJoins from tests.common.test_dimensions import ( create_exec_option_dimension, create_uncompressed_text_dimension) -from tests.common.test_result_verifier import assert_codegen_enabled +from tests.common.test_result_verifier import ( + assert_codegen_enabled, + parse_column_types, + parse_column_labels, + QueryTestResult, + parse_result_rows) from tests.common.test_vector import TestDimension # Test dimensions for TestAggregation. @@ -128,8 +134,9 @@ class TestAggregation(ImpalaTestSuite): self.verify_agg_result(agg_func, data_type, False, result.data[0]); if check_codegen_enabled: - # Verify codegen was enabled for both stages of the aggregation. - assert_codegen_enabled(result.runtime_profile, [1, 3]) + # Verify codegen was enabled for the preaggregation. + # It is deliberately disabled for the merge aggregation. + assert_codegen_enabled(result.runtime_profile, [1]) query = 'select %s(DISTINCT(%s_col)) from alltypesagg where day is not null' % ( agg_func, data_type) @@ -207,7 +214,7 @@ class TestAggregationQueries(ImpalaTestSuite): Required to run directly in python because the order in which results will be merged at the final, single-node aggregation step is non-deterministic (if the first phase is running on multiple nodes). Need to pull the result apart and - compare the actual items)""" + compare the actual items)""" exec_option = vector.get_value('exec_option') disable_codegen = exec_option['disable_codegen'] table_format = vector.get_value('table_format') @@ -263,6 +270,42 @@ class TestAggregationQueries(ImpalaTestSuite): # Verify codegen was enabled for all four stages of the aggregation. assert_codegen_enabled(result.runtime_profile, [1, 2, 4, 6]) +class TestWideAggregationQueries(ImpalaTestSuite): + """Test that aggregations with many grouping columns work""" + @classmethod + def get_workload(self): + return 'functional-query' + + @classmethod + def add_test_dimensions(cls): + super(TestWideAggregationQueries, cls).add_test_dimensions() + + cls.TestMatrix.add_dimension( + create_exec_option_dimension(disable_codegen_options=[False, True])) + + # File format doesn't matter for this test. + cls.TestMatrix.add_constraint( + lambda v: v.get_value('table_format').file_format == 'parquet') + + def test_many_grouping_columns(self, vector): + """Test that an aggregate with many grouping columns works""" + table_format = vector.get_value('table_format') + exec_option = vector.get_value('exec_option') + query = "select distinct * from widetable_1000_cols" + + # Ensure codegen is enabled. + result = self.execute_query(query, exec_option, table_format=table_format) + + # All rows should be distinct. + expected_result = widetable.get_data(1000, 10, quote_strings=True) + + types = parse_column_types(result.schema) + labels = parse_column_labels(result.schema) + expected = QueryTestResult(expected_result, types, labels, order_matters=False) + actual = QueryTestResult(parse_result_rows(result), types, labels, + order_matters=False) + assert expected == actual + class TestTPCHAggregationQueries(ImpalaTestSuite): # Uses the TPC-H dataset in order to have larger aggregations.