如何提高 boost::spirit::x3 键值解析器的性能

2023-12-21

我正在使用以下方法解析键值对(类似于 HTTP 标头)boost::spirit::x3。当与我的手写解析器进行性能比较时,boost::spirit::x3比这慢大约 10%。

我正在使用 boost 1.61 和 GCC 6.1:

$ g++ -std=c++14 -O3 -I/tmp/boost_1_61_0/boost/ main.cpp  && ./a.out

phrase_parse 1.97432 microseconds
parseHeader 1.75742 microseconds

我怎样才能提高性能boost::spirit::x3基于解析器?

#include <iostream>
#include <string>
#include <map>
#include <chrono>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>

using header_map = std::map<std::string, std::string>; 

namespace parser
{
    namespace x3 = boost::spirit::x3;
    using x3::char_;
    using x3::lexeme;

    x3::rule<class map, header_map> const map = "msg";

    const auto key     = +char_("0-9a-zA-Z-");
    const auto value   = +~char_("\r\n");

    const auto header =(key >> ':' >> value >> lexeme["\r\n"]);
    const auto map_def = *header >> lexeme["\r\n"];

    BOOST_SPIRIT_DEFINE(map);
}


template <typename It>
void parseHeader(It& iter, It end, header_map& map)
{
    std::string key;
    std::string value;

    It last = iter;
    bool inKey = true;
    while(iter+1 != end)
    {
        if(inKey && *(iter+1)==':')
        {
            key.assign(last, iter+1);
            iter+=3;
            last = iter;
            inKey = false;
        }
        else if (!inKey && *(iter+1)=='\r' && *(iter+2)=='\n')
        {
            value.assign(last, iter+1);
            map.insert({std::move(key), std::move(value)});
            iter+=3;
            last = iter;
            inKey = true;
        }
        else if (inKey && *(iter)=='\r' && *(iter+1)=='\n') 
        {
            iter+=2;
            break;
        }
        else
        {
            ++iter;
        }
    }
}

template<typename F, typename ...Args>
double benchmark(F func, Args&&... args)
{
    auto start = std::chrono::system_clock::now();

    constexpr auto num = 10 * 1000 * 1000;
    for (std::size_t i = 0; i < num; ++i)
    {
        func(std::forward<Args>(args)...);
    }

    auto end = std::chrono::system_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);

    return duration.count() / (double)num;
}

int main()
{
    const std::size_t headerCount = 20;

    std::string str;
    for(std::size_t i = 0; i < headerCount; ++i)
    {
        std::string num = std::to_string(i);
        str.append("key" + num + ": " + "value" + num + "\r\n");
    }
    str.append("\r\n");

    double t1 = benchmark([&str]() {
        auto iter = str.cbegin();
        auto end = str.cend();

        header_map header;
        phrase_parse(iter, end, parser::map, boost::spirit::x3::ascii::blank, header);
        return header;
    });
    std::cout << "phrase_parse " << t1 << " microseconds"<< std::endl;

    double t2 = benchmark([&str]() {
        auto iter = str.cbegin();
        auto end = str.cend();

        header_map header;
        parseHeader(iter, end, header);
        return header;
    });
    std::cout << "parseHeader " << t2 << " microseconds"<< std::endl;
    return 0;
}

活生生的例子 http://coliru.stacked-crooked.com/a/12ef58fb74744364


这是一个固定的 x3 语法,它更接近于您手写的“解析器”:

const auto key     = +~char_(':');
const auto value   = *(char_ - "\r\n");

const auto header = key >> ':' >> value >> "\r\n";
const auto map    = *header >> "\r\n";

当然,还是更严格、更稳健。另外,不要用空格跳过器调用它,因为您的手动解析器也不会这样做。

这是我的盒子的性能测量结果:

统计数据平均为 2.5μs vs. 3.5μs。

完整代码

Using http://nonius.io http://nonius.io进行稳健的基准测试:

#include <iostream>
#include <string>
#include <map>
#include <nonius/benchmark.h++>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>

using header_map = std::map<std::string, std::string>; 

namespace parser
{
    namespace x3 = boost::spirit::x3;
    using x3::char_;

