(译)搞定深度递归
23 Nov 2020
|
|
简单来说,作者写sql parse代码,可能需要分析表达式,但是表达式特别多,parse代码一半都是遍历二叉树,有递归的,这样递归深度就上去了
unique_ptr<Expression> analyzeExpression(AST* astNode) {
switch (astNode->getType()) {
case AST::BinaryExpression: return analyzeBinaryExpression(astNode->as<BinaryExpAST>());
case AST::CaseExpression: return analyzeCaseExpression(astNode->as<CaseExpAST>());
...
}
}
unique_ptr<Expression> analyzeBinaryExpression(BinaryExpAST* astNode) {
auto left = analyzeExpression(astNode->left);
auto right = analyzeExpression(astNode->right);
auto type = inferBinaryType(astNode->getOp(), left, right);
return make_unique<BinaryExpression>(astNode->getOp(), move(left), move(right), type);
}
表达式规模300000个,直接栈溢出了,不得不探索解决办法
__builtin_frame_address(0)
抓到堆栈溢出直接抛异常,解决的比较恶心
- 表达式太大,直接这个查询就挂了
- 其次,很多地方都这样遍历,不能确定哪里会有这种较大的场景,
- 在优化的过程中,树的结构可能会调整,说不定更深了,表达式更大了,为了优化作出的努力反而因为栈溢出停了
指定堆栈?更恶心了
-fsplit-stack
编译器帮忙维护堆栈吧,这个flag就相当于编译器自动的分堆栈,但是实际测试中,编译器直接内部错误(ICE) ,也没有别人使用过的案例,放弃
boost.context
最终解决方案,用户态堆栈,且不用自己维护,代码改成这个样子
unique_ptr<Expression> analyzeExpression(AST* astNode) {
if (StackGuard::needsNewStack())
return StackGuard::newStack([=]() { return analyzeExpression(astNode); });
... // unchanged
}
靠boost.context来切换堆栈,降低心智负担,代码也没有那么丑陋,可维护
ref
- 他们还发了论文 https://db.in.tum.de/~radke/papers/hugejoins.pdf 这个论文内容说优化的,不是上面的工程实践内容