NoisePage源码分析(未完待续)
源码仓库:https://github.com/cmu-db/noisepage
该项目于2023年在Github Archive,但我看有不少CMU的论文还是基于NoisePage上面改进,所以依然值得去剖析相关源码
相关网络资料:
noisepage源码阅读之Garbage Collection
编译环境
使用GCC9.3/Clang 8.0编译,CCCache作为缓存,测试系统为Ubuntu 20.04(Focal),使用CMake + Ninja编译套件
JIT环境是LLVM的MCJIT(ORC JIT要等LLVM14以后才有)
使用Jenkinss作为CI
提供DockerFile用于测试
third_party(第三方依赖)
libpg_query 用于SQL解析,这没的说
libcuckoo 高性能HashTable库
Google的FlatBuffers,用于定义 Apache Arrow项目中的消息格式规范generated
文件夹里面的代码由FlatBuffers的Compiler生成
而BW-Tree则是团队对于数据存储的实现,这里贴一段B+树和BW-Tree的不同:
特性 | B+树 | BW-Tree |
---|---|---|
并发机制 | 需加锁 | 无锁(基于CAS) |
更新方式 | 原地修改 | Delta节点追加 + 写时复制 |
崩溃恢复 | 较复杂 | 天然支持版本追踪与回滚 |
写放大 | 一定程度存在 | 减少写放大(无页面迁移) |
查询效率(高并发) | 可受锁影响 | 查询需遍历Delta链,视情况而定 |
utils
有三个文件夹,分别是execution, include, runner
runner
关于运行时(Runner)的基础配置
execution
运行环境配置,主要是关于LLVM的部分
从参数可以看出,支持打印AST和ByteCode
有一个名为TPL
的Compiler
llvm::cl::OptionCategory TPL_OPTIONS_CATEGORY("TPL Compiler Options", "Options for controlling the TPL compilation process."); // NOLINTllvm::cl::opt<bool> PRINT_AST("print-ast", llvm::cl::desc("Print the programs AST"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<bool> PRINT_TBC("print-tbc", llvm::cl::desc("Print the generated TPL Bytecode"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<bool> PRETTY_PRINT("pretty-print", llvm::cl::desc("Pretty-print the source from the parsed AST"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<bool> IS_SQL("sql", llvm::cl::desc("Is the input a SQL query?"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<bool> TPCH("tpch", llvm::cl::desc("Should the TPCH database be loaded? Requires '-schema' and '-data' directories."), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<std::string> DATA_DIR("data", llvm::cl::desc("Where to find data files of tables to load"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<std::string> INPUT_FILE(llvm::cl::Positional, llvm::cl::desc("<input file>"), llvm::cl::init(""), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<std::string> OUTPUT_NAME("output-name", llvm::cl::desc("Print the output name"), llvm::cl::init("schema10"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINTllvm::cl::opt<std::string> HANDLERS_PATH("handlers-path", llvm::cl::desc("Path to the bytecode handlers bitcode file"), llvm::cl::init("./bytecode_handlers_ir.bc"), llvm::cl::cat(TPL_OPTIONS_CATEGORY)); // NOLINT
在gen_opt_bc.cpp
中,主要关于LLVM Bitcode的处理
// This executable reads the unoptimized bitcode file and:// 1. Converts to an LLVM Module// 2. Removes the static global variable// 3. Modifies linkage types of all defined functions to LinkOnce// 4. Cleans up function arguments// 5. Writes out optimized module as bitcode file
有一个函数用于清除LLVM全局标记,我觉得可以记下
void RemoveGlobalUses(llvm::Module *module) { // When we created the original bitcode file, we forced all functions to be // generated by storing their address in a global variable. We delete this // variable now so the final binary can be made smaller by eliminating unused // ops. auto var = module->getGlobalVariable(GLOBAL_VAR_NAME); if (var != nullptr) { var->replaceAllUsesWith(llvm::UndefValue::get(var->getType())); var->eraseFromParent(); }
// Clang created a global variable holding all force-used items. Delete it. auto used = module->getGlobalVariable(LLVM_COMPILED_USED); if (used != nullptr) { used->eraseFromParent(); }}
在table_generator
子文件夹下面是关于表的生成与读取
表的生成基于C++模板
还携带一个GenerateTestTables()
的样例
test
下面有多个文件夹对应不同的测试,似乎使用CTest作为测试套件
binder
用于检查SQL与执行计划的解析,包括了CTE的Dependency Graph和Struct Statement Test
TEST_F(BinderCorrectnessTest, SelectStatementComplexTest) { // Test regular table name BINDER_LOG_DEBUG("Parsing sql query"); std::string select_sql = "SELECT A.A1, B.B2 FROM A INNER JOIN b ON a.a1 = b.b1 WHERE a1 < 100 " "GROUP BY A.a1, B.b2 HAVING a1 > 50 ORDER BY a1";
auto parse_tree = parser::PostgresParser::BuildParseTree(select_sql); auto statement = parse_tree->GetStatements()[0]; binder_->BindNameToNode(common::ManagedPointer(parse_tree), nullptr, nullptr); auto select_stmt = statement.CastManagedPointerTo<parser::SelectStatement>(); EXPECT_EQ(0, select_stmt->GetDepth());
// Check select_list BINDER_LOG_DEBUG("Checking select list"); auto col_expr = select_stmt->GetSelectColumns()[0].CastManagedPointerTo<parser::ColumnValueExpression>(); EXPECT_EQ(col_expr->GetDatabaseOid(), db_oid_); // A.a1 EXPECT_EQ(col_expr->GetTableOid(), table_a_oid_); // A.a1 EXPECT_EQ(col_expr->GetColumnOid(), catalog::col_oid_t(1)); // A.a1; columns are indexed from 1 EXPECT_EQ(execution::sql::SqlTypeId::Integer, col_expr->GetReturnValueType()); EXPECT_EQ(0, col_expr->GetDepth());
此外就不一一列举了
src(源码)
目录基本和Test差不多
接下来我挑选几个我感兴趣的部分看看
Binder
以访问者模式实现SQL的解析
namespace noisepage {void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::AggregateExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::CaseExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::ColumnValueExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::ComparisonExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::ConjunctionExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::ConstantValueExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::DefaultValueExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::DerivedValueExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}void binder::SqlNodeVisitor::Visit(common::ManagedPointer<parser::FunctionExpression> expr) { expr->AcceptChildren(common::ManagedPointer(this));}
Execution
execution
下面还有诸多子一级文件夹
vm
文件夹下边有很多关于bytecode的文件(感觉是Noispage有自己的一套ByteCode体系?)
BytecodeEmitter
的部分代码如下
void BytecodeEmitter::EmitDeref(Bytecode bytecode, LocalVar dest, LocalVar src) { NOISEPAGE_ASSERT(bytecode == Bytecode::Deref1 || bytecode == Bytecode::Deref2 || bytecode == Bytecode::Deref4 || bytecode == Bytecode::Deref8, "Bytecode is not a Deref code"); EmitAll(bytecode, dest, src);}
void BytecodeEmitter::EmitDerefN(LocalVar dest, LocalVar src, uint32_t len) { EmitAll(Bytecode::DerefN, dest, src, len);}
void BytecodeEmitter::EmitAssign(Bytecode bytecode, LocalVar dest, LocalVar src) { NOISEPAGE_ASSERT(bytecode == Bytecode::Assign1 || bytecode == Bytecode::Assign2 || bytecode == Bytecode::Assign4 || bytecode == Bytecode::Assign8, "Bytecode is not an Assign code"); EmitAll(bytecode, dest, src);}
void BytecodeEmitter::EmitAssignImm1(LocalVar dest, int8_t val) { EmitAll(Bytecode::AssignImm1, dest, val); }
void BytecodeEmitter::EmitAssignImm2(LocalVar dest, int16_t val) { EmitAll(Bytecode::AssignImm2, dest, val); }
void BytecodeEmitter::EmitAssignImm4(LocalVar dest, int32_t val) { EmitAll(Bytecode::AssignImm4, dest, val); }
void BytecodeEmitter::EmitAssignImm8(LocalVar dest, int64_t val) { EmitAll(Bytecode::AssignImm8, dest, val); }
void BytecodeEmitter::EmitAssignImm4F(LocalVar dest, float val) { EmitAll(Bytecode::AssignImm4F, dest, val); }
void BytecodeEmitter::EmitAssignImm8F(LocalVar dest, double val) { EmitAll(Bytecode::AssignImm8F, dest, val); }
void BytecodeEmitter::EmitUnaryOp(Bytecode bytecode, LocalVar dest, LocalVar input) { EmitAll(bytecode, dest, input); }
void BytecodeEmitter::EmitBinaryOp(Bytecode bytecode, LocalVar dest, LocalVar lhs, LocalVar rhs) { EmitAll(bytecode, dest, lhs, rhs);}
而在llvm_engine.cpp
中,对于类型Type
有明确的映射(我想也可以完成ByteCode往LLVM IR的映射?)
class LLVMEngine::TypeMap { public: explicit TypeMap(llvm::Module *module) : module_(module) { llvm::LLVMContext &ctx = module->getContext(); type_map_["nil"] = llvm::Type::getVoidTy(ctx); type_map_["bool"] = llvm::Type::getInt8Ty(ctx); type_map_["int8"] = llvm::Type::getInt8Ty(ctx); type_map_["int16"] = llvm::Type::getInt16Ty(ctx); type_map_["int32"] = llvm::Type::getInt32Ty(ctx); type_map_["int64"] = llvm::Type::getInt64Ty(ctx); type_map_["int128"] = llvm::Type::getInt128Ty(ctx); type_map_["uint8"] = llvm::Type::getInt8Ty(ctx); type_map_["uint16"] = llvm::Type::getInt16Ty(ctx); type_map_["uint32"] = llvm::Type::getInt32Ty(ctx); type_map_["uint64"] = llvm::Type::getInt64Ty(ctx); type_map_["uint128"] = llvm::Type::getInt128Ty(ctx); type_map_["float32"] = llvm::Type::getFloatTy(ctx); type_map_["float64"] = llvm::Type::getDoubleTy(ctx); type_map_["string"] = llvm::Type::getInt8PtrTy(ctx); }
/** No copying or moving this class. */ DISALLOW_COPY_AND_MOVE(TypeMap);
llvm::Type *VoidType() { return type_map_["nil"]; } llvm::Type *BoolType() { return type_map_["bool"]; } llvm::Type *Int8Type() { return type_map_["int8"]; } llvm::Type *Int16Type() { return type_map_["int16"]; } llvm::Type *Int32Type() { return type_map_["int32"]; } llvm::Type *Int64Type() { return type_map_["int64"]; } llvm::Type *UInt8Type() { return type_map_["uint8"]; } llvm::Type *UInt16Type() { return type_map_["uint16"]; } llvm::Type *UInt32Type() { return type_map_["uint32"]; } llvm::Type *UInt64Type() { return type_map_["uint64"]; } llvm::Type *Float32Type() { return type_map_["float32"]; } llvm::Type *Float64Type() { return type_map_["float64"]; } llvm::Type *StringType() { return type_map_["string"]; }
llvm::Type *GetLLVMType(const ast::Type *type);
private: // Given a non-primitive builtin type, convert it to an LLVM type llvm::Type *GetLLVMTypeForBuiltin(const ast::BuiltinType *builtin_type);
// Given a struct type, convert it into an equivalent LLVM struct type llvm::StructType *GetLLVMStructType(const ast::StructType *struct_type);
// Given a TPL function type, convert it into an equivalent LLVM function type llvm::FunctionType *GetLLVMFunctionType(const ast::FunctionType *func_type);
private: llvm::Module *module_; std::unordered_map<std::string, llvm::Type *> type_map_;};
从AST完成向LLVM Struct的构建
llvm::StructType *LLVMEngine::TypeMap::GetLLVMStructType(const ast::StructType *struct_type) { // Collect the fields here llvm::SmallVector<llvm::Type *, 8> fields;
for (const auto &field : struct_type->GetAllFields()) { fields.push_back(GetLLVMType(field.type_)); }
return llvm::StructType::create(fields);}
从下面关于LLVM的TargetMachine可以看到,要想向量化操作还是依赖编译器的自动向量化
LVMEngine::CompiledModuleBuilder::CompiledModuleBuilder(const CompilerOptions &options, const BytecodeModule &tpl_module) : options_(options), tpl_module_(tpl_module), target_machine_(nullptr), context_(std::make_unique<llvm::LLVMContext>()), llvm_module_(nullptr), type_map_(nullptr) { // // We need to create a suitable TargetMachine for LLVM to before we can JIT // TPL programs. At the moment, we rely on LLVM to discover all CPU features // e.g., AVX2 or AVX512, and we make no assumptions about symbol relocations. // // TODO(pmenon): This may change with LLVM8 that comes with // TargetMachineBuilders // TODO(pmenon): Alter the flags as need be //
const std::string target_triple = llvm::sys::getProcessTriple();
{ std::string error; auto *target = llvm::TargetRegistry::lookupTarget(target_triple, error); if (target == nullptr) { EXECUTION_LOG_ERROR("LLVM: Unable to find target with target_triple {}", target_triple); return; }
// Collect CPU features llvm::StringMap<bool> feature_map; if (bool success = llvm::sys::getHostCPUFeatures(feature_map); !success) { EXECUTION_LOG_ERROR("LLVM: Unable to find all CPU features"); return; }
llvm::SubtargetFeatures target_features; for (const auto &entry : feature_map) { target_features.AddFeature(entry.getKey(), entry.getValue()); }
EXECUTION_LOG_TRACE("LLVM: Discovered CPU features: {}", target_features.getString());
// Both relocation=PIC or JIT=true work. Use the latter for now. llvm::TargetOptions target_options; llvm::Optional<llvm::Reloc::Model> reloc; const llvm::CodeGenOpt::Level opt_level = llvm::CodeGenOpt::Aggressive; target_machine_.reset(target->createTargetMachine(target_triple, llvm::sys::getHostCPUName(), target_features.getString(), target_options, reloc, {}, opt_level, true)); NOISEPAGE_ASSERT(target_machine_ != nullptr, "LLVM: Unable to find a suitable target machine!"); }
// // We've built a TargetMachine we use to generate machine code. Now, we // load the pre-compiled bytecode module containing all the TPL bytecode // logic. We add the functions we're about to compile into this module. This // module forms the unit of JIT. //
{ auto memory_buffer = llvm::MemoryBuffer::getFile(GetEngineSettings()->GetBytecodeHandlersBcPath()); if (auto error = memory_buffer.getError()) { EXECUTION_LOG_ERROR("There was an error loading the handler bytecode: {}", error.message()); }
auto module = llvm::parseBitcodeFile(*(memory_buffer.get()), *context_); if (!module) { auto error = llvm::toString(module.takeError()); EXECUTION_LOG_ERROR("{}", error); throw std::runtime_error(error); }
llvm_module_ = std::move(module.get()); llvm_module_->setModuleIdentifier(tpl_module.GetName()); llvm_module_->setSourceFileName(tpl_module.GetName() + ".tpl"); llvm_module_->setDataLayout(target_machine_->createDataLayout()); llvm_module_->setTargetTriple(target_triple); }
type_map_ = std::make_unique<TypeMap>(llvm_module_.get());}
不太理解为什么还需要重新生成CFG,也许是发现函数过于复杂要拆分?我看更多是关于分支和LLVM Block联系的相关代码
void LLVMEngine::CompiledModuleBuilder::BuildSimpleCFG(const FunctionInfo &func_info, std::map<std::size_t, llvm::BasicBlock *> *blocks) { // Before we can generate LLVM IR, we need to build a control-flow graph (CFG) for the function. // We do this construction directly from the TPL bytecode using a vanilla DFS and produce an // ordered map ('blocks') from bytecode position to an LLVM basic block. Each entry in the map // indicates the start of a basic block.
// We use this vector as a stack for DFS traversal llvm::SmallVector<std::size_t, 16> bb_begin_positions = {0};
for (auto iter = tpl_module_.GetBytecodeForFunction(func_info); !bb_begin_positions.empty();) { std::size_t begin_pos = bb_begin_positions.back(); bb_begin_positions.pop_back();
// We're at what we think is the start of a new basic block. Scan it until we find a terminal // instruction. Once we do, for (iter.SetPosition(begin_pos); !iter.Done(); iter.Advance()) { Bytecode bytecode = iter.CurrentBytecode();
// If the bytecode isn't a terminal for the block, continue until we reach one if (!Bytecodes::IsTerminal(bytecode)) { continue; }
// Return? if (Bytecodes::IsReturn(bytecode)) { break; }
// Unconditional branch? if (Bytecodes::IsUnconditionalJump(bytecode)) { std::size_t branch_target_pos = iter.GetPosition() + Bytecodes::GetNthOperandOffset(bytecode, 0) + iter.GetJumpOffsetOperand(0);
if (blocks->find(branch_target_pos) == blocks->end()) { (*blocks)[branch_target_pos] = nullptr; bb_begin_positions.push_back(branch_target_pos); }
break; }
// Conditional branch? if (Bytecodes::IsConditionalJump(bytecode)) { std::size_t fallthrough_pos = iter.GetPosition() + iter.CurrentBytecodeSize();
if (blocks->find(fallthrough_pos) == blocks->end()) { bb_begin_positions.push_back(fallthrough_pos); (*blocks)[fallthrough_pos] = nullptr; }
std::size_t branch_target_pos = iter.GetPosition() + Bytecodes::GetNthOperandOffset(bytecode, 1) + iter.GetJumpOffsetOperand(1);
if (blocks->find(branch_target_pos) == blocks->end()) { bb_begin_positions.push_back(branch_target_pos); (*blocks)[branch_target_pos] = nullptr; }
break; } } }}
optimize()
处放上LLVM的相关优化
void LLVMEngine::CompiledModuleBuilder::Optimize() { llvm::legacy::FunctionPassManager function_passes(llvm_module_.get());
// Add the appropriate TargetTransformInfo. function_passes.add(llvm::createTargetTransformInfoWrapperPass(target_machine_->getTargetIRAnalysis()));
// Build up optimization pipeline. llvm::PassManagerBuilder pm_builder; uint32_t opt_level = 3; uint32_t size_opt_level = 0; bool disable_inline_hot_call_site = false; pm_builder.OptLevel = opt_level; pm_builder.Inliner = llvm::createFunctionInliningPass(opt_level, size_opt_level, disable_inline_hot_call_site); pm_builder.populateFunctionPassManager(function_passes);
// Add custom passes. Hand-selected based on empirical evaluation. function_passes.add(llvm::createInstructionCombiningPass()); function_passes.add(llvm::createReassociatePass()); function_passes.add(llvm::createGVNPass()); function_passes.add(llvm::createCFGSimplificationPass()); function_passes.add(llvm::createAggressiveDCEPass()); function_passes.add(llvm::createCFGSimplificationPass());
// Run optimization passes on all functions. function_passes.doInitialization(); for (llvm::Function &func : *llvm_module_) { function_passes.run(func); } function_passes.doFinalization();}
util
下面的cpu_info
用于读取机器信息(但感觉只是用来确定CPU三级缓存大小)
void CpuInfo::InitCacheInfo() {#ifdef __APPLE__ // Lookup cache sizes. std::size_t len = 0; sysctlbyname("hw.cachesize", nullptr, &len, nullptr, 0); auto data = std::make_unique<uint64_t[]>(len); sysctlbyname("hw.cachesize", data.get(), &len, nullptr, 0); NOISEPAGE_ASSERT(len / sizeof(uint64_t) >= 3, "Expected three levels of cache!");
// Copy data for (uint32_t idx = 0; idx < K_NUM_CACHE_LEVELS; idx++) { cache_sizes_[idx] = data[idx]; }
// Lookup cache line sizes. std::size_t linesize; std::size_t sizeof_linesize = sizeof(linesize); sysctlbyname("hw.cachelinesize", &linesize, &sizeof_linesize, nullptr, 0); for (auto &cache_line_size : cache_line_sizes_) { cache_line_size = linesize; }#else // Use sysconf to determine cache sizes. cache_sizes_[L1_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL1_DCACHE_SIZE)); cache_sizes_[L2_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL2_CACHE_SIZE)); cache_sizes_[L3_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL3_CACHE_SIZE));
cache_line_sizes_[L1_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL1_DCACHE_LINESIZE)); cache_line_sizes_[L2_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL2_CACHE_LINESIZE)); cache_line_sizes_[L3_CACHE] = static_cast<uint32_t>(sysconf(_SC_LEVEL3_CACHE_LINESIZE));#endif}
一个暂时无法使用的csv_reader.cpp
// #include "execution/util/csv_reader.h" Fix later.
file.cpp
直接通过POSIX接口写入文件
int32_t File::Write(const std::byte *data, std::size_t len) const { NOISEPAGE_ASSERT(IsOpen(), "File must be open before reading"); return HANDLE_EINTR(write(fd_, data, len));}
int64_t File::Seek(File::Whence whence, int64_t offset) const { static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits"); return lseek(fd_, static_cast<off_t>(offset), static_cast<int32_t>(whence));}
bool File::Flush() const {#if defined(OS_LINUX) return HANDLE_EINTR(fdatasync(fd_)) == 0;#else return HANDLE_EINTR(fsync(fd_)) == 0;#endif}
而在vector_util.cpp
中,则是关于AVX向量化的操作,也就是向量化操作并不是在JIT中完成,而是实现做好向量化后再塞入JIT引擎
uint32_t VectorUtil::BitVectorToSelectionVectorDenseAvX2(const uint64_t *bit_vector, uint32_t num_bits, sel_t *sel_vector) { // Vector of '8's = [8,8,8,8,8,8,8] const auto eight = _mm_set1_epi16(8);
// Selection vector write index auto idx = _mm_set1_epi16(0);
// Selection vector size uint32_t k = 0;
const uint32_t num_words = common::MathUtil::DivRoundUp(num_bits, 64); for (uint32_t i = 0; i < num_words; i++) { uint64_t word = bit_vector[i]; for (uint32_t j = 0; j < 8; j++) { const auto mask = static_cast<uint8_t>(word); word >>= 8u; const __m128i match_pos_scaled = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&simd::K8_BIT_MATCH_LUT[mask])); const __m128i match_pos = _mm_cvtepi8_epi16(match_pos_scaled); const __m128i pos_vec = _mm_add_epi16(idx, match_pos); idx = _mm_add_epi16(idx, eight); _mm_storeu_si128(reinterpret_cast<__m128i *>(sel_vector + k), pos_vec); k += BitUtil::CountPopulation(static_cast<uint32_t>(mask)); } }
return k;}
在sql
文件夹下面的memorypool.cpp
,用的是std::calloc
和std::malloc
分配内存,可在CMakeLists.txt
中我记得是有开启jemalloc
的,似乎并没有用上
void *MemoryPool::AllocateAligned(const std::size_t size, const std::size_t alignment, const bool clear) { void *buf = nullptr;
if (size >= mmap_threshold.load(std::memory_order_relaxed)) { buf = util::Memory::MallocHuge(size, true); NOISEPAGE_ASSERT(buf != nullptr, "Null memory pointer"); // No need to clear memory, guaranteed on Linux } else { if (alignment < MIN_MALLOC_ALIGNMENT) { if (clear) { buf = std::calloc(size, 1); } else { buf = std::malloc(size); } } else { buf = util::Memory::MallocAligned(size, alignment); if (clear) { std::memset(buf, 0, size); } } }
在self_driving
文件夹下,有关于论文中提到的冷热切换的实现,而结合Script
代码来看,似乎模型可以通过Python进行调整
关于Arrow
storage里面基本都是关于Arrow的
这玩意,又是Arrow,又是LLVM?
数据在每个block被组织成PAX(行列混存)格式,一个tuple的所有列的数据都在这个block,每个block有一个layout对象,由下面3个部分组成
总结
代码质量很高,不懂得地方看下注释基本都能反应过来
但由于LLVM使用的版本太老,不确定程序是否还能运行起来