毎秒ギガバイトを解析するJSON解析器オープンソース、秒殺一大波解析器!

9658 ワード

最近、GitHubはJSON解析器simdjsonをオープンソースし、他の常用解析器との比較実験を行った結果、simdjsonの解析速度は2.2 GB/sに達し、他の解析器をはるかに秒殺した.以下、simdjsonについて詳しく紹介する.以下の全文は、GitHub上のsimdjsonのドキュメントです.
JSONドキュメントはインターネット上でどこにでもあり、サーバはこれらのドキュメントを解析するのに多くの時間を費やしています.完全検証(文字符号化を含む)を行うとともに,JSONの解析を可能な限り一般的なSIMD命令を用いて加速させることを望んでいる.
いくつかのパフォーマンス結果
RapidJSONなどの最先端の解析器に比べて、4分の1以下の命令を使用する可能性があります.sajsonの半分しかありません.我々の知る限りsimdjsonは商用プロセッサ上で毎秒ギガバイト速度で動作する最初の完全検証JSON解析器である.
Skylakeプロセッサでは、各種解析器がtwitter.を解析します.jsonファイルの速度(GB/s単位)は以下のようになります.
基本的な要件
  • Visual Studio 2017以降でLinux、macOS、Windowsなどのプラットフォームをサポートする.
  • AVX 2を搭載したプロセッサ.
  • は最近のC++コンパイラ(例えば、GNU GCCまたはLLVM CLANgまたはVisual Studio 2017)をサポートし、C++17、GNU GCC 7以上、またはLLVMのclang 6以上のバージョンであると仮定します.
  • は、bashおよび他の一般的なユーティリティコマンドプログラムであってもよいいくつかのベンチマークテストスクリプトを提供するが、オプションである.

  • 許可する
    コードはApache License 2.0ライセンスを採用しています.
    Windowsではwindows/dirent_を使用します.portable.hファイル(ライブラリコードの外)は、自由なMITライセンスに基づいたツールを構築しています.
    コードの例
    #include \u0026quot;simdjson/jsonparser.h\u0026quot;/...const char * filename = ... //// use whatever means you want to get a string of your JSON documentstd::string_view p = get_corpus(filename);ParsedJson pj;pj.allocateCapacity(p.size()); // allocate memory for parsing up to p.size() bytesbool is_ok = json_parse(p, pj); // do the parsing, return false on error// parsing is done!// You can safely delete the string contentfree((void*)p.data());// the ParsedJson document can be used here// js can be reused with other json_parse calls.

    新しいJSONドキュメントごとにメモリを割り当てることを気にしない場合は、より簡単なAPIを使用することもできます.
    #include \u0026quot;simdjson/jsonparser.h\u0026quot;/...const char * filename = ... //std::string_view p = get_corpus(filename);ParsedJson pj = build_parsed_json(p); // do the parsing// you no longer need p at this point, can do aligned_free((void*)p.data())if( ! pj.isValid() ) {    // something went wrong}

    使用法
    シンプルなヘッダファイル
    ヘッダファイルはコードライブラリの「singleheader」を見ることができ、使い方は「amalgamation_demo.cpp」ファイルを見ることができます.ここでは、特定のコンストラクションシステムを使用する必要はありません.ファイルをプロジェクトのパスにコピーするだけです.次に、次の項目を含めることができます.
    #include \u0026lt;iostream\u0026gt;#include \u0026quot;simdjson.h\u0026quot;#include \u0026quot;simdjson.cpp\u0026quot;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 \u0026lt;\u0026lt; \u0026quot;not valid\u0026quot; \u0026lt;\u0026lt; std::endl;  } else {    std::cout \u0026lt;\u0026lt; \u0026quot;valid\u0026quot; \u0026lt;\u0026lt; std::endl;  }  return EXIT_SUCCESS;}

    注意:simdjsonをプリコンパイルする必要がある環境もあります.cppは、それを含むのではなく.
    LinuxやmacOSなどのプラットフォームで旧バージョンのMakefileを使用
    要求:最近のclang(またはgcc)とmake.少なくともGNU GCC/G++7またはLLVM clang 6、LinuxまたはmacOSシステムを使用することをお勧めします.
    テスト:
    makemake test

    ベンチマークテストを実行します.
    make parse./parse jsonexamples/twitter.json

    Linuxでは、parseコマンドはパフォーマンスカウンタの詳細な分析を提供します.
    比較の基準となる別のテストを実行します(他の解析器を使用します):
    make benchmark

    LinuxやmacOSなどのプラットフォーム上のCMakeを使用
    要件:最新バージョンのcmakeが必要です.macOSでcmakeをインストールする最も簡単な方法はbrewを使用することです.
    brew install cmake

    clangやgccのような新しいコンパイラが必要です.少なくともGNU GCC/G++7またはLLVM clang 6を使用することをお勧めします.たとえば、brewを使用して最新のコンパイラをインストールできます.
    brew install gcc@8

    オプション:CC変数とCXX変数を設定してcmakeにどのコンパイラを使用するかを教える必要があります.bashでは、export CC=gcc-7やexport CXX=g+±7などのコマンドを使用できます.
    ≪構築|Build|oem_src≫:プロジェクト・コード・ライブラリで次のコマンドを実行します.
    mkdir buildcd buildcmake ..makemake test

    CMakeはライブラリを構築します.デフォルトでは、共有ライブラリ(Linuxのlibsimdjson.soなど)が構築されています.
    静的ライブラリを構築できます.
    mkdir buildstaticcd buildstaticcmake -DSIMDJSON_BUILD_STATIC=ON ..make make test

    場合によっては、特にシステムのデフォルトのコンパイラが古い場合にコンパイラを指定したい場合があります.次の手順で操作できます.
    brew install gcc@8mkdir buildcd buildexport CXX=g++-8 CC=gcc-8cmake ..makemake test

    Visual StudioによるWindowsでのCMakeの使用
    少なくともVisual Studio 2017を搭載した一般的なWindows PCを持ち、AVX 2をサポートするx 64プロセッサ(2013 Haswell以降)を持っていると仮定します.
  • GitHubからsimdjsonコードを取得します.たとえば、GitHub Desktopを使用してクローン化します.
  • CMakeをインストールします.インストール時に、コマンドラインからcmakeが使用できることを確認してください.最新バージョンのcmakeを選択してください.
  • simdjsonでVisualStudioなどのサブディレクトリを作成します.
  • はshellでこの新しく作成されたディレクトリに移動します.
  • shellにcmake-DCMAKEと入力GENERATOR_PLATFORM=x 64…(または、DLLを構築する場合は、コマンドラインcmake-DCMAKE_GENERATOR_PLATFORM=x 64-DIMDJSON_BUILD_STATIC=OFF…)を使用します.
  • 最後のコマンドは、simdjson.slnなどの新しく作成されたディレクトリにVisual Studioソリューションファイルを作成します.このファイルをVisual Studioで開きます.プロジェクトを構築し、テストを実行できるようになりました.たとえば、「Solution Explorer」ウィンドウで「ALL_BUILD」を右クリックし、「Build」を選択します.コードをテストするには、Solution ExplorerウィンドウでRUN_を選択します.TESTS、Buildを選択します.

  • ツール
  • json2json mydoc.jsonはドキュメントを解析し、モデルを構築し、結果を標準出力に出力します.
  • json2json -d mydoc.jsonはドキュメントを解析し、モデルを構築し、標準出力に出力します.フォーマットは添付ファイルtape.mdに説明があります.
  • minify mydoc.jsonはJSONドキュメントを縮小し、結果を標準出力に出力します.縮小は、不要なスペースを削除することを意味します.

  • 範囲
    非常に速い解析器を提供した.様々な仕様に基づいて入力を完全に検証します.解析器は、後続のアクセスのために可変(読み取り専用)DOM(ドキュメントオブジェクトモデル)を構築します.
    工事を簡略化するために,我々はいくつかの仮定をした.
  • はUTF-8(およびASCII)をサポートしており、他にはありません(Latinはなく、UTF-16はありません).ASCIIまたはUTF-8符号化なしでJSONデータを処理する必要がある厳粛なアプリケーションがあるとは思わないため、これは本当の制限とは思いません.
  • 文字列は、NULLを終端とするC文字列として格納される.したがって、文字列にNULL文字が含まれていないと仮定します.
  • AVX 2をサポートすると仮定します.これは、AMDおよびインテル製の最新の主流x 86プロセッサすべてで使用できます.x 86以外のプロセッサはサポートされていませんが、サポートできます.ARMプロセッサをサポートする予定です.
  • 障害が発生した場合、問題の性質を指摘することなく、障害のみを報告します.
  • 仕様が許容する場合、オブジェクト内に重複するkeyが存在することを許容する.
  • パフォーマンスは、数千バイトから数メガバイトにわたるJSONドキュメントに最適化されています.多くの小型JSONドキュメントと大きなJSONドキュメントのパフォーマンスの問題を解析するのは異なります.

  • 私たちの目標は共通のJSONライブラリを提供することではありません.RapidJSONのようなライブラリは解析機能だけでなくJSONの生成にも利用でき、他にも便利な機能を提供しています.ドキュメントのみを解析します.
    とくせい
  • 入力文字列を変更する必要はありません.(sajsonやRapidJSONのような解析器では入力文字列をバッファとして使用している.)
  • は、整数および浮動小数点数を別個のタイプに解析し、JavaのlongまたはC/C++のlong longのような[9,223,237,20368,547,580,892,337,2036,685,547,5808]区間の64ビット整数をサポートすることができる.整数と浮動小数点数を区別する解析器では、すべての解析器が64ビット整数をサポートしているわけではありません.(たとえば、sajsonでは2147483648以上の整数を含むJSONファイルはサポートされていません.FreeJSOnは長い整数を浮動小数点数に解析します.)整数を符号付き64ビット値として表すことができない場合、JSONドキュメントの解析を拒否します.
  • は解析中に完全なUTF-8検証を行う.(fastjson、gason、dropbox json 11のような解析器ではUTF-8検証は行われません.)
  • 数字を完全に検証します.(gasonやultranjsonのような解析器は[0 e+]という数字を受け入れます.)
  • 文字列コンテンツのエスケープされていない文字を検証します.(fastjsonやultrajsonのような解析器は、文字列内のエスケープされていない改行子やタブを受け入れます.)

  • スキーマ#スキーマ#
    解析器は3つの段階に分けられます.
  • フェーズ1では、構造要素、文字列などをすばやく識別します.この段階でUTF-8符号化を検証する.
  • フェーズ2では、データへのアクセスを容易にするために、ソートされたツリー(テープ化)を構築します.この段階で文字列と数字を解析します.

  • 解決されたドキュメントへのアクセス
    解析したJSONを文字列に戻すコードの例を以下に示します.
    ParsedJson::iterator pjh(pj);    if (!pjh.isOk()) {      std::cerr \u0026lt;\u0026lt; \u0026quot; Could not iterate parsed result. \u0026quot; \u0026lt;\u0026lt; std::endl;      return EXIT_FAILURE;    }    compute_dump(pj);    //    // where compute_dump is :void compute_dump(ParsedJson::iterator \u0026amp;pjh) {  if (pjh.is_object()) {    std::cout \u0026lt;\u0026lt; \u0026quot;{\u0026quot;;    if (pjh.down()) {      pjh.print(std::cout); // must be a string      std::cout \u0026lt;\u0026lt; \u0026quot;:\u0026quot;;      pjh.next();      compute_dump(pjh); // let us recurse      while (pjh.next()) {        std::cout \u0026lt;\u0026lt; \u0026quot;,\u0026quot;;        pjh.print(std::cout);        std::cout \u0026lt;\u0026lt; \u0026quot;:\u0026quot;;        pjh.next();        compute_dump(pjh); // let us recurse      }      pjh.up();    }    std::cout \u0026lt;\u0026lt; \u0026quot;}\u0026quot;;  } else if (pjh.is_array()) {    std::cout \u0026lt;\u0026lt; \u0026quot;[\u0026quot;;    if (pjh.down()) {      compute_dump(pjh); // let us recurse      while (pjh.next()) {        std::cout \u0026lt;\u0026lt; \u0026quot;,\u0026quot;;        compute_dump(pjh); // let us recurse      }      pjh.up();    }    std::cout \u0026lt;\u0026lt; \u0026quot;]\u0026quot;;  } else {    pjh.print(std::cout); // just print the lone value  }}

    次の関数はすべてのuserを見つけます.id整数:
    void simdjson_traverse(std::vector\u0026lt;int64_t\u0026gt; \u0026amp;answer, ParsedJson::iterator \u0026amp;i) {  switch (i.get_type()) {  case '{':    if (i.down()) {      do {        bool founduser = equals(i.get_string(), \u0026quot;user\u0026quot;);        i.next(); // move to value        if (i.is_object()) {          if (founduser \u0026amp;\u0026amp; i.move_to_key(\u0026quot;id\u0026quot;)) {            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

    パフォーマンスの比較:
    make parsingcompetition./parsingcompetition myfile.json

    より広範な比較:
    make allparsingcompetition./allparsingcompetition myfile.json

    英文原文:https://github.com/lemire/simdjson
    詳細については、フロントエンドの頂点に注目してください.