Simdjson:一个超高速的JSON解析工具

        2019-03-24 55517人围观 ,发现 5 个不明物体 工具

        JSON文档在Internet上无处不在,服务器花费大量时间来解析这些文档。我们希望在进行完全验证(包括字符编码)时尽可能使用常用的SIMD指令来加速JSON本身的解析。

        表现结果

        simdjson使用的指令比最先进的解析器RapidJSON少四分之三,比sajson少百分之五十。据我们所知,simdjson是第一个在商用处理器上以每秒千兆字节运行的完全验证的JSON解析器。

        gbps.png

        在Skylake处理器上,twitter.json文件上各种处理器的解析速度(以GB / s为单位)如下。

        解析器 GB /秒
        simdjson 2.2
        RapidJSON编码验证 0.51
        RapidJSON编码验证,原位 0.71
        sajson(原状,动态) 0.70
        sajson(insitu,static) 0.97
        dropbox 0.14
        FASTJSON 0.26
        gason 0.85
        ultrajson 0.42
        jsmn 0.28
        cJSON 0.34

        要求

        我们通过Visual Studio 2017或更高版本支持Linux或macOS等平台以及Windows;

        带有AVX2的处理器(即,2013年发布的Haswell微体系结构的Intel处理器和2017年发布的Zen微体系结构的AMD处理器);

        最近的C ++编译器(例如,GNU GCC或LLVM CLANG或Visual Studio 2017),我们假设C ++ 17。GNU GCC 7或更高版本或LLVM的clang 6或更高版?#23613;?/p>

        License

        此代码在Apache License 2.0下提供。

        在Windows下,我们使用windows/dirent_portable.h文件(在我们的库代码之外)构建一些工具

        代码示例

        #include "simdjson/jsonparser.h"
        
        /...
        
        const char * filename = ... //
        
        //使用您想要的任何方?#20132;?#21462;JSON文档的字符串
        std::string_view p = get_corpus(filename);
        ParsedJson pj;
        pj.allocateCapacity(p.size());//分配内存以解析p.size()字节
        const int res = json_parse(p, pj); //进行解析,成功时返回0 
        //解析完成!
        if(res!= 0){
             //您可以使用“simdjson / simdjson.h”标头来访问错误消息 
           std::cout << "Error parsing:" << simdjson::errorMsg(res) << std::endl;
        }
        //你可以安全地删除字符串内容
        free((void*)p.data());
        //可以在这里使用ParsedJson文档
        // js可以与其他json_parse调用一起使用。
        

        如果您不介意为每个新的JSON文档分配内存开销,也可以使用更简单的API:

        #include "simdjson/jsonparser.h"
        
        / ...
        
        const char * filename = ... //
        std::string_view p = get_corpus(filename);
        ParsedJson pj = build_parsed_json(p);  //进行解析
        //此时你不再需要p,可以执行aligned_free((void *)p.data())
        if( ! pj.isValid() ) {
             //出错了 
        }
        

        用法:简单的版本

        有关用法,请参阅“singleheader”存储库的文件“amalgamation_demo.cpp”。这不需要特定的构建系?#24120;?#21482;需在包含路径中复制项目中的文件即可。然后,您可以非常简单地包含它们:

        #include <iostream>
        #include "simdjson.h"
        #include "simdjson.cpp"
        int main(int argc, char *argv[]) {
          const char * filename = argv[1];
          std::string_view p = get_corpus(filename);
          ParsedJson pj = build_parsed_json(p); // do the parsing
          if( ! pj.isValid() ) {
            std::cout << "not valid" << std::endl;
          } else {
            std::cout << "valid" << std::endl;
          }
          return EXIT_SUCCESS;
        }
        

        我们需要AVX2指令的硬件支持。您必须确保指示编译器根据需要使用这些说明。在GNU GCC或LLVM clang等编译器下,-march=native最近的Intel处理器(Haswell或更好)上使用的标志就足够了。为了便于二进制文件的可移植性,您还可以直接指定Haswell处理器(-march=haswell)。您也可以使用标志-mavx2 -mbmi2。在Visual Studio下,您需要定位x64并添加标志/arch:AVX2

        注意:在某些设置中,可能需要预编译simdjson.cpp而不是包含它。

        用法(在Linux或macOS等平台上使用旧版Makefile)

        要求:最近的clang或gcc,和make。我们建议至少使用GNU GCC / G ++ 7或LLVM clang 6.需要像Linux或macOS这样的系统。

        测试:

        make
        make test
        

        要运行基准测试:

        make parse
        ./parse jsonexamples/twitter.json
        

        在Linux下,该parse命令提供了性能计数器的详细分析。

        运行比较基准测试(与其他解析器):

        make benchmark
        

        用法(在Linux或macOS等平台上使用CMake)

        要求:我们需要最新版本的cmake。在macOS上,安装cmake的最简单方法可能是使用brew然后键入

        brew install cmake
        

        在Linux上有一个相同的Brew也可以以相同的方式工作

        你需要一个像clang或gcc这样的最新编译器。我们建议至少使用GNU GCC / G ++ 7或LLVM clang 6.例如,您可以使用brew安装最新的编译器:

        brew install [email protected]
        

        可选:您需要通过设置CC和CXX变量告诉cmake您希望使用哪个编译器。bash下,你可以用诸如命令这样做export CC=gcc-7export CXX=g++-7

        构建:在项目存储库中,执行以下操作:

        mkdir build
        cd build
        cmake ..
        make
        make test
        

        默认情况下,它构建一个共享库(例如,Linux上的libsimdjson.so)。

        您可以构建一个静态库:

        mkdir buildstatic
        cd buildstatic
        cmake -DSIMDJSON_BUILD_STATIC=ON ..
        make
        make test
        

        在某些情况下,您可能希望指定编译器,尤其是在系统上的默认编译器太旧的情况下。您可以按以下步骤操作:

        brew install [email protected]
        mkdir build
        cd build
        export CXX=g++-8 CC=gcc-8
        cmake ..
        make
        make test
        

        用法(使用Visual Studio在Windows上进行CMake)

        我们假设您有一台普通的Windows PC,至少包含Visual Studio 2017和支持AVX2的x64处理器(2013 Intel Haswell或更高版本)。

        从GitHub获取simdjson代码,例如,使用GitHub Desktop克隆它;

        安装CMake。安装时,请确保cmake?#29992;?#20196;行询问是否可用。请选择最新版本的cmake;

        在simdjson中创建一个子目录,例如VisualStudio;

        使用shell,转到这个新创建的目录;

        cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..VisualStudio存储库中键入shell 。(或者,如果要构建DLL,可以使用命令行cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..

        最后一个命令在新创建的目录中创建了一个Visual Studio解决方案文件(例如simdjson.sln)。在Visual Studio中打开此文件。您现在应该能够构建项目并运行测试。例如,在Solution Explorer窗口(可从View菜单中获得)中,右键单击ALL_BUILD并选择Build。要测试代码,仍然在Solution Explorer窗口中,选择RUN_TESTS并选择Build

        用法(在Windows,Linux和MacOS上使用vcpkg)

        Windows,Linux和MacOS上的vcpkg用户可以simdjson使用他们?#19981;?#30340;shell中的一个命令下载和安装。

        在Linux和MacOS上:

        $ ./vcpkg install simdjson
        

        将构建并安装simdjson为静态库。

        在Windows(64位)上:

        .\vcpkg.exe install simdjson:x64-windows
        

        将构建并安装simdjson为共享库。

        .\vcpkg.exe install simdjson:x64-windows-static  
        

        将构建并安装simdjson为静态库。

        这些命令还将打印出有关如何使用MSBuild或基于CMake的项目库的说明。

        如果您发现simdjson附带的版本vcpkg已过期,请随时通过提交vcpkg问题或创建PR 向社区报告。

        工具

        json2json mydoc.json解析文档,构造模型,然后将结果转储回标准输出

        json2json -d mydoc.json解析文档,构造模型,然后将模型(作为磁带)转储?#22870;?#20934;输出。磁带格式在随附文件中描述tape.md

        minify mydoc.json`缩小JSON文档,将结果输出?#22870;?#20934;输出。缩小意味着删除不需要的空格字符。

        范围

        我们提供快速解析器。它根据各种规格完全验证输入。解析器构建一个有用的不可变(只读)DOM(文档 – 对象模型),以后可以访问它。

        为?#24605;?#21270;工程,我们做了一些假设:

        我们支持UTF-8(以及ASCII),没有别的(没有拉丁语,没有UTF-16)。我们不认为这是一个真正的限制,因为我们认为没有任何?#29616;?#30340;应用程序需要在没有ASCII或UTF-8编码的情况下处理JSON数据;

        JSON文档中的所有字符串最多可包含UTF-8(4GB)中的4294967295个字节。要强制执行此约束,我们拒绝解析包含超过4294967295字节(4GB)的文档。这应该?#35270;?#22823;多数JSON文档;

        我们假设AVX2支持在AMD和英特尔生产的所有最新主流x86处理器中都可用。尽管可以完成,但不包括对非x86处理器的支持。我们计划支持ARM处理器(请求帮助);

        如果发生?#25910;希?#25105;们只会报告?#25910;希?#32780;不会指出问题的性质。(这可以在不影响性能的情况下轻松改进);

        在规范允许的情况下,我们允许对象内的重复键(像sajson这样的其他解析器也这样做);

        性能针对跨越至少几十千字节到几兆字节的JSON文档进行了优化:必须解析许多小型JSON文档或一个真正庞大的JSON文档的性能问题是不同的。

        我们的目标不是提供通用的JSON库。像RapidJSON这样的库提供的不仅仅是解析,它还可以帮助您生成JSON并提供各?#21046;?#20182;?#22870;?#30340;功能。我们只解析文档。

        特征

        输入字符串未修?#27169;?#20687;sajson和RapidJSON这样的解析器使用输入字符串作为缓冲区)。

        我们将整数和浮点数解析为单独的类型,这允许我们支持[-9223372036854775808,9223372036854775808]中的大型64位整数,如Java long或C / C ++ long long。在区分整数和浮点数的解析器中,并非所有解析器都支持64位整数。(例如,sajson拒绝整数大于或等于2147483648的JSON文件.FreeJSON将解析包含过长整数的文件,如18446744073709551616作为浮点数)当我们无法将整数表示为带符号的64位时值,我们拒绝JSON文档。

        在解析过程中进行完整的UTF-8验证(像fastjson,gason和dropbox json11这样的解析器不会进行UTF-8验证);完全验证了这些数字(像gason和ultranjson这样的解析器将接受[0e+]为?#34892;?#30340;JSON);验证未转义字符的字符串内容(像fastjson和ultrajson这样的解析器接受未转义的换行符和字符串中的标签)。

        Architecture

        解析器分两个阶段工作:

        阶段1.(查找标记)快速标识结构元素,字符串等。我们在那个阶段验证UTF-8编码。

        阶段2.(结构构建)涉及构建排序的“树?#20445;?#20855;体化为磁带)以浏览数据。在此阶段解析字符串和数字。

        导航已解析的文档

        以下是将解析后的JSON转储回字符串的代码示例:

           ParsedJson::iterator pjh(pj);
            if (!pjh.isOk()) {
              std::cerr << " Could not iterate parsed result. " << std::endl;
              return EXIT_FAILURE;
            }
            compute_dump(pj);
            //
            // where compute_dump is :
        
        void compute_dump(ParsedJson::iterator &pjh) {
          if (pjh.is_object()) {
            std::cout << "{";
            if (pjh.down()) {
              pjh.print(std::cout); // must be a string
              std::cout << ":";
              pjh.next();
              compute_dump(pjh); // let us recurse
              while (pjh.next()) {
                std::cout << ",";
                pjh.print(std::cout);
                std::cout << ":";
                pjh.next();
                compute_dump(pjh); // let us recurse
              }
              pjh.up();
            }
            std::cout << "}";
          } else if (pjh.is_array()) {
            std::cout << "[";
            if (pjh.down()) {
              compute_dump(pjh); // let us recurse
              while (pjh.next()) {
                std::cout << ",";
                compute_dump(pjh); // let us recurse
              }
              pjh.up();
            }
            std::cout << "]";
          } else {
            pjh.print(std::cout); // just print the lone value
          }
        }
        

        以下函数将查找所有user.id整数:

        void simdjson_traverse(std::vector<int64_t> &answer, ParsedJson::iterator &i) {
          switch (i.get_type()) {
          case '{':
            if (i.down()) {
              do {
                bool founduser = equals(i.get_string(), "user");
                i.next(); // move to value
                if (i.is_object()) {
                  if (founduser && i.move_to_key("id")) {
                    if (i.is_integer()) {
                      answer.push_back(i.get_integer());
                    }
                    i.up();
                  }
                  simdjson_traverse(answer, i);
                } else if (i.is_array()) {
                  simdjson_traverse(answer, i);
                }
              } while (i.next());
              i.up();
            }
            break;
          case '[':
            if (i.down()) {
              do {
                if (i.is_object_or_array()) {
                  simdjson_traverse(answer, i);
                }
              } while (i.next());
              i.up();
            }
            break;
          case 'l':
          case 'd':
          case 'n':
          case 't':
          case 'f':
          default:
            break;
          }
        }
        

        深入比较

        如果您想了解各种解析器如何验证给定的JSON文件:

        make allparserscheckfile
        ./allparserscheckfile myfile.json
        

        对于性能比?#24076;?/p>

        make parsingcompetition
        ./parsingcompetition myfile.json
        

        进行更广泛的比?#24076;?/p>

        make allparsingcompetition
        ./allparsingcompetition myfile.json

        *参考来源github,由周大涛编译,转载请注明来自FreeBuf.COM

        发表评论

        已有 5 条评论

        • [email protected]  2019-03-25 回复 1楼

          治标不治本, 这些格式都是老外定的,以前的html那80,90年代,那时技术毕竟太早,以求简单简便方式,但后来xml完全是一?#36164;?要不是专门开发了个个库来解析,不然自?#21019;?#30721;解析,累死不偿命,后来的到json,也是, 这根是设计原因, 复杂时的xml,人类阅读都累,更不要说机器了,, 正确的做法,应该是像msgpack,或protobuf那样, 每个元素名称,每个元素值?#21152;?#23450;长标记,机器读?#21280;?#24230;,再读下一个数据块, 速度快,还不会有转义符的问题了 而在开发显示时, 转成文字显示,保存时又保存为bin格式

          html2.0以前的头同样 这个问题,a:b方?#20132;购? 可是b里还有复杂项目, 整个头占用大,服务器解析费时间,而到了2.0,这http头虽然有改进,但?#25351;?#26434;起来了
          html body段,完全是垃圾一样的存在, 明明可以把常见的标记,编号一下, 比如<title>关键字保存为#1 <html>#2 onclick#3… 这样,节省资源,也?#22870;?#27983;览器解析
          编辑html时,?#21482;?#21407;成文本编辑 现在一个html不算图片, 纯html就有1M 或更多, 而显示出来,上面可能?#22270;?#20010;正文内容,简直疯了, 通过bin化后, 1M 的文件,不压缩也只有1/10了, 再通过压缩,最后只有1/100

          但是,现实条件是, 现在所有标准是老外定的, 国内没话语权的, 国内也没有一家公司有这样的技术能力(吹牛B和骗补贴,骗股市能力很强),国外现在又不行了, 这个技术?#34892;?#36716;移过程中,中间有个真空时间,估计要好10年以上,弄当了20年都不说不定 ,具体看国内某个不可说的环境,态度

          (freebuf你的页面能不能正常点, 10次里有6次,点提交评论, 整个页面重新刷新,原有打的字没了,必须重新打, 现在评论,已经习惯了点提交前,先ctrl+c一下)

          • Glory232  (1级)  2019-03-25 回复

            @ [email protected] 复杂时的xml,人类阅读都累,更不要说机器了???

            • [email protected]  2019-03-25 回复

              @ Glory232? 你以为xml只有 <name>val<xx>? 每个子结点还有属性了,有时这数据存在属性里,有的数据存在结点值里,也不知道原数据者,完全看xml数据源头的设?#26222;?#24515;情, 你?#32431;磜indows数据库通讯,wdsl数据通讯之类的地方吧(wdsl数据例子:https://en.wikipedia.org/wiki/Web_Services_Description_Language), 还有svg文件,office docX xlsX?#20154;?#26377;***X文件, 假如没有别人专门开发好的库(可能你的用的语言没有这个库,或是有,你还需要熟悉别人的库),要自己实现,累死你,保证你花上几个月,甚至几年的,都不敢公布自己的代码,不然丢死人, 漏洞会有点多的,效率有点低,写的也很难看, 而bin化以后, 可以充值利用机器的优势,避开人的劣势, 人只要写个简单的循环就可以操作所有数据,一般bin化后,大文件?#22815;?#26377;索引块等,因为bin类的文件,通常是70-90年代的人设计的,当时硬件差,很重?#26377;?#29575;, 在windows上,有IXMLDoc接口,可以简单点,省掉了自己解析数据包,但是数据存储还是乱乱的,还是累, 在linux下, 还得引用别人库, 现在各种开源库,越来越大,越来越复杂, 你要接触一个库时,会让你头都大了,要是还干点?#24230;?#24335;的活,还得省空间(一般?#24230;?#24335;flash就4-8M,多点16M,要放下整系统和app,字体,图片,配置文件空间等
              xml在数据表示层(如wdsl)用的很多, 越复杂的格式,漏洞越多,很多会是反序?#26032;?#27934;,json这么简单,都已经有好几个库爆过RCE级漏洞了,xml必然也是有的,不过前面几年网页中用的不多,大家不注意,这些漏洞又都?#21483;?#20462;复了,所以10-20年内,还得忍受这?#32856;?#24335; 在 网页中json,是简单的海了去了, 但是读取的效率还是低的,而且只能是宽类型的, 除了字符串外,其它都是不确定性的, 比如你不清楚数字是只允许整数的,还是小数也可以是
              现在json只是初级阶段(以前xml初级阶段时,主体也只是<x></x>,后来变态的乱, 将来json应用广了也可能会这样, 本身json设计时,面向js解析场合使用的

        • [email protected]  2019-03-25 回复 2楼

          补一下msgpack和pb, 只是说他的bin方式,而不是?#19981;?#36825;两种,也不是说json,html等保存为这?#25351;?#24335;,根本没办法保存的,设计不同, 真要保存成bin肯定定制一套的, msgpack缺点是所有数据是a=b方式, 要是数据库,cvs这类,就大量的name存在, 不能是一个标题后面全部是val列表, 官方无意增加这个功能 pb,最大缺点是必须是和语言本身绑定一下, ?#35759;?#20041;的结构体,转换成代码的struct/record体, 万一某个语言不没有这工具,那就惨了
          另外,生成的定义文件(*.c,*.h)太过复杂,效率上,资源上,编译质量上,有点烂

        取消
        Loading...
        css.php 永利彩票是骗局吗