    const auto key     = +~char_(':');
    const auto value   = *(char_ - "\r\n");

    const auto header = key >> ':' >> value >> "\r\n";
    const auto map    = *header >> "\r\n";
}


template <typename It>
void parseHeader(It& iter, It end, header_map& map)
{
    std::string key;
    std::string value;

    It last = iter;
    bool inKey = true;
    while(iter+1 != end)
    {
        if(inKey && *(iter+1)==':')
        {
            key.assign(last, iter+1);
            iter+=3;
            last = iter;
            inKey = false;
        }
        else if (!inKey && *(iter+1)=='\r' && *(iter+2)=='\n')
        {
            value.assign(last, iter+1);
            map.insert({std::move(key), std::move(value)});
            iter+=3;
            last = iter;
            inKey = true;
        }
        else if (inKey && *(iter)=='\r' && *(iter+1)=='\n') 
        {
            iter+=2;
            break;
        }
        else
        {
            ++iter;
        }
    }
}

static auto const str = [] {
    std::string tmp;
    const std::size_t headerCount = 20;
    for(std::size_t i = 0; i < headerCount; ++i)
    {
        std::string num = std::to_string(i);
        tmp.append("key" + num + ": " + "value" + num + "\r\n");
    }
    tmp.append("\r\n");
    return tmp;
}();

NONIUS_BENCHMARK("manual", [](nonius::chronometer cm) {

    cm.measure([]() {
        auto iter = str.cbegin();
        auto end = str.cend();

        header_map header;
        parseHeader(iter, end, header);
        assert(header.size() == 20);
        return header.size();
    });
})

NONIUS_BENCHMARK("x3", [](nonius::chronometer cm) {

    cm.measure([] {
        auto iter = str.cbegin();
        auto end = str.cend();

        header_map header;
        parse(iter, end, parser::map, header);
        assert(header.size() == 20);
        return header.size();
    });
})

#include <nonius/main.h++>

我正在使用 gcc 5.4 和 Boost 1.61

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何提高 boost::spirit::x3 键值解析器的性能 的相关文章

