You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升2万条正则指纹规则数组的查询性能?

如何提升指纹规则查询的效率?

问题背景

我需要从包含2万余条指纹记录的文件读取输入,记录格式示例如下:

abc     Body="123"
think       Header="think"
xxx         Response=="xxx"
fff         Body!="abc"

将这些记录加载到vector<Rule>中,每条记录动态生成正则表达式,再通过QueryObject对规则集进行查询。

现有代码结构

Rule结构体与加载逻辑

// 对应记录格式:abc  Body="123"
struct Rule
{
    std::string name; // abc
    std::string type; // Body
    std::string op;   // =, ==, !=,根据操作符执行匹配逻辑
    std::string rule; // "123"
    std::shared_ptr<re2::RE2> reg;
};

std::vector<Rule> rules = load("fingerprint.txt");

// 加载函数实现
std::vector<Rule> load(const std::string& filename)
{
    std::vector<Rule> res;
    std::ifstream file(filename);
    std::string line;

    while(std::getline(file, line))
    {
        Rule rule;
        // 解析行内容,示例:line = "abc   Body=\"123\""
        // rule.name = "abc"; rule.type = "Body"; rule.op = "="; rule.rule = "\"123\""
        rule = parse_line(line);
        rule.reg.reset(new re2::RE2(rule.rule));
        res.push_back(rule);
    }
    return res;
}

查询逻辑

struct QueryObject
{
    std::string header;
    std::string body;
    std::string title;
    std::string response;
};

// 根据规则类型获取对应的查询字符串
std::string get_query_str(const Rule& rule, const QueryObject& obj)
{
    if(rule.type=="Header") return obj.header;
    if(rule.type=="Body") return obj.body;
    // 其他类型的判断逻辑
}

// 单线程查询函数
std::vector<std::string> query(const QueryObject& obj)
{
    std::vector<std::string> result;
    std::string query_str;
    for(size_t i = 0; i < rules.size(); ++i)
    {
        const Rule &rule = rules[i];
        query_str = get_query_str(rule, obj);
        if (rule.op == "=" 
            && RE2::PartialMatch(query_str, *rule.reg))
        {
            result.push_back(rule.name);
        }
        else if(rule.op == "!=" 
                && !RE2::PartialMatch(query_str, *rule.reg))
        {
            result.push_back(rule.name);
        }
        else if(rule.op == "==" && query_str == rule.rule)
        {
            result.push_back(rule.name);
        }
    }
    return result;
}

多线程优化尝试

我尝试拆分规则集进行多线程查询,但提升效果极不明显:单线程查询耗时6980ms,2线程仅降至6647ms。伪代码如下:

// 拆分后的查询函数
std::vector<std::string> query(const QueryObject& obj, size_t begin, size_t end)
{
    std::vector<std::string> result;
    std::string query_str;
    for(size_t i = begin; i < end; ++i)
    {
        const Rule &rule = rules[i];
        query_str = get_query_str(rule, obj);
        // 与单线程相同的匹配逻辑
    }
    return result;
}

// 调用方式
QueryObject obj;
auto task1 = thread_pool.enque(query, obj, 0, rules.size()/2);
auto task2 = thread_pool.enque(query, obj, rules.size()/2+1, rules.size());

// 合并结果
auto res1 = task1.get();
auto res2 = task2.get();
res1.insert(res1.end(), res2.begin(), res2.end());

环境信息

  • 编译器:g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  • 构建工具:xmake
  • 编译命令:
xmake f -p linux -a x86_64 -m release
xmake build -w my_app
  • 生成的CXXFLAGS:
fingerprint_CXXFLAGS=-m64 -fvisibility=hidden -fvisibility-inlines-hidden -O2 -isystem /home/hsiao/.xmake/packages/f/fmt/10.1.1/include  -fomit-frame-pointer -DNDEBUG
  • xmake.lua配置:
add_rules("mode.debug", "mode.release")
add_requires("fmt","re2","spdlog","libcurl","openssl","gtest","nlohmann_json")
add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"})

if is_mode("debug") then 
    add_defines("DEBUG")
    set_symbols("debug")
    set_optimize("none")
end 

if is_mode("release") then 
    set_strip("all")
    add_cxflags("-fomit-frame-pointer")
    add_mxflags("-fomit-frame-pointer")
    set_optimize("faster")
