能否通过Boost Interprocess共享boost::variant解析后的大型逻辑表达式?
问题描述
我有多个长度可达300K的大型逻辑表达式,格式示例如下:
( ( ( 1 ) and ( 5933 or 561 or 1641 ) ) or ( ( 71 or 1 or 15 or 20 ) and ( 436 ) ) or ( ( 398 or 22 or 33 ) ) )
这些表达式用Boost Spirit解析,单条解析耗时超一分钟。我希望离线完成解析,得到以下boost::variant类型的表达式结构:
typedef boost::variant <var, boost::recursive_wrapper<unop <op_not> >, boost::recursive_wrapper<binop<op_and> >, boost::recursive_wrapper<binop<op_or> > > expr;
需要把解析后的表达式分发到多台机器做实时计算,但这些机器无法承担初始解析耗时。我试过用unique_ptr把表达式写入Boost Interprocess的managed_mapped_file,但读取后计算时出现段错误;同时Boost序列化对超大型表达式失效,求可行方案。
可行方案
1. 自定义序列化格式(规避Boost序列化的递归/内存限制)
因为Boost序列化对超大型递归结构易出现栈溢出或内存耗尽问题,自行编写序列化逻辑更可控:
- 遍历
expr变体结构,用前缀/后缀表达式(如逆波兰式)将表达式序列化为扁平字节流:- 对于
var类型,直接序列化变量ID(例如4字节整数) - 对于
unop<op_not>,先写入标记位(如0x01),再序列化内部子表达式 - 对于
binop<op_and>/<op_or>,先写入对应标记位(0x02/0x03),再依次序列化左右子表达式
- 对于
- 反序列化时用栈结构还原:遇到变量压栈,遇到操作符弹出对应数量的子表达式构造节点后重新压栈,最终栈顶即为完整
expr对象。 - 该方式完全避免递归依赖,内存占用可控,适配超大型表达式场景。
2. 改用内存映射文件的结构化存储(修复段错误)
此前用unique_ptr写入managed_mapped_file出现段错误,核心原因是指针指向的内存不在共享映射区域内:
- 所有表达式节点(
var、unop、binop)必须从managed_mapped_file的内存分配器创建,不能使用默认堆内存(new/unique_ptr默认堆) - 定义表达式类型时,为递归包装器指定共享内存分配器:
template<typename Op> struct binop { Op op; expr left; expr right; // 用共享内存分配器构造 template<typename Alloc> binop(Op o, expr l, expr r, const Alloc& alloc) : op(o), left(l), right(r) {} }; - 解析时直接在
managed_mapped_file的内存空间中构建expr对象,完成后将根节点的偏移量(而非指针)写入映射文件的固定位置;其他机器读取时,通过偏移量在自身映射文件实例中定位根节点即可直接使用,避免指针失效问题。
3. 预编译表达式为字节码(极端场景最优解)
若表达式分发后逻辑固定,可将解析后的表达式转换为自定义字节码指令集:
- 定义简单指令:例如
LOAD_VAR(id)、OP_NOT、OP_AND、OP_OR - 遍历表达式生成字节码序列,示例表达式可生成:
LOAD_VAR(1), LOAD_VAR(5933), LOAD_VAR(561), OP_OR, LOAD_VAR(1641), OP_OR, OP_AND, LOAD_VAR(71), LOAD_VAR(1), OP_OR, LOAD_VAR(15), OP_OR, LOAD_VAR(20), OP_OR, LOAD_VAR(436), OP_AND, OP_OR, LOAD_VAR(398), LOAD_VAR(22), OP_OR, LOAD_VAR(33), OP_OR, OP_OR - 分发时仅传输字节码数组,目标机器用轻量级字节码解释器执行计算,完全无需解析Boost Spirit表达式,也无复杂变体结构依赖,性能与兼容性拉满。
内容的提问来源于stack exchange,提问作者user1474341
相关产品推荐
相关产品推荐