随机推荐

  • python中自动嵌套for循环

    我知道可以使用同时访问两个集合 for i j in zip 1 2 3 4 5 6 print i j 1 4 2 5 3 6 我想做的是这样的 for i j in 1 2 3 4 5 6 print i j 1 4 1 5 1 6 2
  • 带有嵌套数组的绝对无引用的数组复制

    起初我以为arr slice 0 正在做一个deep未引用的副本 但它实际上只是做了一个shallow未引用的副本 因此如果数组包含嵌套数组 他们仍然被引用 https stackoverflow com questions 1045835
  • Azure AD B2C:无法将公司名称公开为令牌

    我使用 Azure AD B2C 和自定义策略来管理用户并提供登录体验 我注意到有一个内置 不是自定义扩展 属性名称companyName 我正在使用用户的公司信息进行更新 我可以通过图形 API 更新此字段值并分别检索它 更新此值后 我尝
  • 为什么 std::unordered_map 有一个保留方法?

    根据this https stackoverflow com questions 13049340 initializing a stdmap when the size is known in advance您不能预留空间std map
  • 如何对每个请求运行 shell 脚本?

    我想在每次 nginx 服务器收到任何 HTTP 请求时运行 shell 脚本 有什么简单的方法可以做到这一点吗 您可以通过以下方式执行 shell 脚本Luanginx conf 文件中的代码可实现此目的 您需要拥有HttpLua模块 h
  • 在 Android 上 `$cookies.put()` 需要 30 秒才能将 cookie 保存到磁盘上

    Context 我们有一个科尔多瓦应用程序加载一个在线网站 我们基本上使用 cordova 作为插件 我们使用以下方式存储 cookie cookies put 它在桌面浏览器和 iOS 上运行良好 只要我们在 曲奇饼 Problem 在
  • Neo4j 日期数据类型

    我使用的是 Neo4j 2 0 版本 假设 我有很多将日期作为其字段之一的记录 如果我们需要支持很多查询 例如两个特定日期之间的记录计数等 我认为我可能有 tpo 按日期字段对所有记录进行索引 它是否正确 那我该怎么办呢 RECORD 类型
  • 如何仅从命令行检查 JavaScript 代码是否存在语法错误?

    JavaScript 程序可以在 IDE 中检查错误或使用在线网络应用程序 https stackoverflow com questions 5279226 how to check client side javascript code
  • SQLAlchemy 和多数据库

    我有各种类似 但不相同 的数据库 并且想使用 SQLAlchemy 作为 标准化 访问的方式 数据库可能略有不同 例如列名上有唯一的前缀 或者它们可能有更大的差异并且缺少列 或者对于旧数据库 缺少整个表 我寻求帮助的与其说是 SQLAlch
  • 有水平的 UIRefreshControl 吗?

    你可以加UIRefreshControl to UICollectionView 或任何UIScrollView就此而言 将其添加到集合的子视图中 https stackoverflow com a 12502450 458193 UIRe
  • 为什么 Android 丢弃 TCP 数据包发生在 droid 5.x 中,而不是 4.x 中?

    我有一部 Android 智能手机 通过 WIFI 连接到嵌入式 AP 我正在使用 Linux 上运行 Tshark 的笔记本电脑嗅探 WIFI 流量 我每 100 毫秒传输小 234 字节 TCP 数据包 5 次 然后是 500 毫秒 没
  • 根据列中的条件将值分配给组

    我有一个如下所示的数据框 gt df data frame group c 1 1 1 2 2 2 3 3 3 date c 1 2 3 4 5 6 7 8 9 value c 3 4 3 4 5 6 6 4 9 gt df group d
  • 在 Windows 7 中找不到 IIS 服务

    当我进入 Windows 7 中的 Windows 服务窗口 开始 gt 管理工具 gt 服务 时 我找不到列出的 IIS 服务 我错过了什么吗 我只想重新启动服务 我有 IIS 管理器并且可以打开它 非常感谢任何帮助 谢谢 它被称为Wor
  • 如何在 Angular 的 HttpClient 中使用 reportProgress? [复制]

    这个问题在这里已经有答案了 我正在使用下载文件HTTP POST方法 我想调用另一种方法向最终用户显示下载进度 直到文件下载完成 如何使用reportProgress in HttpClient为了这 downfile file any O
  • WSL 下的 NFS 客户端 - mount.nfs:没有此类设备

    我在尝试挂 载 nfs 导出时收到以下错误 sudo mount 192 168 1 175 mnt nas mnt c nas mount nfs No such device 有想法该怎么解决这个吗 截至 2020 年 10 月 您可以
  • 如何将动画固定到位置?

    我尝试为 1 秒后修复的 DIV 制作动画 但我做不到 我希望一秒钟后名为 homepage hero module 的 div 从右向左滑动 正如您在 FIDDLE 中看到的那样 它在一秒钟后变为固定 那么如何制作动画呢 我尝试使用 cs
  • 如何在网格上添加自定义按钮并传递行值?

    我想向网格添加一个按钮 以便用户可以通过传入按钮行中的值来查看给定任务的时间表条目值 网格加载得很好 直到我将按钮添加到 columnCfgs 当按钮在那里时 我收到 Uncaught TypeError Object object Obj
  • JavaFX:如何刷新表?

    我在 JavaFX TableView 中刷新行样式时遇到问题 java版本 1 8 0 51 Java TM SE 运行时环境 版本 1 8 0 51 b16 Java HotSpot TM 服务器 VM 内部版本 25 51 b03 混
  • 如何使用 .NET Framework 3.5 中的数据注释对 C# 类进行属性验证?

    NET Framework 中是否有一种方法可以将某个方法或验证器传递给其类装饰的对象实例数据注释 http msdn microsoft com en us library system componentmodel dataannota
  • 如何提高 boost::spirit::x3 键值解析器的性能

    我正在使用以下方法解析键值对 类似于 HTTP 标头 boost spirit x3 当与我的手写解析器进行性能比较时 boost spirit x3比这慢大约 10 我正在使用 boost 1 61 和 GCC 6 1 g std c 1