end 

target("analyze_fingerprint")
    set_kind("binary")
    add_files("src/*.cpp")
    add_packages("fmt","re2","spdlog","nlohmann_json")
    set_targetdir("bin")
  • 计时代码:
QueryObject obj;
obj.title="ruijie";

auto begin = std::chrono::system_clock::now();
auto result = query(obj);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> duration = end - begin;

优化建议

1. 规则预处理与分类

  • 按类型分组:将规则按type(Header/Body/Title/Response)分组存储,比如用std::unordered_map<std::string, std::vector<Rule>> type_groups。查询时仅遍历当前QueryObject中涉及的类型对应的规则集,避免遍历全部2万条规则。例如查询title时,只处理type="Title"的规则。
  • 按操作符分组:在类型分组的基础上,再按op(=、、!=)细分,减少循环内的条件判断次数,比如每个类型分组下再拆分为eq_rules()、partial_rules(=)、ne_rules(!=)三个子集合。

2. 减少正则匹配开销

  • 区分正则与普通字符串:如果rule.rule是普通字符串(不含正则语法),直接存储原始字符串,用std::string::find代替RE2::PartialMatch,普通字符串查找的速度远快于正则匹配。加载规则时可以先判断是否包含正则元字符(如.*+?等),再决定是否编译正则。
  • 修正==匹配逻辑:当前代码中rule.rule是带引号的(比如"123"),而query_str是obj.body的原始内容,直接比较会导致匹配失败。需要在parse_line时去掉引号,存储原始字符串用于==比较。
  • 优化正则编译选项:创建RE2时传入优化选项,例如:
    RE2::Options opts;
    opts.set_never_capture(true); // 禁用捕获组,提升匹配速度
    opts.set_case_sensitive(false); // 根据需求设置大小写敏感
    rule.reg.reset(new re2::RE2(rule.rule, opts));
    

3. 优化多线程实现

  • 避免不必要的拷贝:线程池传递QueryObject时使用std::ref(obj),避免大字符串(如body)的拷贝开销:
    auto task1 = thread_pool.enque(query, std::ref(obj), 0, rules.size()/2);
    
  • 按CPU核心数拆分任务:不要只拆成2个线程,而是根据std::thread::hardware_concurrency()获取核心数,均匀拆分规则集,提升并行度。
  • 优化结果合并:每个线程预先预留结果空间,合并时直接拼接,减少内存分配开销。或者使用线程安全的结果容器(如分段vector),避免锁的频繁竞争。

4. 编译与架构优化

  • 升级编译优化级别:在xmake.lua的release模式中,将优化级别从faster(对应-O2)改为fastest(对应-O3),并添加-march=native让编译器针对当前CPU架构优化:
    if is_mode("release") then 
        set_strip("all")
        add_cxflags("-fomit-frame-pointer", "-O3", "-march=native")
        add_mxflags("-fomit-frame-pointer", "-O3", "-march=native")
        set_optimize("fastest")
    end 
    
  • 确保RE2启用硬件加速:检查RE2的编译选项,确认启用了SSE4.2、AVX等指令集支持,这些指令能大幅提升正则匹配的速度。

5. 内存布局与缓存优化

  • 调整Rule结构体成员顺序:将频繁访问的成员(type、op、reg)放在一起,减少缓存行失效:
    struct Rule
    {
        std::string name;
        std::string type;
        std::string op;
        std::shared_ptr<re2::RE2> reg;
        std::string rule; // 不频繁访问的成员放在最后
    };
    
  • 预分配vector空间:加载规则时,提前调用res.reserve(20000),避免vector多次扩容的内存拷贝开销。
  • 返回字符串引用:修改get_query_str返回const std::string&,避免每次查询都拷贝字符串:
    const std::string& get_query_str(const Rule& rule, const QueryObject& obj)
    {
        if(rule.type=="Header") return obj.header;
        if(rule.type=="Body") return obj.body;
        // 其他类型逻辑
    }
    

6. 避免不必要的匹配

  • 对于!=操作,如果是普通字符串,先用find快速判断是否存在,不存在则直接加入结果,比正则匹配更快。

内容的提问来源于stack exchange,提问作者programmerHsiao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 04:28:09