Compare commits

..

1 Commits

Author SHA1 Message Date
Daniel Lemire 88374a416f Preparing new patch release. (#1048) 2020-07-17 15:17:12 -04:00
743 changed files with 23755 additions and 77461 deletions
+9 -17
View File
@@ -4,34 +4,26 @@ configuration: Release
image: Visual Studio 2019
platform: x64
cache:
- C:\dependencies -> dependencies\CMakeLists.txt
environment:
# Forward slash is used because this is used in CMake as is
simdjson_DEPENDENCY_CACHE_DIR: C:/dependencies
matrix:
- job_name: VS2019
CMAKE_ARGS: -A %Platform%
CMAKE_ARGS: -A %Platform%
- job_name: VS2019CLANG
CMAKE_ARGS: -A %Platform% -T ClangCL
- job_name: VS2019ARM
CMAKE_ARGS: -A ARM64 -DCMAKE_CROSSCOMPILING=1 -D SIMDJSON_GOOGLE_BENCHMARKS=OFF # Does Google Benchmark builds under VS ARM?
- job_name: VS2017 (Static, No Threads)
image: Visual Studio 2017
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
- job_name: VS2019 (Win32)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CTEST_ARGS: -LE explicitonly
- job_name: VS2019 (Win32, No Exceptions)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
CTEST_ARGS: -LE explicitonly
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CTEST_ARGS: -E checkperf
- job_name: VS2015
image: Visual Studio 2015
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
build_script:
- mkdir build
+52 -119
View File
@@ -1,18 +1,24 @@
version: 2.1
# We constantly run out of memory so please do not use parallelism (-j, -j4).
# Reusable image / compiler definitions
executors:
gcc7:
docker:
- image: gcc:7
environment:
CXX: g++
CC: gcc
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
gcc8:
docker:
- image: conanio/gcc8
environment:
CXX: g++-8
CC: gcc-8
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
gcc9:
docker:
@@ -20,8 +26,8 @@ executors:
environment:
CXX: g++-9
CC: gcc-9
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
gcc10:
docker:
@@ -29,8 +35,8 @@ executors:
environment:
CXX: g++-10
CC: gcc-10
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
clang10:
docker:
@@ -38,8 +44,8 @@ executors:
environment:
CXX: clang++-10
CC: clang-10
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
clang9:
docker:
@@ -47,8 +53,8 @@ executors:
environment:
CXX: clang++-9
CC: clang-9
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
clang6:
docker:
@@ -56,24 +62,11 @@ executors:
environment:
CXX: clang++-6.0
CC: clang-6.0
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
BUILD_FLAGS: -j
CTEST_FLAGS: -j4 --output-on-failure
# Reusable test commands (and initializer for clang 6)
commands:
dependency_restore:
steps:
- restore_cache:
keys:
- cmake-cache-{{ checksum "dependencies/CMakeLists.txt" }}
dependency_cache:
steps:
- save_cache:
key: cmake-cache-{{ checksum "dependencies/CMakeLists.txt" }}
paths:
- dependencies/.cache
install_cmake:
steps:
- run: apt-get update -qq
@@ -84,93 +77,57 @@ commands:
- checkout
- run: mkdir -p build
cmake_build_cache:
steps:
- cmake_prep
- dependency_restore
- run: cmake $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination -B build .
- dependency_cache # dependencies are produced in the configure step
cmake_build:
steps:
- cmake_build_cache
- run: cmake --build build
- cmake_prep
- run: |
cd build &&
cmake $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
make $BUILD_FLAGS all
cmake_test:
steps:
- cmake_build
- run: |
cd build &&
tools/json2json -h &&
cd build && tools/json2json -h &&
ctest $CTEST_FLAGS -L acceptance &&
ctest $CTEST_FLAGS -LE acceptance -LE explicitonly
cmake_assert_test:
steps:
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L assert
ctest $CTEST_FLAGS -LE acceptance -E checkperf
cmake_test_all:
steps:
- cmake_build
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -DSIMDJSON_IMPLEMENTATION="haswell;westmere;fallback" -L acceptance -LE per_implementation &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
cd build && tools/json2json -h &&
ctest $CTEST_FLAGS -L acceptance -LE per_implementation &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation &&SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation &&
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
cmake_perftest:
steps:
- cmake_build_cache
- run: |
cmake --build build --target checkperf &&
cd build &&
ctest --output-on-failure -R checkperf
# we not only want cmake to build and run tests, but we want also a successful installation from which we can build, link and run programs
cmake_install_test: # this version builds, install, test and then verify from the installation
steps:
- run: cd build && make install
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++17 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
cmake_installed_test_cxx20: # assuming that it was installed, this tries to build using C++20
steps:
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++20 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
jobs:
# static
gcc7:
description: Build and run tests on GCC 7 and AVX 2 with a cmake static build
executor: gcc7
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ install_cmake, cmake_test, cmake_install_test ]
justlib-gcc10:
description: Build just the library, install it and do a basic test
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_JUST_LIBRARY=ON }
steps: [ cmake_build, cmake_install_test, cmake_installed_test_cxx20 ]
assert-gcc10:
description: Build the library with asserts on, install it and run tests
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
steps: [ cmake_test, cmake_assert_test ]
assert-clang10:
description: Build just the library, install it and do a basic test
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
steps: [ cmake_test, cmake_assert_test ]
gcc10-perftest:
description: Build and run performance tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_perftest ]
steps: [ cmake_build, cmake_install_test ]
gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake static build
description: Build and run tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
steps: [ cmake_test_all, cmake_install_test ]
clang6:
description: Build and run tests on clang 6 and AVX 2 with a cmake static build
executor: clang6
@@ -180,34 +137,25 @@ jobs:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
steps: [ cmake_test, cmake_install_test ]
# libcpp
libcpp-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build and libc++
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
steps: [ cmake_test, cmake_install_test ]
# sanitize
sanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: -j4 --output-on-failure -E checkperf }
steps: [ cmake_test ]
sanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: -j4 --output-on-failure -E checkperf }
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake dynamic build
@@ -250,24 +198,19 @@ jobs:
# make (test and checkperf)
arch-haswell-gcc10:
description: Build, run tests and check performance on GCC 10 with -march=haswell
description: Build, run tests and check performance on GCC 7 with -march=haswell
executor: gcc10
environment: { CXXFLAGS: -march=haswell }
steps: [ cmake_test ]
arch-nehalem-gcc10:
description: Build, run tests and check performance on GCC 10 with -march=nehalem
description: Build, run tests and check performance on GCC 7 with -march=nehalem
executor: gcc10
environment: { CXXFLAGS: -march=nehalem }
steps: [ cmake_test ]
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
no-computed-goto-gcc10:
description: Build, run tests and check performance on GCC 7 with -DSIMDJSON_NO_COMPUTED_GOTO=true
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CXXFLAGS: -DSIMDJSON_NO_COMPUTED_GOTO=true }
steps: [ cmake_test ]
workflows:
@@ -275,7 +218,7 @@ workflows:
build_and_test:
jobs:
# full multi-implementation tests
#- gcc7 tested on GitHub actions
- gcc7
- gcc10 # do not delete this as it tests our performance
- clang6
#- clang10 # this gets tested a lot below
@@ -286,8 +229,6 @@ workflows:
# full single-implementation tests
- sanitize-gcc10
- sanitize-clang10
- threadsanitize-gcc10
- threadsanitize-clang10
- dynamic-gcc10
- dynamic-clang10
- unthreaded-gcc10
@@ -300,17 +241,9 @@ workflows:
# quicker make single-implementation tests
- arch-haswell-gcc10
- arch-nehalem-gcc10
# sanitized single-implementation tests
- sanitize-haswell-gcc10
- sanitize-haswell-clang10
- no-computed-goto-gcc10
# testing "just the library"
- justlib-gcc10
# testing asserts
- assert-gcc10
- assert-clang10
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows
+3 -8
View File
@@ -6,11 +6,6 @@ task:
env:
ASSUME_ALWAYS_YES: YES
simdjson_DEPENDENCY_CACHE_DIR: $HOME/.dep_cache
dep_cache:
folder: $HOME/.dep_cache
reupload_on_changes: false
fingerprint_script: cat dependencies/CMakeLists.txt
setup_script:
- pkg update -f
- pkg install bash
@@ -19,8 +14,8 @@ task:
build_script:
- mkdir build
- cd build
- cmake -DSIMDJSON_BASH=OFF -DSIMDJSON_GIT=OFF ..
- make
- cmake ..
- make -j4
test_script:
- cd build
- ctest --output-on-failure -LE explicitonly
- ctest -j4 --output-on-failure -E checkperf
+2 -2
View File
@@ -1,7 +1,7 @@
*
!.git
!Makefile
!amalgamate.py
!amalgamate.sh
!benchmark
!dependencies
!include
@@ -12,4 +12,4 @@
!src
!style
!tests
!tools
!tools
+48 -117
View File
@@ -9,10 +9,10 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y g++ cmake gcc git
- apt-get update -qq
- apt-get install -y g++ cmake gcc
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -30,10 +30,10 @@ steps:
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- scripts/addcmakeppa.sh "$(env -i sh -c '. /etc/os-release; echo $VERSION_CODENAME')"
- apt-get install -y clang++-6.0 cmake git
- apt-get update -qq
- apt-get install -y clang++-6.0 cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -50,12 +50,11 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -77,8 +76,8 @@ steps:
CC: clang-6.0
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
@@ -101,11 +100,10 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -124,7 +122,7 @@ steps:
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
@@ -142,12 +140,11 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -168,9 +165,9 @@ steps:
environment:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
@@ -183,27 +180,6 @@ steps:
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: cpp20-clang11-libcpp
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: pauldreik/llvm-11
user: root
environment:
CC: clang-11
CXX: clang++-11
CMAKE_FLAGS: -GNinja
BUILD_FLAGS:
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -std=c++20 -stdlib=libc++
commands:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-gcc8
platform: { os: linux, arch: arm64 }
steps:
@@ -213,12 +189,11 @@ steps:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -233,17 +208,16 @@ name: arm64-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: debian:buster-backports
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
- apt-get install -y clang-6.0 git
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -261,11 +235,10 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -277,17 +250,16 @@ name: arm64-dynamic-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: debian:buster-backports
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
- apt-get install -y clang-6.0 git
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -302,15 +274,13 @@ steps:
image: gcc:8
environment:
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CC: gcc
CXX: g++
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y libstdc++6
- apt-get install -y cmake libstdc++6
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -325,17 +295,16 @@ name: arm64-sanitize-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: debian:buster-backports
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get -qq update
- apt-get -t buster-backports install -y cmake
- apt-get install -y clang-6.0 git
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -357,9 +326,11 @@ steps:
CXX: clang++-9
BUILD_FLAGS: -- -j 4
CMAKE_FLAGS: -GNinja -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure
CXXFLAGS: -stdlib=libc++
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -378,30 +349,11 @@ steps:
CXX: clang++-9
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CXXFLAGS: -stdlib=libc++
commands:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: libcpp-clang7
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang7
user: root
environment:
CC: clang-7
CXX: clang++-7
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CXXFLAGS: -stdlib=libc++
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -419,11 +371,10 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
- apt-get -t buster-backports install -y cmake
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
@@ -431,26 +382,6 @@ steps:
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-fuzz
platform: { os: linux, arch: arm64 }
steps:
- name: Build and run fuzzers shortly
image: ubuntu:20.04
environment:
CC: clang
CXX: clang++
DEBIAN_FRONTEND: noninteractive
ASAN_OPTIONS: detect_leaks=0
commands:
- apt-get -qq update
- apt-get install -q -y clang cmake git wget zip ninja-build
- wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
- tar xf corpus.tar && rm corpus.tar
- fuzz/build_like_ossfuzz.sh
- mkdir -p common_out
- for fuzzer in build/fuzz/fuzz_* ; do echo $fuzzer;$fuzzer common_out out/* -max_total_time=40; done
---
kind: pipeline
name: stylecheck
platform: { os: linux, arch: amd64 }
steps:
+9 -9
View File
@@ -80,15 +80,15 @@
.gitignore export-ignore
# Sources
*.c text eol=lf diff=c
*.cc text eol=lf diff=cpp
*.cxx text eol=lf diff=cpp
*.cpp text eol=lf diff=cpp
*.c++ text eol=lf diff=cpp
*.hpp text eol=lf diff=cpp
*.h text eol=lf diff=c
*.h++ text eol=lf diff=cpp
*.hh text eol=lf diff=cpp
*.c text diff=c
*.cc text diff=cpp
*.cxx text diff=cpp
*.cpp text diff=cpp
*.c++ text diff=cpp
*.hpp text diff=cpp
*.h text diff=c
*.h++ text diff=cpp
*.hh text diff=cpp
# Compiled Object files
*.slo binary
+2 -10
View File
@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug (unverified)
labels: bug
assignees: ''
---
@@ -12,30 +12,22 @@ Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
**Describe the bug**
A clear and concise description of what the bug is.
Note that a compiler warning is not a bug.
**To Reproduce**
Steps to reproduce the behaviour: provide a code sample if possible.
If we cannot reproduce the issue, then we cannot address it.
Note that a stack trace from your own program is not enough.
**Configuration (please complete the following information if relevant):**
- OS: [e.g. Ubuntu 16.04.6 LTS]
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
- Version [e.g. 22]
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug. In particular, we do not support legacy 32-bit systems.
**Indicate whether you are willing or able to provide a bug fix as a pull request**
If you plan to contribute to simdjson, please read our
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+2 -3
View File
@@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
labels: feature request
assignees: ''
---
@@ -12,7 +12,6 @@ Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
@@ -32,6 +31,6 @@ A clear and concise description of any alternative solutions or features you've
Add any other context or screenshots about the feature request here.
** Are you willing to contribute code or documentation toward this new feature? **
If you plan to contribute to simdjson, please read our
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
@@ -12,7 +12,6 @@ Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
@@ -27,6 +26,6 @@ Is your issue:
4. A documentation issue? Can you suggest an improvement?
If you plan to contribute to simdjson, please read our
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
-8
View File
@@ -1,8 +0,0 @@
Our tests check whether you have introduced trailing white space. If such a test fails, please check the "artifacts button" above, which if you click it gives a link to a downloadable file to help you identify the issue. You can also run scripts/remove_trailing_whitespace.sh locally if you have a bash shell and the sed command available on your system.
If you plan to contribute to simdjson, please read our
CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
-40
View File
@@ -1,40 +0,0 @@
name: Alpine Linux
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: start docker
run: |
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
echo 'docker exec alpine "$@";' > ./alpine.sh
chmod +x ./alpine.sh
- name: install packages
run: |
./alpine.sh apk update
./alpine.sh apk add build-base cmake g++ linux-headers git bash
- name: cmake
run: |
./alpine.sh cmake -B build_for_alpine
- name: build
run: |
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -LE explicitonly"
@@ -1,37 +0,0 @@
name: Detect trailing whitespace
on:
pull_request:
branches:
- master
jobs:
whitespace:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Remove whitespace and check the diff
run: |
set -eu
scripts/remove_trailing_whitespace.sh
git diff >whitespace.patch
cat whitespace.patch
if [ $(wc -c <whitespace.patch) -ne 0 ] ; then
echo " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! "
echo "You have trailing whitespace, please download the artifact"
echo "and apply with git apply <whitespace.patch or"
echo "run scripts/remove_trailing_whitespace.sh locally."
echo " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! "
exit 1
else
echo "no trailing whitespace found, good!"
fi
- name: Archive whitespace patch
uses: actions/upload-artifact@v2
if: always()
with:
name: whitespace-patch
path: |
whitespace.patch
if-no-files-found: ignore
+67 -104
View File
@@ -1,33 +1,21 @@
name: Fuzz and run valgrind
name: Run fuzzers on stored corpus and test it with valgrind
# In the case of a pull request happening at the same time as a cron
# job, there is a risk two jobs run at the same time. Therefore,
# the corpus is only uploaded for the master branch. Pull requests will
# fuzz for a short while, but the results are not uploaded.
on:
push:
branches:
- master
pull_request:
branches:
- master
schedule:
- cron: 23 */8 * * *
jobs:
build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
build:
runs-on: ubuntu-latest
env:
# fuzzers that change behaviour with SIMDJSON_FORCE_IMPLEMENTATION
defaultimplfuzzers: atpointer dump dump_raw_tape element minify parser print_json
# fuzzers that loop over the implementations themselves, or don't need to switch.
implfuzzers: implementations minifyimpl ndjson ondemand padded utf8
implementations: haswell westmere fallback
UBSAN_OPTIONS: halt_on_error=1
MAXLEN: -max_len=4000
CLANGVERSION: 11
# which optimization level to use for the sanitizer build (see build_fuzzer.variants.sh)
OPTLEVEL: -O3
allfuzzers: parser dump dump_raw_tape print_json
artifactsprefix: -artifact_prefix=fuzzfailure/
steps:
- name: Install packages necessary for building
run: |
@@ -35,130 +23,105 @@ jobs:
sudo apt-get install --quiet ninja-build valgrind zip unzip
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh $CLANGVERSION
sudo ./llvm.sh 9
- uses: actions/checkout@v1
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Create and prepare the initial seed corpus
run: |
fuzz/build_corpus.sh
mv corpus.zip seed_corpus.zip
mkdir seedcorpus
unzip -q -d seedcorpus seed_corpus.zip
- name: Download the corpus from the last run
run: |
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
tar xf corpus.tar
rm corpus.tar
- name: List clang versions
run: |
ls /usr/bin/clang*
which clang++
clang++ --version
- name: Build all the variants
run: CLANGSUFFIX=-$CLANGVERSION fuzz/build_fuzzer_variants.sh
- name: Explore fast (release build, default implementation)
run: fuzz/build_fuzzer_variants.sh
- name: Verify that the oss-fuzz seed corpus passes without problems
run: |
set -eux
for fuzzer in $defaultimplfuzzers $implfuzzers; do
mkdir -p out/$fuzzer # in case this is a new fuzzer, or corpus.tar is broken
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-fast/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=30 $MAXLEN
done
- name: Fuzz default impl. fuzzers with sanitizer+asserts (good at detecting errors)
run: |
set -eux
for fuzzer in $defaultimplfuzzers; do
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
for implementation in $implementations; do
export SIMDJSON_FORCE_IMPLEMENTATION=$implementation
build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
mkdir seedcorpus
unzip -q -d seedcorpus seed_corpus.zip
for buildvariant in noavx withavx; do
for fuzzer in $allfuzzers; do
build-ossfuzz-$buildvariant/fuzz/fuzz_$fuzzer seedcorpus -max_total_time=1
done
done
- name: Run the fastest fuzzer to explore fast
run: |
for fuzzer in $allfuzzers; do
mkdir -p out/$fuzzer # in case this is a new fuzzer, or corpus.tar is broken
build-ossfuzz-fast9/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=30 $artifactsprefix || touch failed
# make sure the failing output is visible in the log
if [ -e failed ] ; then
ls fuzzfailure/* |xargs -n1 base64
exit 1
fi
done
- name: Run the other fuzzer variants for $fuzzer, with sanitizers etc
run: |
set -x
for fuzzer in $allfuzzers; do
build-ossfuzz-withavx/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=20 $artifactsprefix || touch failed
build-ossfuzz-noavx/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 $artifactsprefix || touch failed
build-ossfuzz-noavx9/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 $artifactsprefix || touch failed
if [ -e failed ] ; then
# make sure the failing output is visible in the log
ls fuzzfailure/* |xargs -n1 base64
exit 1
fi
echo disable msan runs, it fails inside the fuzzing engine and not the fuzzed code!
echo build-ossfuzz-msan-noavx9/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 -reload=0 $artifactsprefix
echo build-ossfuzz-msan-withavx9/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 -reload=0 $artifactsprefix
echo now have $(ls out/$fuzzer |wc -l) files in corpus
done
- name: Fuzz differential impl. fuzzers with sanitizer+asserts (good at detecting errors)
- name: Minimize the corpus with the fast fuzzer
run: |
set -eux
for fuzzer in $implfuzzers; do
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
echo now have $(ls out/$fuzzer |wc -l) files in corpus
done
- name: Minimize the corpus with the fast fuzzer on the default implementation
run: |
set -eux
for fuzzer in $defaultimplfuzzers $implfuzzers; do
for fuzzer in $allfuzzers; do
mkdir -p out/cmin/$fuzzer
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-fast/fuzz/fuzz_$fuzzer -merge=1 $MAXLEN out/cmin/$fuzzer out/$fuzzer $others seedcorpus
build-ossfuzz-fast9/fuzz/fuzz_$fuzzer -merge=1 out/cmin/$fuzzer out/$fuzzer
rm -rf out/$fuzzer
mv out/cmin/$fuzzer out/$fuzzer
done
- name: Package the corpus into an artifact
run: |
for fuzzer in $defaultimplfuzzers $implfuzzers; do
for fuzzer in $allfuzzers; do
tar rf corpus.tar out/$fuzzer
done
- name: Save the corpus as a github artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v1
with:
name: corpus
path: corpus.tar
# This takes a subset of the minimized corpus and run it through valgrind. It is slow,
# therefore take a "random" subset. The random selection is accomplished by sorting on filenames,
# which are hashes of the content.
- name: Run some of the minimized corpus through valgrind (replay build, default implementation)
- name: Run the corpus through valgrind (normal build)
run: |
for fuzzer in $defaultimplfuzzers $implfuzzers; do
find out/$fuzzer -type f |sort|head -n200|xargs -n40 valgrind build-replay/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer.txt
done
for fuzzer in $allfuzzers; do
find out/$fuzzer -type f |sort|xargs valgrind build-plain-noavx/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer-noavx.txt
done
- name: Run the corpus through valgrind (noavx build)
run: |
for fuzzer in $allfuzzers; do
find out/$fuzzer -type f |sort|xargs valgrind build-plain-normal/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer-normal.txt
done
- name: Compress the valgrind output
run: tar cf valgrind.tar valgrind-*.txt
- name: Save valgrind output as a github artifact
uses: actions/upload-artifact@v2
if: always()
uses: actions/upload-artifact@v1
with:
name: valgrindresults
path: valgrind.tar
if-no-files-found: ignore
- name: Upload the corpus and results to bintray if we are on master
if: ${{ github.event_name == 'schedule' }}
run: |
echo uploading each artifact twice, otherwise it will not be published
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
- name: Archive any crashes as an artifact
uses: actions/upload-artifact@v2
if: always()
with:
name: crashes
path: |
crash-*
leak-*
timeout-*
if-no-files-found: ignore
if [ $(git rev-parse --verify HEAD) = $(git rev-parse --verify origin/master) ] ; then
echo uploading each artifact twice, otherwise it will not be published
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
else
echo "not on master, won't upload to bintray"
fi
+10 -20
View File
@@ -1,12 +1,7 @@
name: MinGW32-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
on: push
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
@@ -15,9 +10,6 @@ on:
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-gcc
runs-on: windows-2016
@@ -35,13 +27,7 @@ jobs:
path: |
C:\ProgramData\scoop
key: scoop32 # static key: should be good forever
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Setup Windows # This should almost never run if the cache works.
- name: Setup Windows # This should almost never run if the cache works.
if: steps.cache.outputs.cache-hit != 'true'
shell: powershell
run: |
@@ -50,7 +36,7 @@ jobs:
sudo scoop install git --global
sudo scoop install ninja --global
sudo scoop install cmake --global
sudo scoop install gcc --arch 32bit --global
sudo scoop install gcc --arch 32bit --global
$env:path
Write-Host 'Everything has been installed, you are good!'
- name: Build and Test 32-bit x86
@@ -64,5 +50,9 @@ jobs:
mkdir build32
cd build32
cmake -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
cmake --build . --target basictests numberparsingcheck stringparsingcheck errortests integer_tests pointercheck --verbose
ctest . -R stringparsingcheck --output-on-failure
ctest . -R numberparsingcheck --output-on-failure
ctest . -R errortests --output-on-failure
ctest . -R integer_tests --output-on-failure
ctest . -R pointercheck --output-on-failure
+12 -26
View File
@@ -1,12 +1,7 @@
name: MinGW64-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
on: push
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
@@ -15,9 +10,6 @@ on:
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-gcc
runs-on: windows-2016
@@ -29,25 +21,20 @@ jobs:
steps: # To reproduce what is below, start a powershell with administrative rights, using scoop *is* a good idea
- uses: actions/checkout@v2
- uses: actions/cache@v2 # we cache the scoop setup with 64-bit GCC
id: cache
with:
path: |
C:\ProgramData\scoop
key: scoop64 # static key: should be good forever
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
key: scoop64 # static key: should be good forever
- name: Setup Windows # This should almost never run if the cache works.
if: steps.cache.outputs.cache-hit != 'true'
shell: powershell
run: |
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install sudo --global
sudo scoop install git --global
sudo scoop install git --global
sudo scoop install ninja --global
sudo scoop install cmake --global
sudo scoop install gcc --arch 64bit --global
@@ -64,11 +51,10 @@ jobs:
mkdir build64
cd build64
cmake -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
cd ..
mkdir build64debug
cd build64debug
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
cmake --build . --target basictests numberparsingcheck stringparsingcheck errortests integer_tests pointercheck --verbose
ctest . -R stringparsingcheck --output-on-failure
ctest . -R numberparsingcheck --output-on-failure
ctest . -R errortests --output-on-failure
ctest . -R integer_tests --output-on-failure
ctest . -R pointercheck --output-on-failure
-54
View File
@@ -1,54 +0,0 @@
name: MSYS2-CLANG-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows-mingw:
name: ${{ matrix.msystem }}
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Release
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
type: Release
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Debug
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang
type: Debug
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{ matrix.msystem }}
install: ${{ matrix.install }}
- name: Build and Test
run: |
mkdir build
cd build
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -LE explicitonly
-57
View File
@@ -1,57 +0,0 @@
name: MSYS2-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows-mingw:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: ${{ matrix.msystem }}
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Release
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc
type: Release
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Debug
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc
type: Debug
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{ matrix.msystem }}
install: ${{ matrix.install }}
- name: Build and Test
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -LE explicitonly
-65
View File
@@ -1,65 +0,0 @@
name: short fuzz on the power arch
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
armv7_job:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
# The host should always be Linux
runs-on: ubuntu-20.04
name: Build on ubuntu-20.04 ppc64le
steps:
- uses: actions/checkout@v2.1.0
- uses: uraimo/run-on-arch-action@v2.0.5
name: Run commands
id: runcmd
env:
DEBIAN_FRONTEND: noninteractive
with:
arch: ppc64le
distro: buster
# Not required, but speeds up builds by storing container images in
# a GitHub package registry.
githubToken: ${{ github.token }}
run: |
export CLANGSUFFIX="-7"
apt-get -qq update
apt-get install -q -y clang-7 libfuzzer-7-dev cmake git wget zip ninja-build
mkdir -p build ; cd build
cmake .. -GNinja \
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
-DSIMDJSON_BUILD_STATIC=Off \
-DENABLE_FUZZING=On \
-DSIMDJSON_COMPETITION=OFF \
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
-DSIMDJSON_FUZZ_LDFLAGS=-lFuzzer \
-DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=" \
-DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link" \
-DCMAKE_BUILD_TYPE=Release \
-DSIMDJSON_FUZZ_LINKMAIN=Off
cd ..
builddir=build
cmake --build $builddir
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
tar xf corpus.tar && rm corpus.tar
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
for fuzzer in $fuzzernames ; do
exe=$builddir/fuzz/$fuzzer
shortname=$(echo $fuzzer |cut -f2- -d_)
echo found fuzzer $shortname with executable $exe
mkdir -p out/$shortname
others=$(find out -type d -not -name $shortname -not -name out -not -name cmin)
$exe -max_total_time=20 -max_len=4000 out/$shortname $others
echo "*************************************************************************"
done
echo "all is good, no errors found in any of these fuzzers: $fuzzernames"
-29
View File
@@ -1,29 +0,0 @@
name: Performance check on Ubuntu 18.04 CI (GCC 7)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf ubuntu18-checkperf.yml
-30
View File
@@ -1,30 +0,0 @@
name: Ubuntu 18.04 CI (GCC 7) with Thread Sanitizer
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target parse_many_test &&
ctest --output-on-failure -R parse_many_test &&
ctest --output-on-failure -R document_stream_tests
-31
View File
@@ -1,31 +0,0 @@
name: Ubuntu 18.04 CI (GCC 7)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
-29
View File
@@ -1,29 +0,0 @@
name: Performance check on Ubuntu 20.04 CI (GCC 9)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
-30
View File
@@ -1,30 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) with Thread Sanitizer
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target parse_many_test &&
ctest --output-on-failure -R parse_many_test &&
ctest --output-on-failure -R document_stream_tests
-31
View File
@@ -1,31 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -LE explicitonly &&
make install &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
-29
View File
@@ -1,29 +0,0 @@
name: VS15-CI
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs15
runs-on: windows-2016
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 15 2017, arch: Win32}
- {gen: Visual Studio 15 2017, arch: x64}
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
mkdir build
cd build && cmake -DSIMDJSON_COMPETITION=OFF -G "${{matrix.gen}}" -A ${{matrix.arch}} ..
- name: Build
run: cmake --build build --config Release --parallel
- name: 'Run CTest'
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
-39
View File
@@ -1,39 +0,0 @@
name: VS16-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
-38
View File
@@ -1,38 +0,0 @@
name: VS16-CLANG-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
-39
View File
@@ -1,39 +0,0 @@
name: VS16-Ninja-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
-36
View File
@@ -1,36 +0,0 @@
name: VS16-NoExcept-CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_EXCEPTIONS=OFF
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
-1
View File
@@ -96,4 +96,3 @@ objs
# Generated docs
/doc/api
*.orig
+36
View File
@@ -0,0 +1,36 @@
[submodule "scalarvssimd/rapidjson"]
path = dependencies/rapidjson
url = https://github.com/Tencent/rapidjson.git
[submodule "dependencies/sajson"]
path = dependencies/sajson
url = https://github.com/chadaustin/sajson.git
[submodule "dependencies/json11"]
path = dependencies/json11
url = https://github.com/dropbox/json11.git
[submodule "dependencies/fastjson"]
path = dependencies/fastjson
url = https://github.com/mikeando/fastjson.git
[submodule "dependencies/gason"]
path = dependencies/gason
url = https://github.com/vivkin/gason.git
[submodule "dependencies/ujson4c"]
path = dependencies/ujson4c
url = https://github.com/esnme/ujson4c.git
[submodule "dependencies/jsmn"]
path = dependencies/jsmn
url = https://github.com/zserge/jsmn.git
[submodule "dependencies/cJSON"]
path = dependencies/cJSON
url = https://github.com/DaveGamble/cJSON.git
[submodule "dependencies/jsoncpp"]
path = dependencies/jsoncpp
url = https://github.com/open-source-parsers/jsoncpp.git
[submodule "dependencies/json"]
path = dependencies/json
url = https://github.com/nlohmann/json.git
[submodule "dependencies/benchmark"]
path = dependencies/benchmark
url = https://github.com/google/benchmark.git
[submodule "dependencies/cxxopts"]
path = dependencies/cxxopts
url = https://github.com/jarro2783/cxxopts
+27 -185
View File
@@ -1,188 +1,30 @@
language: cpp
sudo: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-7
- g++-7
- clang-format
- python
branches:
only:
- master
dist: bionic
script:
- export CXX=g++-7
- export CC=gcc-7
- make
- make test
- make everything
- make amalgamate
- make clean
- make SANITIZEGOLD=1 test
- make clean
- ARCHFLAGS="-march=nehalem" make
- ARCHFLAGS="-march=nehalem" make test
- ARCHFLAGS="-march=nehalem" make everything
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
arch:
- ppc64le
cache:
directories:
- $HOME/.dep_cache
env:
global:
- simdjson_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache
matrix:
include:
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-8
env:
- COMPILER="CC=gcc-8 && CXX=g++-8"
compiler: gcc-8
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-9
env:
- COMPILER="CC=gcc-9 && CXX=g++-9"
compiler: gcc-9
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-10
env:
- COMPILER="CC=gcc-10 && CXX=g++-10"
compiler: gcc-10
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-10
env:
- COMPILER="CC=gcc-10 && CXX=g++-10"
- SANITIZE="on"
compiler: gcc-10-sanitize
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-10
env:
- COMPILER="CC=gcc-10 && CXX=g++-10"
- STATIC="on"
compiler: gcc-10-static
- os: linux
addons:
apt:
sources:
- llvm-toolchain-bionic-6.0
packages:
- clang-6.0
env:
- COMPILER="CC=clang-6.0 && CXX=clang++-6.0"
compiler: clang-6
- os: linux
addons:
apt:
sources:
- llvm-toolchain-bionic-7
packages:
- clang-7
env:
- COMPILER="CC=clang-7 && CXX=clang++-7"
compiler: clang-7
- os: linux
addons:
apt:
sources:
- llvm-toolchain-bionic-8
packages:
- clang-8
env:
- COMPILER="CC=clang-8 && CXX=clang++-8"
compiler: clang-8
- os: linux
addons:
apt:
sources:
- llvm-toolchain-bionic-9
packages:
- clang-9
env:
- COMPILER="CC=clang-9 && CXX=clang++-9"
compiler: clang-9
- os: linux
addons:
apt:
packages:
- clang-10
sources:
- ubuntu-toolchain-r-test
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
env:
- COMPILER="CC=clang-10 && CXX=clang++-10"
compiler: clang-10
- os: linux
addons:
apt:
packages:
- clang-10
sources:
- ubuntu-toolchain-r-test
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
env:
- COMPILER="CC=clang-10 && CXX=clang++-10"
- STATIC="on"
compiler: clang-10-static
- os: linux
addons:
apt:
packages:
- clang-10
sources:
- ubuntu-toolchain-r-test
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
env:
- COMPILER="CC=clang-10 && CXX=clang++-10"
- SANITIZE="on"
compiler: clang-10-sanitize
before_install:
- eval "${COMPILER}"
install:
- wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | sudo apt-key add -
- sudo apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
- sudo apt-get -qq update
- sudo apt-get purge cmake cmake-data
- sudo apt-get -t simdjson -y install cmake
- export CMAKE_CXX_FLAGS="-maltivec -mcpu=power9 -mtune=power9"
- export CMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}"
- export CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DSIMDJSON_IMPLEMENTATION=ppc64;fallback";
- if [[ "${SANITIZE}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_SANITIZE=ON";
export ASAN_OPTIONS="detect_leaks=0";
fi
- if [[ "${STATIC}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON";
fi
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
script:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . -- -j2
- SIMDJSON_FORCE_IMPLEMENTATION=ppc64 ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation"
+15 -24
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.9) # CMP0069 NEW
project(simdjson
DESCRIPTION "Parsing gigabytes of JSON per second"
@@ -6,11 +6,11 @@ project(simdjson
)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 8)
set(PROJECT_VERSION_PATCH 2)
set(SIMDJSON_SEMANTIC_VERSION "0.8.2" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "7.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "7" CACHE STRING "simdjson library soversion")
set(PROJECT_VERSION_MINOR 4)
set(PROJECT_VERSION_PATCH 7)
set(SIMDJSON_SEMANTIC_VERSION "0.4.7" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "2.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "2" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
include(GNUInstallDirs)
@@ -21,15 +21,20 @@ include(cmake/simdjson-user-cmakecache.cmake)
if(SIMDJSON_JUST_LIBRARY)
message( STATUS "Building just the library, omitting all tests, tools and benchmarks." )
else(SIMDJSON_JUST_LIBRARY)
# Setup tests
endif()
#
# Set up test data
#
if(NOT(SIMDJSON_JUST_LIBRARY))
enable_testing()
add_subdirectory(jsonchecker)
add_subdirectory(jsonexamples)
add_library(test-data INTERFACE)
target_link_libraries(test-data INTERFACE jsonchecker-data jsonchecker-minefield-data jsonexamples-data)
endif(SIMDJSON_JUST_LIBRARY)
target_link_libraries(test-data INTERFACE jsonchecker-data jsonexamples-data)
endif()
#
# Create the top level simdjson library (must be done at this level to use both src/ and include/
# directories) and tools
#
@@ -43,20 +48,6 @@ if(NOT(SIMDJSON_JUST_LIBRARY))
endif()
install(FILES singleheader/simdjson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
include(CMakePackageConfigHelpers)
configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/simdjson-config.cmake.in"
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
VERSION ${SIMDJSON_SEMANTIC_VERSION}
COMPATIBILITY SameMinorVersion)
install(FILES "${PROJECT_BINARY_DIR}/simdjson-config.cmake"
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson")
#
# Compile tools / tests / benchmarks
#
-10
View File
@@ -41,8 +41,6 @@ We have few hard rules, but we have some:
- Printing to standard output or standard error (`stderr`, `stdout`, `std::cerr`, `std::cout`) in the core library is forbidden. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Compiled code should not write to stdout or stderr".
- Calls to `abort()` are forbidden in the core library. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Under no circumstances should your compiled code ever call abort or exit".
- All source code files (.h, .cpp) must be ASCII.
- All C macros introduced in public headers need to be prefixed with either `SIMDJSON_` or `simdjson_`.
- We avoid trailing white space characters within lines. That is, your lines of code should not terminate with unnecessary spaces. Generally, please avoid making unnecessary changes to white-space characters when contributing code.
Tools, tests and benchmarks are not held to these same strict rules.
@@ -70,17 +68,9 @@ Pull requests are always invited. However, we ask that you follow these guidelin
- Changes should be focused and minimal. You should change as few lines of code as possible. Please do not reformat or touch files needlessly.
- New features must be accompanied of new tests, in general.
- Your code should pass our continuous-integration tests. It is your responsability to ensure that your proposal pass the tests. We do not merge pull requests that would break our build.
- An exception to this would be changes to non-code files, such as documentation and assets, or trivial changes to code, such as comments, where it is encouraged to explicitly ask for skipping a CI run using the `[skip ci]` prefix in your Pull Request title **and** in the first line of the most recent commit in a push. Example for such a commit: `[skip ci] Fixed typo in power_of_ten's docs`
This benefits the project in such a way that the CI pipeline is not burdened by running jobs on changes that don't change any behavior in the code, which reduces wait times for other Pull Requests that do change behavior and require testing.
If the benefits of your proposed code remain unclear, we may choose to discard your code: that is not an insult, we frequently discard our own code. We may also consider various alternatives and choose another path. Again, that is not an insult or a sign that you have wasted your time.
Style
-----
Our formatting style is inspired by the LLVM style.
The simdjson library is written using the snake case: when a variable or a function is a phrase, each space is replaced by an underscore character, and the first letter of each word written in lowercase. Compile-time constants are written entirely in uppercase with the same underscore convention.
Code of Conduct
---------------
-1
View File
@@ -35,6 +35,5 @@ Matjaž Ostroveršnik
Nong Li
Furkan Taşkale
Brendan Knapp
Danila Kutenin
# if you have contributed to the project and your name does not
# appear in this list, please let us know!
+1 -1
View File
@@ -41,7 +41,7 @@
#
# Next you can test it as follows:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -LE explicitonly"
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -E checkperf"
#
# The run the complete tests requires you to have built all of simdjson.
#
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "0.8.2"
PROJECT_NUMBER = "0.4.7"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+419 -60
View File
@@ -1,38 +1,11 @@
Hacking simdjson
================
Here is wisdom about how to build, test and run simdjson from within the repository. This is mostly useful for people who plan to contribute simdjson, or maybe study the design.
Here is wisdom about how to build, test and run simdjson from within the repository. *Users* of
simdjson should use the released simdjson.h and simdjson.cpp files.
If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md) guide.
Design notes
------------------------------
The parser works in two stages:
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
The role of stage 1 is to identify pseudo-structural characters as quickly as possible. A character is pseudo-structural if and only if:
1. Not enclosed in quotes, AND
2. Is a non-whitespace character, AND
3. Its preceding character is either:
(a) a structural character, OR
(b) whitespace OR
(c) the final quote in a string.
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
Stage 1 also does unicode validation.
Stage 2 handles all of the rest: number parsings, recognizing atoms like true, false, null, and so forth.
Directory Structure and Source
------------------------------
@@ -40,15 +13,15 @@ simdjson's source structure, from the top level, looks like this:
* **CMakeLists.txt:** The main build system.
* **include:** User-facing declarations and inline definitions (most user-facing functions are inlined).
* simdjson.h: A "main include" that includes files from include/simdjson/. This is equivalent to
* simdjson.h: A "master include" that includes files from include/simdjson/. This is equivalent to
the distributed simdjson.h.
* simdjson/*.h: Declarations for public simdjson classes and functions.
* simdjson/*-inl.h: Definitions for public simdjson classes and functions.
* simdjson/inline/*.h: Definitions for public simdjson classes and functions.
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
implementations).
* simdjson.cpp: A "main source" that includes all implementation files from src/. This is
* simdjson.cpp: A "master source" that includes all implementation files from src/. This is
equivalent to the distributed simdjson.cpp.
* arm64/|fallback/|haswell/|ppc64/|westmere/: Architecture-specific implementations. All functions are
* arm64/|fallback/|haswell/|westmere/: Architecture-specific implementations. All functions are
Each architecture defines its own namespace, e.g. simdjson::haswell.
* generic/: Generic implementations of the simdjson parser. These files may be included and
compiled multiple times, from whichever architectures use them. They assume they are already
@@ -65,9 +38,7 @@ Other important files and directories:
* **.drone.yml:** Definitions for Drone CI.
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
* **.circleci:** Definitions for Circle CI.
* **.github/workflows:** Definitions for GitHub Actions (CI).
* **singleheader:** Contains generated `simdjson.h` and `simdjson.cpp` that we release. The files `singleheader/simdjson.h` and `singleheader/simdjson.cpp` should never be edited by hand.
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script).
* **amalgamate.sh:** Generates singleheader/simdjson.h and singleheader/simdjson.cpp for release.
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnnecessary system calls. We recommend checking the performance as follows:
@@ -93,24 +64,18 @@ Other important files and directories:
* **jsonchecker:** A set of JSON files used to check different functionality of the parser.
* **pass*.json:** Files that should pass validation.
* **fail*.json:** Files that should fail validation.
* **jsonchecker/minefield/y_*.json:** Files that should pass validation.
* **jsonchecker/minefield/n_*.json:** Files that should fail validation.
* **jsonexamples:** A wide spread of useful, real-world JSON files with different characteristics
and sizes.
* **singleheader:** Contains generated simdjson.h and simdjson.cpp that we release.
* **test:** The tests are here. basictests.cpp and errortests.cpp are the primary ones.
* **tools:** Source for executables that can be distributed with simdjson. Some examples:
* `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
* `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
* `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
*`jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
* **tools:** Source for executables that can be distributed with simdjson
> **Don't modify the files in singleheader/ directly; these are automatically generated.**
>
> While we distribute those files on release, we *maintain* the files under include/ and src/.
While simdjson distributes just two files from the singleheader/ directory, we *maintain* the code in
multiple files under include/ and src/. The files include/simdjson.h and src/simdjson.cpp are the "spine" for
these, and you can include them as if they were the corresponding singleheader/ files.
multiple files under include/ and src/. include/simdjson.h and src/simdjson.cpp are the "spine" for
these, and you can include
@@ -159,29 +124,26 @@ We also handle the special case where a user is compiling using LLVM clang under
Regenerating Single-Header Files
Regenerating Single Headers From Master
---------------------------------------
The simdjson.h and simdjson.cpp files in the singleheader directory are not always up-to-date with the rest of the code; they are only ever
systematically regenerated on releases. To ensure you have the latest code, you can regenerate them by running this at the top level:
simdjson.h and simdjson.cpp are not always up to date in master. To ensure you have the latest copy,
you can regenerate them by running this at the top level:
```bash
mkdir build
cd build
cmake ..
cmake --build . # needed, because currently dependencies do not work fully for the amalgamate target
cmake --build . --target amalgamate
```
You need to have python3 installed on your system.
The amalgamator script `amalgamate.py` generates singleheader/simdjson.h by
The amalgamator is at `amalgamate.sh` at the top level. It generates singleheader/simdjson.h by
reading through include/simdjson.h, copy/pasting each header file into the amalgamated file at the
point it gets included (but only once per header). singleheader/simdjson.cpp is generated from
src/simdjson.cpp the same way, except files under generic/ may be included and copy/pasted multiple
times.
### Usage (CMake on 64-bit platforms like Linux, FreeBSD or macOS)
### Usage (CMake on 64-bit platforms like Linux, freeBSD or macOS)
Requirements: In addition to git, we require a recent version of CMake as well as bash.
@@ -194,7 +156,7 @@ brew install cmake
apt-get update -qq
apt-get install -y cmake
```
3. On FreeBSD, you might be able to install bash and CMake as follows:
3. On freeBSD, you might be able to install bash and CMake as follows:
```
pkg update -f
pkg install bash
@@ -244,15 +206,15 @@ Note that the name of directory (`build`) is arbitrary, you can name it as you w
### Usage (CMake on 64-bit Windows using Visual Studio 2019)
### Usage (CMake on 64-bit Windows using Visual Studio)
We assume you have a common 64-bit Windows PC with at least Visual Studio 2019.
We assume you have a common 64-bit Windows PC with at least Visual Studio 2017 and an x64 processor with AVX2 support (2013 Intel Haswell or later) or SSE 4.2 + CLMUL (2010 Westmere or later).
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
- Create a subdirectory within simdjson, such as `build`.
- Using a shell, go to this newly created directory. You can start a shell directly from GitHub Desktop (Repository > Open in Command Prompt).
- Type `cmake ..` in the shell while in the `build` repository.
- Type `cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..` in the shell while in the `build` repository. (Alternatively, if you want to build a DLL, you may use the command line `cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..`.)
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
@@ -273,11 +235,380 @@ Furthermore, if you have installed LLVM clang on Windows, for example as a compo
- `cmake --build . -config Release`
### Usage (Using `vcpkg` on 64-bit Windows, Linux and macOS)
[vcpkg](https://github.com/Microsoft/vcpkg) users on Windows, Linux and macOS can download and install `simdjson` with one single command from their favorite shell.
On 64-bit Linux and macOS:
```
$ ./vcpkg install simdjson
```
will build and install `simdjson` as a static library.
On Windows (64-bit):
```
.\vcpkg.exe install simdjson:x64-windows
```
will build and install `simdjson` as a shared library.
```
.\vcpkg.exe install simdjson:x64-windows-static
```
will build and install `simdjson` as a static library.
These commands will also print out instructions on how to use the library from MSBuild or CMake-based projects.
If you find the version of `simdjson` shipped with `vcpkg` is out-of-date, feel free to report it to
`vcpkg` community either by submitting an issue or by creating a PR.
### Usage (Docker)
One can run tests and benchmarks using docker. It especially makes sense under Linux. Privileged
access may be needed to get performance counters.
```
git clone https://github.com/simdjson/simdjson.git
cd simdjson
docker build -t simdjson .
docker run --privileged -t simdjson
```
## Architecture and Design Notes
### Requirements
- 64-bit platforms like Linux or macOS, as well as Windows through Visual Studio 2017 or later.
- Any 64-bit processor:
- AVX2 (i.e., Intel processors starting with the Haswell microarchitecture released 2013 and AMD
processors starting with the Zen microarchitecture released 2017),
- SSE 4.2 and CLMUL (i.e., Intel processors going back to Westmere released in 2010 or AMD
processors starting with the Jaguar used in the PS4 and XBox One),
- 64-bit ARM processor (ARMv8-A NEON): this covers a wide range of mobile processors, including
all Apple processors currently available for sale, going as far back as the iPhone 5s (2013).
- Any 64-bit processor (simdjson has a fallback generic 64-bit implementation that is still super
fast).
- A recent C++ compiler (e.g., GNU GCC or LLVM CLANG or Visual Studio 2017), we assume C++17. GNU
GCC 7 or better or LLVM's clang 6 or better.
- Some benchmark scripts assume bash and other common utilities, but they are optional.
### Scope
We provide a fast parser, that fully validates an input according to various specifications.
The parser builds a useful immutable (read-only) DOM (document-object model) which can be later accessed.
To simplify the engineering, we make some assumptions.
- We support UTF-8 (and thus ASCII), nothing else (no Latin, no UTF-16). We do not believe this is a
genuine limitation, because we do not think there is any serious application that needs to process
JSON data without an ASCII or UTF-8 encoding. If the UTF-8 contains a leading BOM, it should be
omitted: the user is responsible for detecting and skipping the BOM; UTF-8 BOMs are discouraged.
- All strings in the JSON document may have up to 4294967295 bytes in UTF-8 (4GB). To enforce this
constraint, we refuse to parse a document that contains more than 4294967295 bytes (4GB). This
should accommodate most JSON documents.
- As allowed by the specification, we allow repeated keys within an object (other parsers like
sajson do the same).
- [The simdjson library is fast for JSON documents spanning a few bytes up to many megabytes](https://github.com/lemire/simdjson/issues/312).
_We do not aim to provide a general-purpose JSON library._ A library like RapidJSON offers much more
than just parsing, it helps you generate JSON and offers various other convenient functions. We
merely parse the document. This may change in the future.
### Features
- The input string is unmodified. (Parsers like sajson and RapidJSON use the input string as a buffer.)
- We parse integers and floating-point numbers as separate types which allows us to support large signed 64-bit integers in [-9223372036854775808,9223372036854775808), like a Java `long` or a C/C++ `long long` and large unsigned integers up to the value 18446744073709551615. Among the parsers that differentiate between integers and floating-point numbers, not all support 64-bit integers. (For example, sajson rejects JSON files with integers larger than or equal to 2147483648. RapidJSON will parse a file containing an overly long integer like 18446744073709551616 as a floating-point number.) When we cannot represent exactly an integer as a signed or unsigned 64-bit value, we reject the JSON document.
- We support the full range of 64-bit floating-point numbers (binary64). The values range from ` std::numeric_limits<double>::lowest()` to `std::numeric_limits<double>::max()`, so from -1.7976e308 all the way to 1.7975e308. Extreme values (less or equal to -1e308, greater or equal to 1e308) are rejected: we refuse to parse the input document.
- We test for accurate float parsing with a perfect accuracy (ULP 0). Many parsers offer only approximate floating parsing. For example, RapidJSON also offers the option of accurate float parsing (`kParseFullPrecisionFlag`) but it comes at a significant performance penalty compared to the default settings. By default, RapidJSON tolerates an error of 3 ULP.
- We do full UTF-8 validation as part of the parsing. (Parsers like fastjson, gason and dropbox json11 do not do UTF-8 validation. The sajson parser does incomplete UTF-8 validation, accepting code point
sequences like 0xb1 0x87.)
- We fully validate the numbers. (Parsers like gason and ultranjson will accept `[0e+]` as valid JSON.)
- We validate string content for unescaped characters. (Parsers like fastjson and ultrajson accept unescaped line breaks and tabs in strings.)
- We fully validate the white-space characters outside of the strings. Parsers like RapidJSON will accept JSON documents with null characters outside of strings.
### Architecture
The parser works in two stages:
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
### Remarks on JSON parsing
- The JSON spec defines what a JSON parser is:
> A JSON parser transforms a JSON text into another representation. A JSON parser MUST accept all texts that conform to the JSON grammar. A JSON parser MAY accept non-JSON forms or extensions. An implementation may set limits on the size of texts that it accepts. An implementation may set limits on the maximum depth of nesting. An implementation may set limits on the range and precision of numbers. An implementation may set limits on the length and character contents of strings.
* JSON is not JavaScript:
> All JSON is Javascript but NOT all Javascript is JSON. So {property:1} is invalid because property does not have double quotes around it. {'property':1} is also invalid, because it's single quoted while the only thing that can placate the JSON specification is double quoting. JSON is even fussy enough that {"property":.1} is invalid too, because you should have of course written {"property":0.1}. Also, don't even think about having comments or semicolons, you guessed it: they're invalid. (credit:https://github.com/elzr/vim-json)
* The structural characters are:
begin-array = [ left square bracket
begin-object = { left curly bracket
end-array = ] right square bracket
end-object = } right curly bracket
name-separator = : colon
value-separator = , comma
### Pseudo-structural elements
A character is pseudo-structural if and only if:
1. Not enclosed in quotes, AND
2. Is a non-whitespace character, AND
3. Its preceding character is either:
(a) a structural character, OR
(b) whitespace.
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
### UTF-8 validation (lookup2)
The simdjson library relies on the lookup2 algorithm for UTF-8 validation on x64 platforms.
This algorithm validate the length of multibyte characters (that each multibyte character has the right number of continuation characters, and that all continuation characters are part of a multibyte character).
#### Algorithm
This algorithm compares *expected* continuation characters with *actual* continuation bytes, and emits an error anytime there is a mismatch.
For example, in the string "𝄞₿֏ab", which has a 4-, 3-, 2- and 1-byte
characters, the file will look like this:
| Character | 𝄞 | | | | ₿ | | | ֏ | | a | b |
|-----------------------|----|----|----|----|----|----|----|----|----|----|----|
| Character Length | 4 | | | | 3 | | | 2 | | 1 | 1 |
| Byte | F0 | 9D | 84 | 9E | E2 | 82 | BF | D6 | 8F | 61 | 62 |
| is_second_byte | | X | | | | X | | | X | | |
| is_third_byte | | | X | | | | X | | | | |
| is_fourth_byte | | | | X | | | | | | | |
| expected_continuation | | X | X | X | | X | X | | X | | |
| is_continuation | | X | X | X | | X | X | | X | | |
The errors here are basically (Second Byte OR Third Byte OR Fourth Byte == Continuation):
- **Extra Continuations:** Any continuation that is not a second, third or fourth byte is not
part of a valid 2-, 3- or 4-byte character and is thus an error. It could be that it's just
floating around extra outside of any character, or that there is an illegal 5-byte character,
or maybe it's at the beginning of the file before any characters have started; but it's an
error in all these cases.
- **Missing Continuations:** Any second, third or fourth byte that *isn't* a continuation is an error, because that means
we started a new character before we were finished with the current one.
#### Getting the Previous Bytes
Because we want to know if a byte is the *second* (or third, or fourth) byte of a multibyte
character, we need to "shift the bytes" to find that out. This is what they mean:
- `is_continuation`: if the current byte is a continuation.
- `is_second_byte`: if 1 byte back is the start of a 2-, 3- or 4-byte character.
- `is_third_byte`: if 2 bytes back is the start of a 3- or 4-byte character.
- `is_fourth_byte`: if 3 bytes back is the start of a 4-byte character.
We use shuffles to go n bytes back, selecting part of the current `input` and part of the
`prev_input` (search for `.prev<1>`, `.prev<2>`, etc.). These are passed in by the caller
function, because the 1-byte-back data is used by other checks as well.
#### Getting the Continuation Mask
Once we have the right bytes, we have to get the masks. To do this, we treat UTF-8 bytes as
numbers, using signed `<` and `>` operations to check if they are continuations or leads.
In fact, we treat the numbers as *signed*, partly because it helps us, and partly because
Intel's SIMD presently only offers signed `<` and `>` operations (not unsigned ones).
In UTF-8, bytes that start with the bits 110, 1110 and 11110 are 2-, 3- and 4-byte "leads,"
respectively, meaning they expect to have 1, 2 and 3 "continuation bytes" after them.
Continuation bytes start with 10, and ASCII (1-byte characters) starts with 0.
When treated as signed numbers, they look like this:
| Type | High Bits | Binary Range | Signed |
|--------------|------------|--------------|--------|
| ASCII | `0` | `01111111` | 127 |
| | | `00000000` | 0 |
| 4+-Byte Lead | `1111` | `11111111` | -1 |
| | | `11110000 | -16 |
| 3-Byte Lead | `1110` | `11101111` | -17 |
| | | `11100000 | -32 |
| 2-Byte Lead | `110` | `11011111` | -33 |
| | | `11000000 | -64 |
| Continuation | `10` | `10111111` | -65 |
| | | `10000000 | -128 |
This makes it pretty easy to get the continuation mask! It's just a single comparison:
```
is_continuation = input < -64`
```
We can do something similar for the others, but it takes two comparisons instead of one: "is
the start of a 4-byte character" is `< -32` and `> -65`, for example. And 2+ bytes is `< 0` and
`> -64`. Surely we can do better, they're right next to each other!
#### Getting the is_xxx Masks: Shifting the Range
Notice *why* continuations were a single comparison. The actual *range* would require two
comparisons--`< -64` and `> -129`--but all characters are always greater than -128, so we get
that for free. In fact, if we had *unsigned* comparisons, 2+, 3+ and 4+ comparisons would be
just as easy: 4+ would be `> 239`, 3+ would be `> 223`, and 2+ would be `> 191`.
Instead, we add 128 to each byte, shifting the range up to make comparison easy. This wraps
ASCII down into the negative, and puts 4+-Byte Lead at the top:
| Type | High Bits | Binary Range | Signed |
|----------------------|------------|--------------|-------|
| 4+-Byte Lead (+ 127) | `0111` | `01111111` | 127 |
| | | `01110000 | 112 |
|----------------------|------------|--------------|-------|
| 3-Byte Lead (+ 127) | `0110` | `01101111` | 111 |
| | | `01100000 | 96 |
|----------------------|------------|--------------|-------|
| 2-Byte Lead (+ 127) | `010` | `01011111` | 95 |
| | | `01000000 | 64 |
|----------------------|------------|--------------|-------|
| Continuation (+ 127) | `00` | `00111111` | 63 |
| | | `00000000 | 0 |
|----------------------|------------|--------------|-------|
| ASCII (+ 127) | `1` | `11111111` | -1 |
| | | `10000000` | -128 |
|----------------------|------------|--------------|-------|
*Now* we can use signed `>` on all of them:
```
prev1 = input.prev<1>
prev2 = input.prev<2>
prev3 = input.prev<3>
prev1_flipped = input.prev<1>(prev_input) ^ 0x80; // Same as `+ 128`
prev2_flipped = input.prev<2>(prev_input) ^ 0x80; // Same as `+ 128`
prev3_flipped = input.prev<3>(prev_input) ^ 0x80; // Same as `+ 128`
is_second_byte = prev1_flipped > 63;2+-byte lead
is_third_byte = prev2_flipped > 95;3+-byte lead
is_fourth_byte = prev3_flipped > 111; // 4+-byte lead
```
NOTE: we use `^ 0x80` instead of `+ 128` in the code, which accomplishes the same thing, and even takes the same number
of cycles as `+`, but on many Intel architectures can be parallelized better (you can do 3
`^`'s at a time on Haswell, but only 2 `+`'s).
That doesn't look like it saved us any instructions, did it? Well, because we're adding the
same number to all of them, we can save one of those `+ 128` operations by assembling
`prev2_flipped` out of prev 1 and prev 3 instead of assembling it from input and adding 128
to it. One more instruction saved!
```
prev1 = input.prev<1>
prev3 = input.prev<3>
prev1_flipped = prev1 ^ 0x80; // Same as `+ 128`
prev3_flipped = prev3 ^ 0x80; // Same as `+ 128`
prev2_flipped = prev1_flipped.concat<2>(prev3_flipped): // <shuffle: take the first 2 bytes from prev1 and the rest from prev3
```
#### Bringing It All Together: Detecting the Errors
At this point, we have `is_continuation`, `is_first_byte`, `is_second_byte` and `is_third_byte`.
All we have left to do is check if they match!
```
return (is_second_byte | is_third_byte | is_fourth_byte) ^ is_continuation;
```
But wait--there's more. The above statement is only 3 operations, but they *cannot be done in
parallel*. You have to do 2 `|`'s and then 1 `&`. Haswell, at least, has 3 ports that can do
bitwise operations, and we're only using 1!
#### Epilogue: Addition For Booleans
There is one big case the above code doesn't explicitly talk about--what if is_second_byte
and is_third_byte are BOTH true? That means there is a 3-byte and 2-byte character right next
to each other (or any combination), and the continuation could be part of either of them!
Our algorithm using `&` and `|` won't detect that the continuation byte is problematic.
Never fear, though. If that situation occurs, we'll already have detected that the second
leading byte was an error, because it was supposed to be a part of the preceding multibyte
character, but it *wasn't a continuation*.
We could stop here, but it turns out that we can fix it using `+` and `-` instead of `|` and
`&`, which is both interesting and possibly useful (even though we're not using it here). It
exploits the fact that in SIMD, a *true* value is -1, and a *false* value is 0. So those
comparisons were giving us numbers!
Given that, if you do `is_second_byte + is_third_byte + is_fourth_byte`, under normal
circumstances you will either get 0 (0 + 0 + 0) or -1 (-1 + 0 + 0, etc.). Thus,
`(is_second_byte + is_third_byte + is_fourth_byte) - is_continuation` will yield 0 only if
*both* or *neither* are 0 (0-0 or -1 - -1). You'll get 1 or -1 if they are different. Because
*any* nonzero value is treated as an error (not just -1), we're just fine here :)
Further, if *more than one* multibyte character overlaps,
`is_second_byte + is_third_byte + is_fourth_byte` will be -2 or -3! Subtracting `is_continuation`
from *that* is guaranteed to give you a nonzero value (-1, -2 or -3). So it'll always be
considered an error.
One reason you might want to do this is parallelism. ^ and | are not associative, so
(A | B | C) ^ D will always be three operations in a row: either you do A | B -> | C -> ^ D, or
you do B | C -> | A -> ^ D. But addition and subtraction *are* associative: (A + B + C) - D can
be written as `(A + B) + (C - D)`. This means you can do A + B and C - D at the same time, and
then adds the result together. Same number of operations, but if the processor can run
independent things in parallel (which most can), it runs faster.
This doesn't help us on Intel, but might help us elsewhere: on Haswell, at least, | and ^ have
a super nice advantage in that more of them can be run at the same time (they can run on 3
ports, while + and - can run on 2)! This means that we can do A | B while we're still doing C,
saving us the cycle we would have earned by using +. Even more, using an instruction with a
wider array of ports can help *other* code run ahead, too, since these instructions can "get
out of the way," running on a port other instructions can't.
#### Epilogue II: One More Trick
There's one more relevant trick up our sleeve, it turns out: it turns out on Intel we can "pay
for" the (prev<1> + 128) instruction, because it can be used to save an instruction in
check_special_cases()--but we'll talk about that there :)
## About the Project
### Bindings and Ports of simdjson
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/Licenser/simdjson-rs): Rust port.
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
### Tools
- `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
- `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
- `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
- `jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
### Various References
- [Google double-conv](https://github.com/google/double-conversion/)
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
- [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php)
- https://tools.ietf.org/html/rfc7159
- The Mison implementation in rust https://github.com/pikkr/pikkr
- http://rapidjson.org/md_doc_sax.html
- https://github.com/Geal/parser_benchmarks/tree/master/json
- Gron: A command line tool that makes JSON greppable https://news.ycombinator.com/item?id=16727665
@@ -291,3 +622,31 @@ Inspiring links:
- https://auth0.com/blog/beating-json-performance-with-protobuf/
- https://gist.github.com/shijuvar/25ad7de9505232c87034b8359543404a
- https://github.com/frankmcsherry/blog/blob/master/posts/2018-02-11.md
Validating UTF-8 takes no more than 0.7 cycles per byte:
- https://github.com/lemire/fastvalidate-utf-8 https://lemire.me/blog/2018/05/16/validating-utf-8-strings-using-as-little-as-0-7-cycles-per-byte/
### Academic References
- T.Mühlbauer, W.Rödiger, R.Seilbeck, A.Reiser, A.Kemper, and T.Neumann. Instant loading for main memory databases. PVLDB, 6(14):17021713, 2013. (SIMD-based CSV parsing)
- Mytkowicz, Todd, Madanlal Musuvathi, and Wolfram Schulte. "Data-parallel finite-state machines." ACM SIGARCH Computer Architecture News. Vol. 42. No. 1. ACM, 2014.
- Lu, Yifan, et al. "Tree structured data processing on GPUs." Cloud Computing, Data Science & Engineering-Confluence, 2017 7th International Conference on. IEEE, 2017.
- Sidhu, Reetinder. "High throughput, tree automata based XML processing using FPGAs." Field-Programmable Technology (FPT), 2013 International Conference on. IEEE, 2013.
- Dai, Zefu, Nick Ni, and Jianwen Zhu. "A 1 cycle-per-byte XML parsing accelerator." Proceedings of the 18th annual ACM/SIGDA international symposium on Field programmable gate arrays. ACM, 2010.
- Lin, Dan, et al. "Parabix: Boosting the efficiency of text processing on commodity processors." High Performance Computer Architecture (HPCA), 2012 IEEE 18th International Symposium on. IEEE, 2012. http://parabix.costar.sfu.ca/export/1783/docs/HPCA2012/final_ieee/final.pdf
- Deshmukh, V. M., and G. R. Bamnote. "An empirical evaluation of optimization parameters in XML parsing for performance enhancement." Computer, Communication and Control (IC4), 2015 International Conference on. IEEE, 2015.
- Moussalli, Roger, et al. "Efficient XML Path Filtering Using GPUs." ADMS@ VLDB. 2011.
- Jianliang, Ma, et al. "Parallel speculative dom-based XML parser." High Performance Computing and Communication & 2012 IEEE 9th International Conference on Embedded Software and Systems (HPCC-ICESS), 2012 IEEE 14th International Conference on. IEEE, 2012.
- Li, Y., Katsipoulakis, N.R., Chandramouli, B., Goldstein, J. and Kossmann, D., 2017. Mison: a fast JSON parser for data analytics. Proceedings of the VLDB Endowment, 10(10), pp.1118-1129. http://www.vldb.org/pvldb/vol10/p1118-li.pdf
- Cameron, Robert D., et al. "Parallel scanning with bitstream addition: An xml case study." European Conference on Parallel Processing. Springer, Berlin, Heidelberg, 2011.
- Cameron, Robert D., Kenneth S. Herdy, and Dan Lin. "High performance XML parsing using parallel bit stream technology." Proceedings of the 2008 conference of the center for advanced studies on collaborative research: meeting of minds. ACM, 2008.
- Shah, Bhavik, et al. "A data parallel algorithm for XML DOM parsing." International XML Database Symposium. Springer, Berlin, Heidelberg, 2009.
- Cameron, Robert D., and Dan Lin. "Architectural support for SWAR text processing with parallel bit streams: the inductive doubling principle." ACM Sigplan Notices. Vol. 44. No. 3. ACM, 2009.
- Amagasa, Toshiyuki, Mana Seino, and Hiroyuki Kitagawa. "Energy-Efficient XML Stream Processing through Element-Skipping Parsing." Database and Expert Systems Applications (DEXA), 2013 24th International Workshop on. IEEE, 2013.
- Medforth, Nigel Woodland. "icXML: Accelerating Xerces-C 3.1. 1 using the Parabix Framework." (2013).
- Zhang, Qiang Scott. Embedding Parallel Bit Stream Technology Into Expat. Diss. Simon Fraser University, 2010.
- Cameron, Robert D., et al. "Fast Regular Expression Matching with Bit-parallel Data Streams."
- Lin, Dan. Bits filter: a high-performance multiple string pattern matching algorithm for malware detection. Diss. School of Computing Science-Simon Fraser University, 2010.
- Yang, Shiyang. Validation of XML Document Based on Parallel Bit Stream Technology. Diss. Applied Sciences: School of Computing Science, 2013.
- N. Nakasato, "Implementation of a parallel tree method on a GPU", Journal of Computational Science, vol. 3, no. 3, pp. 132-141, 2012.
+15 -63
View File
@@ -1,9 +1,10 @@
[![Build Status](https://cloud.drone.io/api/badges/simdjson/simdjson/status.svg)](https://cloud.drone.io/simdjson/simdjson)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&q=proj%3Asimdjson&can=2)
![Ubuntu 18.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
[![Build status](https://ci.appveyor.com/api/projects/status/ae77wp5v3lebmu6n/branch/master?svg=true)](https://ci.appveyor.com/project/lemire/simdjson-jmmti/branch/master)
[![simdjson](https://circleci.com/gh/simdjson/simdjson.svg?style=svg)](https://circleci.com/gh/simdjson/simdjson)
[![CirrusCI](https://api.cirrus-ci.com/github/simdjson/simdjson.svg)](https://cirrus-ci.com/github/simdjson/simdjson)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.8.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.4.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
@@ -11,16 +12,13 @@ simdjson : Parsing gigabytes of JSON per second
<img src="images/logo.png" width="10%" style="float: right">
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
to parse JSON 2.5x faster than RapidJSON and 25x faster than JSON for Modern C++.
to parse JSON 2.5x faster than anything else out there.
* **Fast:** Over 2.5x faster than commonly used production-grade JSON parsers.
* **Record Breaking Features:** Minify JSON at 6 GB/s, validate UTF-8 at 13 GB/s, NDJSON at 3.5 GB/s.
* **Easy:** First-class, easy to use and carefully documented APIs.
* **Beyond DOM:** Try the new On Demand API for twice the speed (>4GB/s).
* **Fast:** Over 2.5x faster than other production-grade JSON parsers.
* **Easy:** First-class, easy to use API.
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
* **Peer Reviewed:** Our research appears in venues like VLDB Journal, Software: Practice and Experience.
This library is part of the [Awesome Modern C++](https://awesomecpp.com) list.
@@ -28,7 +26,6 @@ Table of Contents
-----------------
* [Quick Start](#quick-start)
* [On Demand](#on-demand)
* [Documentation](#documentation)
* [Performance results](#performance-results)
* [Real-world usage](#real-world-usage)
@@ -41,14 +38,11 @@ Table of Contents
Quick Start
-----------
The simdjson library is easily consumable with a single .h and .cpp file.
0. Prerequisites: `g++` (version 7 or better) or `clang++` (version 6 or better), and a 64-bit
system with a command-line shell (e.g., Linux, macOS, freeBSD). We also support programming
environments like Visual Studio and Xcode, but different steps are needed.
1. Pull [simdjson.h](singleheader/simdjson.h) and [simdjson.cpp](singleheader/simdjson.cpp) into a
directory, along with the sample file [twitter.json](jsonexamples/twitter.json).
0. Prerequisites: `g++` (version 7 or better) or `clang++` (version 6 or better), and a 64-bit system with a command-line shell (e.g., Linux, macOS, freeBSD). We also support programming environnements like Visual Studio and Xcode, but different steps are needed.
1. Pull [simdjson.h](singleheader/simdjson.h) and [simdjson.cpp](singleheader/simdjson.cpp) into a directory, along with the sample file [twitter.json](jsonexamples/twitter.json).
```
wget https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.h https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.cpp https://raw.githubusercontent.com/simdjson/simdjson/master/jsonexamples/twitter.json
```
@@ -68,34 +62,6 @@ The simdjson library is easily consumable with a single .h and .cpp file.
100 results.
```
### On Demand
The new On Demand JSON parser is just as easy, but much faster due to just-in-time parsing. It is in
alpha right now. More information can be found in the [On Demand Guide](doc/ondemand.md).
1. Do step 1 of the [Quick Start](#quick-start).
2. Create `quickstart.cpp`:
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```
3. `c++ -march=native -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
```
100 results.
```
You'll notice that the code here is very similar to the [main Quick Start code](#quick-start) (and
indeed, it does the same thing). However, if you compare the performance, you should find On
Demand much faster.
Documentation
-------------
@@ -105,7 +71,7 @@ Usage documentation is available:
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
how you can work with it.
* [API](https://simdjson.org/api/0.8.0/annotated.html) contains the automatically generated API documentation.
* [API](https://simdjson.org/api/0.3.1/annotated.html) contains the automatically generated API documentation.
Performance results
-------------------
@@ -154,21 +120,15 @@ or larger files (e.g., 3MB). The following plot presents parsing
speed for [synthetic files over various sizes generated with a script](https://github.com/simdjson/simdjson_experiments_vldb2019/blob/master/experiments/growing/gen.py) on a 3.4 GHz Skylake processor (GNU GCC 9, -O3).
<img src="doc/growing.png" width="90%">
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
You can go beyond 4 GB/s with our new [On Demand API](https://github.com/simdjson/simdjson/blob/master/doc/ondemand.md).
For NDJSON files, we can exceed 3 GB/s with [our multithreaded parsing functions](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md).
Real-world usage
----------------
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
- [Yandex ClickHouse](https://github.com/yandex/ClickHouse)
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
If you are planning to use simdjson in a product, please work from one of our releases.
@@ -178,7 +138,6 @@ Bindings and Ports of simdjson
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
- [libpy_simdjson](https://github.com/gerrymanoim/libpy_simdjson/): high-speed Python bindings for simdjson using [libpy](https://github.com/quantopian/libpy).
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/simd-lite): Rust port.
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
@@ -186,11 +145,8 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
- [fast_jsonparser](https://github.com/anilmaurya/fast_jsonparser): Ruby bindings for the simdjson project.
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
- [simdjson_erlang](https://github.com/ChomperT/simdjson_erlang): erlang bindings.
About simdjson
--------------
@@ -200,12 +156,8 @@ instructions, reducing branch misprediction, and reducing data dependency to tak
CPU's multiple execution cores.
Some people [enjoy reading our paper](https://arxiv.org/abs/1902.08318): A description of the design
and implementation of simdjson is in our research article:
- Geoff Langdale, Daniel Lemire, [Parsing Gigabytes of JSON per Second](https://arxiv.org/abs/1902.08318), VLDB Journal 28 (6), 2019.
We have an in-depth paper focused on the UTF-8 validation:
- John Keiser, Daniel Lemire, [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090), Software: Practice & Experience (to appear)
and implementation of simdjson is in our research article: Geoff Langdale, Daniel
Lemire, [Parsing Gigabytes of JSON per Second](https://arxiv.org/abs/1902.08318), VLDB Journal 28 (6), 2019.
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
+1 -20
View File
@@ -1,23 +1,4 @@
# 0.5
## Highlights
Performance
* Faster and simpler UTF-8 validation with the lookup4 algorithm https://github.com/simdjson/simdjson/pull/993
* We improved the performance of simdjson under Visual Studio by about 25%. Users will still get better performance with clang-cl (+30%) but the gap has been reduced. https://github.com/simdjson/simdjson/pull/1031
Code usability
* In `parse_many`, when parsing streams of JSON documetns, we give to the users runtime control as to whether threads are used (via the parser.threaded attribute). https://github.com/simdjson/simdjson/issues/925
* Prefixed public macros to avoid name clashes with other libraries. https://github.com/simdjson/simdjson/issues/1035
* Better documentation regarding package managers (brew, MSYS2, conan, apt, vcpkg, FreeBSD package manager, etc.).
* Better documentation regarding CMake usage.
Standards
* We improved standard compliance with respect to both the JSON RFC 8259 and JSON Pointer RFC 6901. We added the at_pointer method to nodes for standard-compliant JSON Pointer queries. The legacy `at(std::string_view)` method remains but is deprecated since it is not standard-compliant as per RFC 6901.
* We removed computed GOTOs without sacrificing performance thus improving the C++ standard compliance (since computed GOTOs are compiler-specific extensions).
* Better support for C++20 https://github.com/simdjson/simdjson/pull/1050
# 0.4
# 0.3
## Highlights
+15 -18
View File
@@ -1,7 +1,5 @@
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
link_libraries(simdjson simdjson-flags)
link_libraries(simdjson simdjson-flags simdjson-windows-headers test-data)
add_executable(benchfeatures benchfeatures.cpp)
add_executable(get_corpus_benchmark get_corpus_benchmark.cpp)
add_executable(perfdiff perfdiff.cpp)
@@ -20,21 +18,20 @@ if (TARGET benchmark::benchmark)
link_libraries(benchmark::benchmark)
add_executable(bench_parse_call bench_parse_call.cpp)
add_executable(bench_dom_api bench_dom_api.cpp)
if(SIMDJSON_EXCEPTIONS)
add_executable(bench_ondemand bench_ondemand.cpp)
if(TARGET yyjson)
target_link_libraries(bench_ondemand PRIVATE yyjson)
endif()
if(TARGET rapidjson)
target_link_libraries(bench_ondemand PRIVATE rapidjson)
endif()
if(TARGET sajson)
target_link_libraries(bench_ondemand PRIVATE sajson)
endif()
if(TARGET nlohmann_json)
target_link_libraries(bench_ondemand PRIVATE nlohmann_json)
endif()
endif()
endif()
if (TARGET competition-all)
add_executable(distinctuseridcompetition distinctuseridcompetition.cpp)
target_link_libraries(distinctuseridcompetition competition-core)
add_executable(minifiercompetition minifiercompetition.cpp)
target_link_libraries(minifiercompetition competition-core)
add_executable(parseandstatcompetition parseandstatcompetition.cpp)
target_link_libraries(parseandstatcompetition competition-core)
add_executable(parsingcompetition parsingcompetition.cpp)
target_link_libraries(parsingcompetition competition-core)
add_executable(allparsingcompetition parsingcompetition.cpp)
target_link_libraries(allparsingcompetition competition-all)
target_compile_definitions(allparsingcompetition PRIVATE ALLPARSER)
endif()
include(checkperf.cmake)
+39 -232
View File
@@ -8,189 +8,9 @@ using namespace std;
const padded_string EMPTY_ARRAY("[]", 2);
static const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
static const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
static void recover_one_string(State& state) {
dom::parser parser;
const std::string_view data = "\"one string\"";
padded_string docdata{data};
// we do not want mem. alloc. in the loop.
auto error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse string" << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::string_view v;
error = doc.get(v);
if (error) {
cerr << "could not get string" << error << endl;
return;
}
benchmark::DoNotOptimize(v);
}
}
BENCHMARK(recover_one_string);
static void serialize_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
std::string serial = simdjson::minify(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// we validate the result
{
auto serial = simdjson::minify(doc);
dom::element doc2; // we parse the minified output
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
auto serial2 = simdjson::minify(doc2); // we minify a second time
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_big_string_to_string(State& state) {
dom::parser parser;
std::vector<char> content;
content.push_back('\"');
for(size_t i = 0 ; i < 100000; i ++) {
content.push_back('0' + char(i%10)); // we add what looks like a long list of digits
}
content.push_back('\"');
dom::element doc;
simdjson::error_code error;
if ((error = parser.parse(content.data(), content.size()).get(doc))) {
cerr << "could not parse big string" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
auto serial = simdjson::to_string(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_big_string_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_twitter_to_string(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
auto serial = simdjson::to_string(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// we validate the result
{
auto serial = simdjson::to_string(doc);
dom::element doc2; // we parse the stringify output
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
auto serial2 = simdjson::to_string(doc2); // we stringify again
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_twitter_string_builder(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
simdjson::internal::string_builder<> sb;// not part of our public API, for internal use
for (simdjson_unused auto _ : state) {
sb.clear();
sb.append(doc);
std::string_view serial = sb.str();
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter_string_builder)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void numbers_scan(State& state) {
@@ -202,7 +22,7 @@ static void numbers_scan(State& state) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
double x;
@@ -211,7 +31,7 @@ static void numbers_scan(State& state) {
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_scan);
@@ -224,7 +44,7 @@ static void numbers_size_scan(State& state) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
@@ -236,7 +56,7 @@ static void numbers_size_scan(State& state) {
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_size_scan);
@@ -250,7 +70,7 @@ static void numbers_type_scan(State& state) {
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
return;
}
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
dom::element_type actual_type = e.type();
@@ -276,7 +96,7 @@ static void numbers_type_size_scan(State& state) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
@@ -301,7 +121,7 @@ static void numbers_load_scan(State& state) {
dom::parser parser;
dom::array arr;
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
// this may hit the disk, but probably just once
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
@@ -315,7 +135,7 @@ static void numbers_load_scan(State& state) {
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_load_scan);
@@ -324,7 +144,7 @@ static void numbers_load_size_scan(State& state) {
dom::parser parser;
dom::array arr;
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
// this may hit the disk, but probably just once
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
@@ -341,7 +161,7 @@ static void numbers_load_size_scan(State& state) {
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_load_size_scan);
@@ -353,14 +173,14 @@ static void numbers_exceptions_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
for (double x : arr) {
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_exceptions_scan);
@@ -368,7 +188,7 @@ static void numbers_exceptions_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
@@ -378,7 +198,7 @@ static void numbers_exceptions_size_scan(State& state) {
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_exceptions_size_scan);
@@ -388,7 +208,7 @@ static void numbers_type_exceptions_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
dom::element_type actual_type = e.type();
@@ -407,7 +227,7 @@ static void numbers_type_exceptions_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
@@ -428,7 +248,7 @@ BENCHMARK(numbers_type_exceptions_size_scan);
static void numbers_exceptions_load_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
// this may hit the disk, but probably just once
dom::array arr = parser.load(NUMBERS_JSON);
std::vector<double> container;
@@ -437,14 +257,14 @@ static void numbers_exceptions_load_scan(State& state) {
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_exceptions_load_scan);
static void numbers_exceptions_load_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
// this may hit the disk, but probably just once
dom::array arr = parser.load(NUMBERS_JSON);
std::vector<double> container;
@@ -456,7 +276,7 @@ static void numbers_exceptions_load_size_scan(State& state) {
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
}
BENCHMARK(numbers_exceptions_load_size_scan);
@@ -465,21 +285,20 @@ static void twitter_count(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
uint64_t result_count = doc["search_metadata"]["count"];
if (result_count != 100) { return; }
}
}
BENCHMARK(twitter_count);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_count(State& state) {
// Prints the number of results in twitter.json
padded_string json = padded_string::load(TWITTER_JSON);
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
ParsedJson::Iterator iter(pj);
// uint64_t result_count = doc["search_metadata"]["count"];
if (!iter.move_to_key("search_metadata")) { return; }
@@ -492,18 +311,17 @@ static void iterator_twitter_count(State& state) {
}
BENCHMARK(iterator_twitter_count);
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void twitter_default_profile(State& state) {
// Count unique users with a default profile.
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<string_view> default_users;
for (dom::object tweet : doc["statuses"]) {
dom::object user = tweet["user"];
if (user["default_profile"]) {
default_users.emplace(user["screen_name"]);
default_users.insert(user["screen_name"]);
}
}
if (default_users.size() != 86) { return; }
@@ -516,14 +334,14 @@ static void twitter_image_sizes(State& state) {
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
for (dom::object tweet : doc["statuses"]) {
dom::array media;
if (not (error = tweet["entities"]["media"].get(media))) {
for (dom::object image : media) {
for (auto size : image["sizes"].get<dom::object>()) {
image_sizes.emplace(size.value["w"], size.value["h"]);
image_sizes.insert({ size.value["w"], size.value["h"] });
}
}
}
@@ -541,7 +359,7 @@ static void error_code_twitter_count(State& state) noexcept {
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
uint64_t value;
if ((error = doc["search_metadata"]["count"].get(value))) { return; }
if (value != 100) { return; }
@@ -555,7 +373,7 @@ static void error_code_twitter_default_profile(State& state) noexcept {
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<string_view> default_users;
dom::array tweets;
@@ -577,18 +395,15 @@ static void error_code_twitter_default_profile(State& state) noexcept {
}
BENCHMARK(error_code_twitter_default_profile);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_default_profile(State& state) {
// Count unique users with a default profile.
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<string_view> default_users;
ParsedJson::Iterator iter(pj);
@@ -607,7 +422,7 @@ static void iterator_twitter_default_profile(State& state) {
// default_users.insert(user["screen_name"]);
if (!(iter.move_to_key("screen_name") && iter.is_string())) { return; }
default_users.emplace(iter.get_string(), iter.get_string_length());
default_users.insert(string_view(iter.get_string(), iter.get_string_length()));
}
if (!iter.up()) { return; } // back to user
}
@@ -620,10 +435,8 @@ static void iterator_twitter_default_profile(State& state) {
if (default_users.size() != 86) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(iterator_twitter_default_profile);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void error_code_twitter_image_sizes(State& state) noexcept {
// Count unique image sizes
@@ -631,7 +444,7 @@ static void error_code_twitter_image_sizes(State& state) noexcept {
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
dom::array statuses;
if ((error = doc["statuses"].get(statuses))) { return; }
@@ -645,7 +458,7 @@ static void error_code_twitter_image_sizes(State& state) noexcept {
uint64_t width, height;
if ((error = size.value["w"].get(width))) { return; }
if ((error = size.value["h"].get(height))) { return; }
image_sizes.emplace(width, height);
image_sizes.insert({ width, height });
}
}
}
@@ -655,8 +468,6 @@ static void error_code_twitter_image_sizes(State& state) noexcept {
}
BENCHMARK(error_code_twitter_image_sizes);
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_image_sizes(State& state) {
@@ -665,7 +476,7 @@ static void iterator_twitter_image_sizes(State& state) {
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
ParsedJson::Iterator iter(pj);
@@ -699,7 +510,7 @@ static void iterator_twitter_image_sizes(State& state) {
if (!(iter.move_to_key("h")) && !iter.is_integer()) { return; }
uint64_t height = iter.get_integer();
if (!iter.up()) { return; } // back to size
image_sizes.emplace(width, height);
image_sizes.insert({ width, height });
} while (iter.next()); // next size
if (!iter.up()) { return; } // back to sizes
@@ -711,7 +522,7 @@ static void iterator_twitter_image_sizes(State& state) {
if (!iter.up()) { return; } // back to entities
}
if (!iter.up()) { return; } // back to status
}
}
} while (iter.next()); // next status
}
@@ -720,9 +531,6 @@ static void iterator_twitter_image_sizes(State& state) {
}
BENCHMARK(iterator_twitter_image_sizes);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
static void print_json(State& state) noexcept {
// Prints the number of results in twitter.json
dom::parser parser;
@@ -733,13 +541,12 @@ static void print_json(State& state) noexcept {
int code = json_parse(json, parser);
if (code) { cerr << error_message(code) << endl; return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
std::stringstream s;
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
}
}
BENCHMARK(print_json);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK_MAIN();
BENCHMARK_MAIN();
-72
View File
@@ -1,72 +0,0 @@
#include "simdjson.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "yyjson.h"
#endif
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#endif
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "sajson.h"
#endif
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include <nlohmann/json.hpp>
#endif
// This has to be last, for reasons I don't yet understand
#include <benchmark/benchmark.h>
SIMDJSON_POP_DISABLE_WARNINGS
#include "partial_tweets/simdjson_dom.h"
#include "partial_tweets/simdjson_ondemand.h"
#include "partial_tweets/yyjson.h"
#include "partial_tweets/sajson.h"
#include "partial_tweets/rapidjson.h"
#include "partial_tweets/nlohmann_json.h"
#include "large_random/simdjson_dom.h"
#include "large_random/simdjson_ondemand.h"
#include "large_random/simdjson_ondemand_unordered.h"
#include "large_random/yyjson.h"
#include "large_random/sajson.h"
#include "large_random/rapidjson.h"
#include "large_random/nlohmann_json.h"
#include "kostya/simdjson_dom.h"
#include "kostya/simdjson_ondemand.h"
#include "kostya/yyjson.h"
#include "kostya/sajson.h"
#include "kostya/rapidjson.h"
#include "kostya/nlohmann_json.h"
#include "distinct_user_id/simdjson_dom.h"
#include "distinct_user_id/simdjson_ondemand.h"
#include "distinct_user_id/yyjson.h"
#include "distinct_user_id/sajson.h"
#include "distinct_user_id/rapidjson.h"
#include "distinct_user_id/nlohmann_json.h"
#include "find_tweet/simdjson_dom.h"
#include "find_tweet/simdjson_ondemand.h"
#include "find_tweet/yyjson.h"
#include "find_tweet/sajson.h"
#include "find_tweet/rapidjson.h"
#include "find_tweet/nlohmann_json.h"
#include "top_tweet/simdjson_dom.h"
#include "top_tweet/simdjson_ondemand.h"
#include "top_tweet/yyjson.h"
#include "top_tweet/sajson.h"
#include "top_tweet/rapidjson.h"
#include "top_tweet/nlohmann_json.h"
BENCHMARK_MAIN();
+11 -46
View File
@@ -10,36 +10,6 @@ const char *GSOC_JSON = SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json";
static void unicode_validate_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
bool is_ok = simdjson::validate_utf8(docdata.data(), docdata.size());
bytes += docdata.size();
benchmark::DoNotOptimize(is_ok);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(unicode_validate_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void parse_twitter(State& state) {
dom::parser parser;
padded_string docdata;
@@ -55,9 +25,10 @@ static void parse_twitter(State& state) {
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
dom::element doc;
bytes += docdata.size();
;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
@@ -90,7 +61,7 @@ static void parse_gsoc(State& state) {
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
bytes += docdata.size();
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
@@ -111,25 +82,22 @@ BENCHMARK(parse_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::v
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void json_parse(State& state) {
ParsedJson pj;
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
auto error = json_parse(EMPTY_ARRAY, pj);
if (error) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(json_parse);
#endif // SIMDJSON_DISABLE_DEPRECATED_API
static void parser_parse_error_code(State& state) {
dom::parser parser;
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
auto error = parser.parse(EMPTY_ARRAY).error();
if (error) { return; }
}
@@ -141,9 +109,9 @@ BENCHMARK(parser_parse_error_code);
static void parser_parse_exception(State& state) {
dom::parser parser;
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
try {
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
UNUSED dom::element doc = parser.parse(EMPTY_ARRAY);
} catch(simdjson_error &j) {
cout << j.what() << endl;
return;
@@ -154,11 +122,10 @@ BENCHMARK(parser_parse_exception);
#endif // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void build_parsed_json(State& state) {
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
if (!parser.valid) { return; }
}
@@ -166,10 +133,8 @@ static void build_parsed_json(State& state) {
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(build_parsed_json);
#endif
static void document_parse_error_code(State& state) {
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
dom::parser parser;
auto error = parser.parse(EMPTY_ARRAY).error();
if (error) { return; }
@@ -180,10 +145,10 @@ BENCHMARK(document_parse_error_code);
#if SIMDJSON_EXCEPTIONS
static void document_parse_exception(State& state) {
for (simdjson_unused auto _ : state) {
for (UNUSED auto _ : state) {
try {
dom::parser parser;
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
UNUSED dom::element doc = parser.parse(EMPTY_ARRAY);
} catch(simdjson_error &j) {
cout << j.what() << endl;
return;
+5 -11
View File
@@ -63,8 +63,8 @@ void print_usage(ostream& out) {
out << "-s STAGE - Stop after the given stage." << endl;
out << " -s stage1 - Stop after find_structural_bits." << endl;
out << " -s all - Run all stages." << endl;
out << "-a ARCH - Use the parser with the designated architecture (HASWELL, WESTMERE," << endl;
out << " PPC64 or ARM64). By default, detects best supported architecture." << endl;
out << "-a ARCH - Use the parser with the designated architecture (HASWELL, WESTMERE" << endl;
out << " or ARM64). By default, detects best supported architecture." << endl;
}
void exit_usage(string message) {
@@ -96,14 +96,8 @@ struct option_struct {
case 'v':
verbose = true;
break;
case 'a': {
auto impl = simdjson::available_implementations[optarg];
if(impl && impl->supported_by_runtime_system()) {
simdjson::active_implementation = impl;
} else {
std::cerr << "implementation " << optarg << " not found or not supported " << std::endl;
}
}
case 'a':
simdjson::active_implementation = simdjson::available_implementations[optarg];
break;
case 's':
if (!strcmp(optarg, "stage1")) {
@@ -164,7 +158,7 @@ struct feature_benchmarker {
}
simdjson_really_inline void run_iterations(size_t iterations, bool stage1_only=false) {
really_inline void run_iterations(size_t iterations, bool stage1_only=false) {
struct7.run_iterations(iterations, stage1_only);
struct7_miss.run_iterations(iterations, stage1_only);
struct7_full.run_iterations(iterations, stage1_only);
+34 -37
View File
@@ -12,9 +12,9 @@
#define BEST_TIME(name, test, expected, pre, repeat, size, verbose) \
do { \
if (verbose) \
std::printf("%-40s\t: ", name); \
printf("%-40s\t: ", name); \
else \
std::printf("\"%-40s\"", name); \
printf("\"%-40s\"", name); \
fflush(NULL); \
event_collector collector; \
event_aggregate aggregate{}; \
@@ -23,50 +23,48 @@
std::atomic_thread_fence(std::memory_order_acquire); \
collector.start(); \
if (test != expected) { \
std::fprintf(stderr, "not expected (%d , %d )", (int)test, \
(int)expected); \
fprintf(stderr, "not expected (%d , %d )", (int)test, (int)expected); \
break; \
} \
std::atomic_thread_fence(std::memory_order_release); \
event_count allocate_count = collector.end(); \
aggregate << allocate_count; \
} \
uint64_t S = size; \
if (collector.has_events()) { \
std::printf("%7.3f", \
aggregate.best.cycles() / static_cast<double>(size)); \
printf("%7.3f", aggregate.best.cycles() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" cycles/byte "); \
printf(" cycles/byte "); \
} \
std::printf("\t"); \
std::printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
printf("\t"); \
printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" instructions/byte "); \
printf(" instructions/byte "); \
} \
std::printf("\t"); \
printf("\t"); \
} \
double gb = static_cast<double>(size) / 1000000000.0; \
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" GB/s "); \
printf(" GB/s "); \
} \
std::printf("\t"); \
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" documents/s "); \
printf(" documents/s "); \
} \
std::printf("\n"); \
std::fflush(NULL); \
printf("\n"); \
fflush(NULL); \
} while (0)
// like BEST_TIME, but no check
#define BEST_TIME_NOCHECK(name, test, pre, repeat, size, verbose) \
do { \
if (verbose) \
std::printf("%-40s\t: ", name); \
printf("%-40s\t: ", name); \
else \
std::printf("\"%-40s\"", name); \
std::fflush(NULL); \
printf("\"%-40s\"", name); \
fflush(NULL); \
event_collector collector; \
event_aggregate aggregate{}; \
for (decltype(repeat) i = 0; i < repeat; i++) { \
@@ -78,32 +76,31 @@
event_count allocate_count = collector.end(); \
aggregate << allocate_count; \
} \
uint64_t S = size; \
if (collector.has_events()) { \
std::printf("%7.3f", \
aggregate.best.cycles() / static_cast<double>(size)); \
printf("%7.3f", aggregate.best.cycles() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" cycles/byte "); \
printf(" cycles/byte "); \
} \
std::printf("\t"); \
std::printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
printf("\t"); \
printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" instructions/byte "); \
printf(" instructions/byte "); \
} \
std::printf("\t"); \
printf("\t"); \
} \
double gb = static_cast<double>(size) / 1000000000.0; \
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" GB/s "); \
printf(" GB/s "); \
} \
std::printf("\t"); \
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" documents/s "); \
printf(" documents/s "); \
} \
std::printf("\n"); \
std::fflush(NULL); \
printf("\n"); \
fflush(NULL); \
} while (0)
#endif
+27 -38
View File
@@ -225,19 +225,8 @@ struct progress_bar {
}
};
/**
* The speed at which we can allocate memory is strictly system specific.
* It depends on the OS and the runtime library. It is subject to various
* system-specific knobs. It is not something that we can reasonably
* benchmark with crude timings.
* If someone wants to optimize how simdjson allocate memory, then it will
* almost surely require a distinct benchmarking tool. What is meant by
* "memory allocation" also requires a definition. Doing "new char[size]" can
* do many different things depending on the system.
*/
enum class BenchmarkStage {
ALL, // This excludes allocation
ALL,
ALLOCATE,
STAGE1,
STAGE2
@@ -245,7 +234,7 @@ enum class BenchmarkStage {
const char* benchmark_stage_name(BenchmarkStage stage) {
switch (stage) {
case BenchmarkStage::ALL: return "All (Without Allocation)";
case BenchmarkStage::ALL: return "All";
case BenchmarkStage::ALLOCATE: return "Allocate";
case BenchmarkStage::STAGE1: return "Stage 1";
case BenchmarkStage::STAGE2: return "Stage 2";
@@ -264,8 +253,8 @@ struct benchmarker {
// Statistics about the JSON file independent of its speed (amount of utf-8, structurals, etc.).
// Loaded on first parse.
json_stats* stats;
// Speed and event summary for full parse (stage 1 and stage 2, but *excluding* allocation)
event_aggregate all_stages_without_allocation{};
// Speed and event summary for full parse (including allocation, stage 1 and stage 2)
event_aggregate all_stages{};
// Speed and event summary for stage 1
event_aggregate stage1{};
// Speed and event summary for stage 2
@@ -296,26 +285,25 @@ struct benchmarker {
const event_aggregate& operator[](BenchmarkStage stage) const {
switch (stage) {
case BenchmarkStage::ALL: return this->all_stages_without_allocation;
case BenchmarkStage::ALL: return this->all_stages;
case BenchmarkStage::STAGE1: return this->stage1;
case BenchmarkStage::STAGE2: return this->stage2;
case BenchmarkStage::ALLOCATE: return this->allocate_stage;
default: exit_error("Unknown stage"); return this->all_stages_without_allocation;
default: exit_error("Unknown stage"); return this->all_stages;
}
}
int iterations() const {
return all_stages_without_allocation.iterations;
return all_stages.iterations;
}
simdjson_really_inline void run_iteration(bool stage1_only, bool hotbuffers=false) {
really_inline void run_iteration(bool stage1_only, bool hotbuffers=false) {
// Allocate dom::parser
collector.start();
dom::parser parser;
// We always allocate at least 64KB. Smaller allocations may actually be slower under some systems.
error_code error = parser.allocate(json.size() < 65536 ? 65536 : json.size());
error_code error = parser.allocate(json.size());
if (error) {
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON text: " + error_message(error));
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON result: " + error_message(error));
}
event_count allocate_count = collector.end();
allocate_stage << allocate_count;
@@ -341,7 +329,7 @@ struct benchmarker {
// Stage 2 (unified machine) and the rest
if (stage1_only) {
all_stages_without_allocation << stage1_count;
all_stages << stage1_count;
} else {
event_count stage2_count;
collector.start();
@@ -351,7 +339,7 @@ struct benchmarker {
}
stage2_count = collector.end();
stage2 << stage2_count;
all_stages_without_allocation << stage1_count + stage2_count;
all_stages << allocate_count + stage1_count + stage2_count;
}
// Calculate stats the first time we parse
if (stats == NULL) {
@@ -384,7 +372,7 @@ struct benchmarker {
loop << all_loop_count;
}
simdjson_really_inline void run_iterations(size_t iterations, bool stage1_only, bool hotbuffers=false) {
really_inline void run_iterations(size_t iterations, bool stage1_only, bool hotbuffers=false) {
for (size_t i = 0; i<iterations; i++) {
run_iteration(stage1_only, hotbuffers);
}
@@ -398,7 +386,7 @@ struct benchmarker {
prefix,
"Speed",
stage.elapsed_ns() / static_cast<double>(stats->blocks), // per block
percent(stage.elapsed_sec(), all_stages_without_allocation.elapsed_sec()), // %
percent(stage.elapsed_sec(), all_stages.elapsed_sec()), // %
stage.elapsed_ns() / static_cast<double>(stats->bytes), // per byte
stage.elapsed_ns() / static_cast<double>(stats->structurals), // per structural
(static_cast<double>(json.size()) / 1000000000.0) / stage.elapsed_sec() // GB/s
@@ -409,7 +397,7 @@ struct benchmarker {
prefix,
"Cycles",
stage.cycles() / static_cast<double>(stats->blocks),
percent(stage.cycles(), all_stages_without_allocation.cycles()),
percent(stage.cycles(), all_stages.cycles()),
stage.cycles() / static_cast<double>(stats->bytes),
stage.cycles() / static_cast<double>(stats->structurals),
(stage.cycles() / stage.elapsed_sec()) / 1000000000.0
@@ -418,7 +406,7 @@ struct benchmarker {
prefix,
"Instructions",
stage.instructions() / static_cast<double>(stats->blocks),
percent(stage.instructions(), all_stages_without_allocation.instructions()),
percent(stage.instructions(), all_stages.instructions()),
stage.instructions() / static_cast<double>(stats->bytes),
stage.instructions() / static_cast<double>(stats->structurals),
stage.instructions() / static_cast<double>(stage.cycles())
@@ -429,9 +417,9 @@ struct benchmarker {
prefix,
"Misses",
stage.branch_misses(),
percent(stage.branch_misses(), all_stages_without_allocation.branch_misses()),
percent(stage.branch_misses(), all_stages.branch_misses()),
stage.cache_misses(),
percent(stage.cache_misses(), all_stages_without_allocation.cache_misses()),
percent(stage.cache_misses(), all_stages.cache_misses()),
stage.cache_references()
);
}
@@ -468,14 +456,14 @@ struct benchmarker {
allocate_stage.best.cycles() / static_cast<double>(json.size()),
stage1.best.cycles() / static_cast<double>(json.size()),
stage2.best.cycles() / static_cast<double>(json.size()),
all_stages_without_allocation.best.cycles() / static_cast<double>(json.size()),
gb / all_stages_without_allocation.best.elapsed_sec(),
all_stages.best.cycles() / static_cast<double>(json.size()),
gb / all_stages.best.elapsed_sec(),
gb / stage1.best.elapsed_sec(),
gb / stage2.best.elapsed_sec());
} else {
printf("\"%s\"\t\t\t\t\t%f\t%f\t%f\n",
base,
gb / all_stages_without_allocation.best.elapsed_sec(),
gb / all_stages.best.elapsed_sec(),
gb / stage1.best.elapsed_sec(),
gb / stage2.best.elapsed_sec());
}
@@ -502,10 +490,10 @@ struct benchmarker {
stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
}
printf("\n");
printf("All Stages (excluding allocation)\n");
print_aggregate("| " , all_stages_without_allocation.best);
printf("All Stages\n");
print_aggregate("| " , all_stages.best);
// frequently, allocation is a tiny fraction of the running time so we omit it
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages.best.elapsed_sec()) {
printf("|- Allocation\n");
print_aggregate("| ", allocate_stage.best);
}
@@ -516,16 +504,17 @@ struct benchmarker {
if (collector.has_events()) {
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
double freqall = (all_stages_without_allocation.best.cycles() / all_stages_without_allocation.best.elapsed_sec()) / 1000000000.0;
double freqall = (all_stages.best.cycles() / all_stages.best.elapsed_sec()) / 1000000000.0;
double freqmin = min(freq1, freq2);
double freqmax = max(freq1, freq2);
if((freqall < 0.95 * freqmin) or (freqall > 1.05 * freqmax)) {
printf("\nWarning: The processor frequency fluctuates in an expected way!!!\n"
"Expect the overall speed not to match stage 1 and stage 2 speeds.\n"
"Range for stage 1 and stage 2 : [%.3f GHz, %.3f GHz], overall: %.3f GHz.\n",
freqmin, freqmax, freqall);
}
}
printf("\n%.1f documents parsed per second (best)\n", 1.0/static_cast<double>(all_stages_without_allocation.best.elapsed_sec()));
printf("\n%.1f documents parsed per second (best)\n", 1.0/static_cast<double>(all_stages.best.elapsed_sec()));
}
}
};
+3 -3
View File
@@ -7,7 +7,7 @@
# Clone the repository if it's not there
find_package(Git QUIET)
if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) AND (NOT MSVC) ) # We use "-C" which requires a recent git
if (SIMDJSON_IS_UNDER_GIT AND Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) ) # We use "-C" which requires a recent git
message(STATUS "Git is available and it is recent. We are enabling checkperf targets.")
# sync_git_repository(myrepo ...) creates two targets:
# myrepo - if the repo does not exist, creates and syncs it against the origin branch
@@ -89,13 +89,13 @@ if (Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_G
# COMMAND ECHO $<TARGET_FILE:perfdiff> \"$<TARGET_FILE:parse> -t ${SIMDJSON_CHECKPERF_ARGS}\" \"${CHECKPERF_PARSE} -t ${SIMDJSON_CHECKPERF_ARGS}\" }
COMMAND $<TARGET_FILE:perfdiff> $<TARGET_FILE:parse> ${CHECKPERF_PARSE} -H -t ${SIMDJSON_CHECKPERF_ARGS}
)
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation explicitonly)
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation)
set_property(TEST checkperf APPEND PROPERTY DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
else()
if (CMAKE_GENERATOR MATCHES Ninja)
message(STATUS "We disable the checkperf targets under Ninja.")
else()
else()
message(STATUS "Either git is unavailable or else it is too old. We are disabling checkperf targets.")
endif()
endif ()
@@ -1,53 +0,0 @@
#pragma once
#include "json_benchmark/file_runner.h"
#include <vector>
namespace distinct_user_id {
using namespace json_benchmark;
template<typename I>
struct runner : public file_runner<I> {
std::vector<uint64_t> result{};
bool setup(benchmark::State &state) {
return this->load_json(state, TWITTER_JSON);
}
bool before_run(benchmark::State &state) {
if (!file_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
bool after_run(benchmark::State &state) {
if (!file_runner<I>::after_run(state)) { return false; }
std::sort(result.begin(), result.end());
auto last = std::unique(result.begin(), result.end());
result.erase(last, result.end());
return true;
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
size_t items_per_iteration() {
return result.size();
}
};
struct simdjson_dom;
template<typename I> simdjson_really_inline static void distinct_user_id(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace distinct_user_id
@@ -1,27 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "distinct_user_id.h"
namespace distinct_user_id {
struct nlohmann_json {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
for (auto tweet : root["statuses"]) {
result.push_back(tweet["user"]["id"]);
if (tweet.contains("retweeted_status")) {
result.push_back(tweet["retweeted_status"]["user"]["id"]);
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, nlohmann_json)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-58
View File
@@ -1,58 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace rapidjson;
struct rapidjson_base {
Document doc{};
bool run(Document &root, std::vector<uint64_t> &result) {
if (root.HasParseError()) { printf("parse error\n"); return false; }
if (!root.IsObject()) { printf("root is not an object\n"); return false; }
auto statuses = root.FindMember("statuses");
if (statuses == root.MemberEnd() || !statuses->value.IsArray()) { printf("statuses is not an array\n"); return false; }
for (auto &tweet : statuses->value.GetArray()) {
if (!tweet.IsObject()) { return false; }
auto user = tweet.FindMember("user");
if (user == tweet.MemberEnd() || !user->value.IsObject()) { printf("user is not an object\n"); return false; }
auto id = user->value.FindMember("id");
if (id == user->value.MemberEnd() || !id->value.IsUint64()) { printf("id is not an int\n"); return false; }
result.push_back(id->value.GetUint64());
auto retweet = tweet.FindMember("retweeted_status");
if (retweet != tweet.MemberEnd()) {
if (!retweet->value.IsObject()) { printf("retweet is not an object\n"); return false; }
user = retweet->value.FindMember("user");
if (user == retweet->value.MemberEnd() || !user->value.IsObject()) { printf("rewtweet.user is not an object\n"); return false; }
id = user->value.FindMember("id");
if (id == user->value.MemberEnd() || !id->value.IsUint64()) { printf("retweet.id is not an int\n"); return false; }
result.push_back(id->value.GetUint64());
}
}
return true;
}
};
struct rapidjson : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson)->UseManualTime();
struct rapidjson_insitu : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_insitu)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-80
View File
@@ -1,80 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "distinct_user_id.h"
namespace distinct_user_id {
struct sajson {
size_t ast_buffer_size{0};
size_t *ast_buffer{nullptr};
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
auto val = obj.get_value_of_key({key.data(), key.length()});
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
return { val.as_cstring(), val.get_string_length() };
}
simdjson_really_inline uint64_t get_str_uint64(const ::sajson::value &obj, std::string_view key) {
// Since sajson only supports 53-bit numbers, and IDs in twitter.json can be > 53 bits, we read the corresponding id_str and parse that.
auto val = obj.get_value_of_key({key.data(), key.length()});
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field not a string"; }
auto str = val.as_cstring();
char *endptr;
uint64_t result = strtoull(str, &endptr, 10);
if (endptr != &str[val.get_string_length()]) { throw "field is a string, but not an integer string"; }
return result;
}
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
using namespace sajson;
if (!ast_buffer) {
ast_buffer_size = json.size();
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
}
auto doc = parse(
bounded_allocation(ast_buffer, ast_buffer_size),
mutable_string_view(json.size(), json.data())
);
if (!doc.is_valid()) { return false; }
auto root = doc.get_root();
if (root.get_type() != TYPE_OBJECT) { return false; }
auto statuses = root.get_value_of_key({"statuses", strlen("statuses")});
if (statuses.get_type() != TYPE_ARRAY) { return false; }
for (size_t i=0; i<statuses.get_length(); i++) {
auto tweet = statuses.get_array_element(i);
// get tweet.user.id
if (tweet.get_type() != TYPE_OBJECT) { return false; }
auto user = tweet.get_value_of_key({"user", strlen("user")});
if (user.get_type() != TYPE_OBJECT) { return false; }
result.push_back(get_str_uint64(user, "id_str"));
// get tweet.retweeted_status.user.id
auto retweet = tweet.get_value_of_key({"retweeted_status", strlen("retweeted_status")});
switch (retweet.get_type()) {
case TYPE_OBJECT: {
auto retweet_user = retweet.get_value_of_key({"user", strlen("user")});
if (retweet_user.get_type() != TYPE_OBJECT) { return false; }
result.push_back(get_str_uint64(retweet_user, "id_str"));
break;
}
// TODO distinguish null and missing. null is bad. missing is fine.
case TYPE_NULL:
break;
default:
return false;
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, sajson)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_SAJSON
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_dom {
dom::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.parse(json);
for (dom::object tweet : doc["statuses"]) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet["user"]["id"]);
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet = tweet["retweeted_status"];
if (retweet.error() != NO_SUCH_FIELD) {
result.push_back(retweet["user"]["id"]);
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_dom)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
@@ -1,37 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinct_user_id.h"
namespace distinct_user_id {
using namespace simdjson;
struct simdjson_ondemand {
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc.find_field("statuses")) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not.
result.push_back(tweet.find_field("user").find_field("id"));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet = tweet.find_field("retweeted_status");
if (!retweet.error()) {
result.push_back(retweet.find_field("user").find_field("id"));
}
}
return true;
}
};
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_ondemand)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
-61
View File
@@ -1,61 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "distinct_user_id.h"
namespace distinct_user_id {
struct yyjson_base {
bool run(yyjson_doc *doc, std::vector<uint64_t> &result) {
if (!doc) { return false; }
yyjson_val *root = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(root)) { return false; }
yyjson_val *statuses = yyjson_obj_get(root, "statuses");
if (!yyjson_is_arr(statuses)) { return false; }
// Walk the document, parsing the tweets as we go
size_t tweet_idx, tweets_max;
yyjson_val *tweet;
yyjson_arr_foreach(statuses, tweet_idx, tweets_max, tweet) {
auto user = yyjson_obj_get(tweet, "user");
if (!yyjson_is_obj(user)) { return false; }
auto id = yyjson_obj_get(user, "id");
if (!yyjson_is_uint(id)) { return false; }
result.push_back(yyjson_get_uint(id));
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet = yyjson_obj_get(tweet, "retweeted_status");
if (retweet) {
if (!yyjson_is_obj(retweet)) { return false; }
user = yyjson_obj_get(retweet, "user");
if (!yyjson_is_obj(user)) { return false; }
id = yyjson_obj_get(user, "id");
if (!yyjson_is_uint(id)) { return false; }
result.push_back(yyjson_get_sint(id));
}
}
return true;
}
};
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(distinct_user_id, yyjson_insitu)->UseManualTime();
} // namespace distinct_user_id
#endif // SIMDJSON_COMPETITION_YYJSON
+406
View File
@@ -0,0 +1,406 @@
#include "simdjson.h"
#include <algorithm>
#include <cstring>
#include <unistd.h>
#include <vector>
#include "benchmark.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// #define RAPIDJSON_SSE2 // bad for performance
// #define RAPIDJSON_SSE42 // bad for performance
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "sajson.h"
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
bool equals(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; }
void remove_duplicates(std::vector<int64_t> &v) {
std::sort(v.begin(), v.end());
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());
}
void print_vec(const std::vector<int64_t> &v) {
for (auto i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
}
// clang-format off
// simdjson_recurse below come be implemented like so but it is slow:
/*void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
error_code error;
if (element.is_array()) {
dom::array array;
error = element.get(array);
for (auto child : array) {
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
simdjson_recurse(v, child);
}
}
} else if (element.is_object()) {
int64_t id;
error = element["user"]["id"].get(id);
if(!error) {
v.push_back(id);
}
for (auto [key, value] : object) {
if (value.is<simdjson::dom::array>() || value.is<simdjson::dom::object>()) {
simdjson_recurse(v, value);
}
}
}
}*/
// clang-format on
really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element);
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::array array) {
for (auto child : array) {
simdjson_recurse(v, child);
}
}
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::object object) {
for (auto [key, value] : object) {
if((key.size() == 4) && (memcmp(key.data(), "user", 4) == 0)) {
// we are in an object under the key "user"
simdjson::error_code error;
simdjson::dom::object child_object;
simdjson::dom::object child_array;
if (not (error = value.get(child_object))) {
for (auto [child_key, child_value] : child_object) {
if((child_key.size() == 2) && (memcmp(child_key.data(), "id", 2) == 0)) {
int64_t x;
if (not (error = child_value.get(x))) {
v.push_back(x);
}
}
simdjson_recurse(v, child_value);
}
} else if (not (error = value.get(child_array))) {
simdjson_recurse(v, child_array);
}
// end of: we are in an object under the key "user"
} else {
simdjson_recurse(v, value);
}
}
}
really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
UNUSED simdjson::error_code error;
simdjson::dom::array array;
simdjson::dom::object object;
if (not (error = element.get(array))) {
simdjson_recurse(v, array);
} else if (not (error = element.get(object))) {
simdjson_recurse(v, object);
}
}
really_inline std::vector<int64_t>
simdjson_just_dom(simdjson::dom::element doc) {
std::vector<int64_t> answer;
simdjson_recurse(answer, doc);
remove_duplicates(answer);
return answer;
}
really_inline std::vector<int64_t>
simdjson_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
simdjson::dom::parser parser;
simdjson::dom::element doc;
auto error = parser.parse(p).get(doc);
if (!error) {
simdjson_recurse(answer, doc);
remove_duplicates(answer);
}
return answer;
}
really_inline simdjson::error_code
simdjson_just_parse(const simdjson::padded_string &p) {
simdjson::dom::parser parser;
return parser.parse(p).error();
}
void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
using namespace sajson;
switch (node.get_type()) {
case TYPE_ARRAY: {
auto length = node.get_length();
for (size_t i = 0; i < length; ++i) {
sajson_traverse(answer, node.get_array_element(i));
}
break;
}
case TYPE_OBJECT: {
auto length = node.get_length();
// sajson has O(log n) find_object_key, but we still visit each node anyhow
// because we need to visit all values.
for (auto i = 0u; i < length; ++i) {
auto key = node.get_object_key(i); // expected: sajson::string
bool found_user =
(key.length() == 4) && (memcmp(key.data(), "user", 4) == 0);
if (found_user) { // found a user!!!
auto user_value = node.get_object_value(i); // get the value
if (user_value.get_type() ==
TYPE_OBJECT) { // the value should be an object
// now we know that we only need one value
auto user_value_length = user_value.get_length();
auto right_index =
user_value.find_object_key(sajson::string("id", 2));
if (right_index < user_value_length) {
auto v = user_value.get_object_value(right_index);
if (v.get_type() == TYPE_INTEGER) { // check that it is an integer
answer.push_back(v.get_integer_value()); // record it!
} else if (v.get_type() == TYPE_DOUBLE) {
answer.push_back((int64_t)v.get_double_value()); // record it!
}
}
}
}
sajson_traverse(answer, node.get_object_value(i));
}
break;
}
case TYPE_NULL:
case TYPE_FALSE:
case TYPE_TRUE:
case TYPE_STRING:
case TYPE_DOUBLE:
case TYPE_INTEGER:
break;
default:
assert(false && "unknown node type");
}
}
really_inline std::vector<int64_t>
sasjon_just_dom(sajson::document &d) {
std::vector<int64_t> answer;
sajson_traverse(answer, d.get_root());
remove_duplicates(answer);
return answer;
}
really_inline std::vector<int64_t>
sasjon_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
auto d = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
if (!d.is_valid()) {
free(buffer);
return answer;
}
sajson_traverse(answer, d.get_root());
free(buffer);
remove_duplicates(answer);
return answer;
}
really_inline bool
sasjon_just_parse(const simdjson::padded_string &p) {
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
auto d = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
bool answer = !d.is_valid();
free(buffer);
return answer;
}
void rapid_traverse(std::vector<int64_t> &answer, const rapidjson::Value &v) {
switch (v.GetType()) {
case kObjectType:
for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd();
++m) {
bool found_user = (m->name.GetStringLength() == 4) &&
(memcmp(m->name.GetString(), "user", 4) == 0);
if (found_user) {
const rapidjson::Value &child = m->value;
if (child.GetType() == kObjectType) {
for (Value::ConstMemberIterator k = child.MemberBegin();
k != child.MemberEnd(); ++k) {
if (equals(k->name.GetString(), "id")) {
const rapidjson::Value &val = k->value;
if (val.GetType() == kNumberType) {
answer.push_back(val.GetInt64());
}
}
}
}
}
rapid_traverse(answer, m->value);
}
break;
case kArrayType:
for (Value::ConstValueIterator i = v.Begin(); i != v.End();
++i) { // v.Size();
rapid_traverse(answer, *i);
}
break;
case kNullType:
case kFalseType:
case kTrueType:
case kStringType:
case kNumberType:
default:
break;
}
}
really_inline std::vector<int64_t>
rapid_just_dom(rapidjson::Document &d) {
std::vector<int64_t> answer;
rapid_traverse(answer, d);
remove_duplicates(answer);
return answer;
}
really_inline std::vector<int64_t>
rapid_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
if (d.HasParseError()) {
free(buffer);
return answer;
}
rapid_traverse(answer, d);
free(buffer);
remove_duplicates(answer);
return answer;
}
really_inline bool
rapid_just_parse(const simdjson::padded_string &p) {
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
bool answer = d.HasParseError();
free(buffer);
return answer;
}
int main(int argc, char *argv[]) {
bool verbose = false;
bool just_data = false;
int c;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr
<< "Using different parsers, we compute the content statistics of "
"JSON documents."
<< std::endl;
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
simdjson::padded_string p;
auto error = simdjson::padded_string::load(filename).get(p);
if (error) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
if (verbose) {
std::cout << "Input has ";
if (p.size() > 1000 * 1000)
std::cout << p.size() / (1000 * 1000) << " MB ";
else if (p.size() > 1000)
std::cout << p.size() / 1000 << " KB ";
else
std::cout << p.size() << " B ";
std::cout << std::endl;
}
std::vector<int64_t> s1 = simdjson_compute_stats(p);
if (verbose) {
printf("simdjson: ");
print_vec(s1);
}
std::vector<int64_t> s2 = rapid_compute_stats(p);
if (verbose) {
printf("rapid: ");
print_vec(s2);
}
std::vector<int64_t> s3 = sasjon_compute_stats(p);
if (verbose) {
printf("sasjon: ");
print_vec(s3);
}
assert(s1 == s2);
assert(s1 == s3);
size_t size = s1.size();
int repeat = 500;
size_t volume = p.size();
if (just_data) {
printf(
"name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
BEST_TIME("simdjson ", simdjson_compute_stats(p).size(), size, , repeat,
volume, !just_data);
BEST_TIME("rapid ", rapid_compute_stats(p).size(), size, , repeat, volume,
!just_data);
BEST_TIME("sasjon ", sasjon_compute_stats(p).size(), size, , repeat, volume,
!just_data);
BEST_TIME("simdjson (just parse) ", simdjson_just_parse(p), simdjson::error_code::SUCCESS, , repeat,
volume, !just_data);
BEST_TIME("rapid (just parse) ", rapid_just_parse(p), false, , repeat,
volume, !just_data);
BEST_TIME("sasjon (just parse) ", sasjon_just_parse(p), false, , repeat,
volume, !just_data);
simdjson::dom::parser parser;
simdjson::dom::element doc;
error = parser.parse(p).get(doc);
BEST_TIME("simdjson (just dom) ", simdjson_just_dom(doc).size(), size,
, repeat, volume, !just_data);
char *buffer = (char *)malloc(p.size() + 1);
buffer[p.size()] = '\0';
memcpy(buffer, p.data(), p.size());
rapidjson::Document drapid;
drapid.ParseInsitu<kParseValidateEncodingFlag>(buffer);
BEST_TIME("rapid (just dom) ", rapid_just_dom(drapid).size(), size, , repeat,
volume, !just_data);
memcpy(buffer, p.data(), p.size());
auto dsasjon = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
BEST_TIME("sasjon (just dom) ", sasjon_just_dom(dsasjon).size(), size, ,
repeat, volume, !just_data);
free(buffer);
}
+5 -5
View File
@@ -116,30 +116,30 @@ struct event_collector {
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector(bool quiet = false) : linux_events(vector<int>{
event_collector() : linux_events(vector<int>{
PERF_COUNT_HW_CPU_CYCLES,
PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_MISSES,
PERF_COUNT_HW_CACHE_REFERENCES,
PERF_COUNT_HW_CACHE_MISSES
}, quiet) {}
}) {}
bool has_events() {
return linux_events.is_working();
}
#else
event_collector(simdjson_unused bool _quiet = false) {}
event_collector() {}
bool has_events() {
return false;
}
#endif
simdjson_really_inline void start() {
really_inline void start() {
#if defined(__linux)
linux_events.start();
#endif
start_clock = steady_clock::now();
}
simdjson_really_inline event_count& end() {
really_inline event_count& end() {
time_point<steady_clock> end_clock = steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include "json_benchmark/file_runner.h"
namespace find_tweet {
using namespace json_benchmark;
template<typename I>
struct runner : public file_runner<I> {
typename I::StringType result;
bool setup(benchmark::State &state) {
return this->load_json(state, TWITTER_JSON);
}
bool before_run(benchmark::State &state) {
if (!file_runner<I>::before_run(state)) { return false; }
result = "";
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, 505874901689851904ULL, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
};
struct simdjson_dom;
template<typename I> simdjson_really_inline static void find_tweet(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace find_tweet
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "find_tweet.h"
namespace find_tweet {
struct nlohmann_json {
using StringType=std::string;
bool run(simdjson::padded_string &json, uint64_t find_id, std::string &result) {
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
for (auto tweet : root["statuses"]) {
if (tweet["id"] == find_id) {
result = tweet["text"];
return true;
}
}
return false;
}
};
BENCHMARK_TEMPLATE(find_tweet, nlohmann_json)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-52
View File
@@ -1,52 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "find_tweet.h"
namespace find_tweet {
using namespace rapidjson;
struct rapidjson_base {
using StringType=std::string_view;
Document doc{};
bool run(Document &root, uint64_t find_id, std::string_view &result) {
if (root.HasParseError() || !root.IsObject()) { return false; }
auto statuses = root.FindMember("statuses");
if (statuses == root.MemberEnd() || !statuses->value.IsArray()) { return false; }
for (auto &tweet : statuses->value.GetArray()) {
if (!tweet.IsObject()) { return false; }
auto id = tweet.FindMember("id");
if (id == tweet.MemberEnd() || !id->value.IsUint64()) { return false; }
if (id->value.GetUint64() == find_id) {
auto text = tweet.FindMember("text");
if (text == tweet.MemberEnd() || !text->value.IsString()) { return false; }
result = { text->value.GetString(), text->value.GetStringLength() };
return true;
}
}
return false;
}
};
struct rapidjson : rapidjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, rapidjson)->UseManualTime();
struct rapidjson_insitu : rapidjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, rapidjson_insitu)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-66
View File
@@ -1,66 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "find_tweet.h"
namespace find_tweet {
struct sajson {
using StringType=std::string_view;
size_t ast_buffer_size{0};
size_t *ast_buffer{nullptr};
simdjson_really_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
auto val = obj.get_value_of_key({key.data(), key.length()});
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
return { val.as_cstring(), val.get_string_length() };
}
simdjson_really_inline uint64_t get_str_uint64(const ::sajson::value &obj, std::string_view key) {
// Since sajson only supports 53-bit numbers, and IDs in twitter.json can be > 53 bits, we read the corresponding id_str and parse that.
auto val = obj.get_value_of_key({key.data(), key.length()});
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field not a string"; }
auto str = val.as_cstring();
char *endptr;
uint64_t result = strtoull(str, &endptr, 10);
if (endptr != &str[val.get_string_length()]) { throw "field is a string, but not an integer string"; }
return result;
}
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
if (!ast_buffer) {
ast_buffer_size = json.size();
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
}
auto doc = ::sajson::parse(
::sajson::bounded_allocation(ast_buffer, ast_buffer_size),
::sajson::mutable_string_view(json.size(), json.data())
);
if (!doc.is_valid()) { return false; }
auto root = doc.get_root();
if (root.get_type() != ::sajson::TYPE_OBJECT) { printf("a\n"); return false; }
auto statuses = root.get_value_of_key({"statuses", strlen("statuses")});
if (statuses.get_type() != ::sajson::TYPE_ARRAY) { return false; }
for (size_t i=0; i<statuses.get_length(); i++) {
auto tweet = statuses.get_array_element(i);
if (tweet.get_type() != ::sajson::TYPE_OBJECT) { printf("b\n"); return false; }
// TODO if there is a way to get the raw string, it might be faster to iota find_id and then
// compare it to each id_str, instead of parsing each int and comparing to find_id.
if (get_str_uint64(tweet, "id_str") == find_id) {
result = get_string_view(tweet, "text");
return true;
}
}
return false;
}
};
BENCHMARK_TEMPLATE(find_tweet, sajson)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_SAJSON
-33
View File
@@ -1,33 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "find_tweet.h"
namespace find_tweet {
using namespace simdjson;
struct simdjson_dom {
using StringType=std::string_view;
dom::parser parser{};
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
result = "";
auto doc = parser.parse(json);
for (auto tweet : doc["statuses"]) {
if (uint64_t(tweet["id"]) == find_id) {
result = tweet["text"];
return true;
}
}
return false;
}
};
BENCHMARK_TEMPLATE(find_tweet, simdjson_dom)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_EXCEPTIONS
-33
View File
@@ -1,33 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "find_tweet.h"
namespace find_tweet {
using namespace simdjson;
struct simdjson_ondemand {
using StringType=std::string_view;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (auto tweet : doc.find_field("statuses")) {
if (uint64_t(tweet.find_field("id")) == find_id) {
result = tweet.find_field("text");
return true;
}
}
return false;
}
};
BENCHMARK_TEMPLATE(find_tweet, simdjson_ondemand)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_EXCEPTIONS
-53
View File
@@ -1,53 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "find_tweet.h"
namespace find_tweet {
struct yyjson_base {
using StringType=std::string_view;
bool run(yyjson_doc *doc, uint64_t find_id, std::string_view &result) {
if (!doc) { return false; }
yyjson_val *root = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(root)) { return false; }
yyjson_val *statuses = yyjson_obj_get(root, "statuses");
if (!yyjson_is_arr(statuses)) { return false; }
// Walk the document, parsing the tweets as we go
size_t tweet_idx, tweets_max;
yyjson_val *tweet;
yyjson_arr_foreach(statuses, tweet_idx, tweets_max, tweet) {
if (!yyjson_is_obj(tweet)) { return false; }
auto id = yyjson_obj_get(tweet, "id");
if (!yyjson_is_uint(id)) { return false; }
if (yyjson_get_uint(id) == find_id) {
auto text = yyjson_obj_get(tweet, "text");
if (yyjson_is_str(id)) { return false; }
result = { yyjson_get_str(text), yyjson_get_len(text) };
return true;
}
}
return false;
}
};
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, yyjson)->UseManualTime();
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), find_id, result);
}
};
BENCHMARK_TEMPLATE(find_tweet, yyjson_insitu)->UseManualTime();
} // namespace find_tweet
#endif // SIMDJSON_COMPETITION_YYJSON
+3 -3
View File
@@ -5,11 +5,11 @@
#include <iostream>
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
simdjson_never_inline
never_inline
double bench(std::string filename, simdjson::padded_string& p) {
std::chrono::time_point<std::chrono::steady_clock> start_clock =
std::chrono::steady_clock::now();
simdjson::padded_string::load(filename).value_unsafe().swap(p);
simdjson::padded_string::load(filename).first.swap(p);
std::chrono::time_point<std::chrono::steady_clock> end_clock =
std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed = end_clock - start_clock;
@@ -29,7 +29,7 @@ int main(int argc, char *argv[]) {
<< std::endl;
}
simdjson::padded_string p;
bench(filename, p);
bench(filename, p);
double meanval = 0;
double maxval = 0;
double minval = 10000;
-8
View File
@@ -1,8 +0,0 @@
#pragma once
namespace json_benchmark {
static constexpr const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
static constexpr const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
}
-92
View File
@@ -1,92 +0,0 @@
#pragma once
#include <vector>
#include <sstream>
#include <limits>
namespace json_benchmark {
enum class diff_flags {
NONE = 0,
IMPRECISE_FLOATS = 1
};
template<typename T, typename U>
static bool diff_results(benchmark::State &state, const T &result, const U &reference, diff_flags flags);
template<typename T, typename U>
struct result_differ {
static bool diff(benchmark::State &state, const T &result, const U &reference, diff_flags flags) {
if (result != reference) {
std::stringstream str;
str << "result incorrect: " << result << " ... reference: " << reference;
state.SkipWithError(str.str().data());
return false;
}
return true;
}
};
template<typename T, typename U>
struct result_differ<std::vector<T>, std::vector<U>> {
static bool diff(benchmark::State &state, const std::vector<T> &result, const std::vector<U> &reference, diff_flags flags) {
auto result_iter = result.begin();
auto reference_iter = reference.begin();
while (result_iter != result.end() && reference_iter != reference.end()) {
if (!diff_results(state, *result_iter, *reference_iter, flags)) { return false; }
result_iter++;
reference_iter++;
}
if (result_iter != result.end()) {
std::stringstream str;
str << "extra results (got " << result.size() << ", expected " << reference.size() << "): first extra element: " << *result_iter;
state.SkipWithError(str.str().data());
return false;
} else if (reference_iter != reference.end()) {
std::stringstream str;
str << "missing results (got " << result.size() << ", expected " << reference.size() << "): first missing element: " << *reference_iter;
state.SkipWithError(str.str().data());
return false;
}
return true;
}
};
template<>
struct result_differ<double, double> {
static bool diff(benchmark::State &state, const double &result, const double &reference, diff_flags flags) {
bool different;
if (int(flags) & int(diff_flags::IMPRECISE_FLOATS)) {
different = f64_ulp_dist(result, reference) > 1;
} else {
different = result != reference;
}
if (different) {
std::stringstream str;
// We print it out using full precision.
constexpr auto precision = std::numeric_limits<double>::max_digits10;
str << std::setprecision(precision);
str << "incorrect double result: " << std::endl;
str << " result: " << std::left << std::setw(precision+2) << result << " (hexfloat " << std::hexfloat << result << ")" << std::defaultfloat << std::endl;
str << "reference: " << std::left << std::setw(precision+2) << reference << " (hexfloat " << std::hexfloat << reference << ")" << std::defaultfloat << std::endl;
state.SkipWithError(str.str().data());
}
return true;
}
static uint64_t f64_ulp_dist(double a, double b) {
uint64_t ua, ub;
std::memcpy(&ua, &a, sizeof(ua));
std::memcpy(&ub, &b, sizeof(ub));
if ((int64_t)(ub ^ ua) >= 0)
return (int64_t)(ua - ub) >= 0 ? (ua - ub) : (ub - ua);
return ua + ub + 0x80000000;
}
};
template<typename T, typename U>
static bool diff_results(benchmark::State &state, const T &result, const U &reference, diff_flags flags) {
return result_differ<T, U>::diff(state, result, reference, flags);
}
} // namespace json_benchmark
-48
View File
@@ -1,48 +0,0 @@
#pragma once
#include "json_benchmark/runner_base.h"
#include "simdjson.h"
namespace json_benchmark {
template<typename I>
struct file_runner : public runner_base<I> {
simdjson::padded_string original_json{};
simdjson::padded_string json{};
simdjson_warn_unused bool load_json(benchmark::State &state, const char *file) {
simdjson::error_code error;
if ((error = simdjson::padded_string::load(file).get(original_json))) {
std::stringstream err;
err << "error loading " << file << ": " << error;
state.SkipWithError(err.str().data());
return false;
}
json = simdjson::padded_string(original_json.data(), original_json.size());
return true;
}
simdjson_warn_unused bool before_run(benchmark::State &state) {
if (!runner_base<I>::after_run(state)) { return false; };
// Copy the original json in case we did *in situ* last time
std::memcpy(json.data(), original_json.data(), original_json.size());
return true;
}
/** Get the total number of bytes processed in each iteration. Used for metrics like bytes/second. */
size_t bytes_per_iteration() {
return json.size();
}
/** Get the total number of documents processed in each iteration. Used for metrics like documents/second. */
size_t documents_per_iteration() {
return 1;
}
/** Get the total number of items processed in each iteration. Used for metrics like items/second. */
size_t items_per_iteration() {
return 1;
}
};
} // namespace json_benchmark
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include "diff_results.h"
namespace json_benchmark {
struct point {
double x;
double y;
double z;
};
template<>
struct result_differ<point, point> {
static bool diff(benchmark::State &state, const point &result, const point &reference, diff_flags flags) {
return diff_results(state, result.x, reference.x, flags)
&& diff_results(state, result.y, reference.y, flags)
&& diff_results(state, result.z, reference.z, flags);
}
};
static simdjson_unused std::ostream &operator<<(std::ostream &o, const point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
} // namespace json_benchmark
@@ -1,106 +0,0 @@
#pragma once
#include "simdjson.h"
#include "event_counter.h"
#include <iostream>
namespace json_benchmark {
void maybe_display_implementation() {
static bool displayed_implementation = false;
if(!displayed_implementation) {
displayed_implementation = true;
std::cout << "simdjson::dom implementation: " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation: " << simdjson::builtin_implementation()->name() << std::endl;
}
}
template<typename B, typename R> static void run_json_benchmark(benchmark::State &state) {
maybe_display_implementation();
event_collector collector(true);
event_aggregate events;
// Warmup and equality check (make sure the data is right!)
B bench;
if (!bench.setup(state)) { return; }
if (!bench.before_run(state)) { state.SkipWithError("warmup document before_run failed"); return; }
if (!bench.run(state)) { state.SkipWithError("warmup document reading failed"); return; }
if (!bench.after_run(state)) { state.SkipWithError("warmup document after_run failed"); return; }
{
R reference;
if (!reference.setup(state)) { return; }
if (!reference.before_run(state)) { state.SkipWithError("reference before_run failed"); };
if (!reference.run(state)) { state.SkipWithError("reference document reading failed"); return; }
if (!reference.after_run(state)) { state.SkipWithError("reference before_run failed"); };
if (!bench.diff(state, reference)) { return; }
}
// Run the benchmark
for (simdjson_unused auto _ : state) {
if (!bench.before_run(state)) { state.SkipWithError("before_run failed"); };
collector.start();
if (!bench.run(state)) { state.SkipWithError("run failed"); return; }
auto event = collector.end();
events << event;
state.SetIterationTime(event.elapsed_sec());
if (!bench.after_run(state)) { state.SkipWithError("after_run failed"); return; };
}
state.SetBytesProcessed(bench.bytes_per_iteration() * state.iterations());
state.SetItemsProcessed(bench.items_per_iteration() * state.iterations());
state.counters["best_docs_per_sec"] = benchmark::Counter(double(bench.documents_per_iteration()) / events.best.elapsed_sec());
state.counters["best_bytes_per_sec"] = benchmark::Counter(double(bench.bytes_per_iteration()) / events.best.elapsed_sec());
state.counters["best_items_per_sec"] = benchmark::Counter(double(bench.items_per_iteration()) / events.best.elapsed_sec());
state.counters["docs_per_sec"] = benchmark::Counter(double(bench.documents_per_iteration()), benchmark::Counter::kIsIterationInvariantRate);
if (collector.has_events()) {
state.counters["instructions"] = events.instructions();
state.counters["cycles"] = events.cycles();
state.counters["branch_miss"] = events.branch_misses();
state.counters["cache_miss"] = events.cache_misses();
state.counters["cache_ref"] = events.cache_references();
state.counters["instructions_per_byte"] = events.instructions() / double(bench.bytes_per_iteration());
state.counters["instructions_per_cycle"] = events.instructions() / events.cycles();
state.counters["cycles_per_byte"] = events.cycles() / double(bench.bytes_per_iteration());
state.counters["frequency"] = benchmark::Counter(events.cycles(), benchmark::Counter::kIsIterationInvariantRate);
state.counters["best_instructions"] = events.best.instructions();
state.counters["best_cycles"] = events.best.cycles();
state.counters["best_branch_miss"] = events.best.branch_misses();
state.counters["best_cache_miss"] = events.best.cache_misses();
state.counters["best_cache_ref"] = events.best.cache_references();
state.counters["best_instructions_per_byte"] = events.best.instructions() / double(bench.bytes_per_iteration());
state.counters["best_instructions_per_cycle"] = events.best.instructions() / events.best.cycles();
state.counters["best_cycles_per_byte"] = events.best.cycles() / double(bench.bytes_per_iteration());
state.counters["best_frequency"] = events.best.cycles() / events.best.elapsed_sec();
}
state.counters["bytes"] = benchmark::Counter(double(bench.bytes_per_iteration()));
state.counters["items"] = benchmark::Counter(double(bench.items_per_iteration()));
// Build the label
using namespace std;
stringstream label;
label << fixed << setprecision(2);
label << "[BEST:";
label << " throughput=" << setw(6) << (double(bench.bytes_per_iteration()) / 1000000000.0 / events.best.elapsed_sec()) << " GB/s";
label << " doc_throughput=" << setw(6) << uint64_t(bench.documents_per_iteration() / events.best.elapsed_sec()) << " docs/s";
if (collector.has_events()) {
label << " instructions=" << setw(12) << uint64_t(events.best.instructions()) << setw(0);
label << " cycles=" << setw(12) << uint64_t(events.best.cycles()) << setw(0);
label << " branch_miss=" << setw(8) << uint64_t(events.best.branch_misses()) << setw(0);
label << " cache_miss=" << setw(8) << uint64_t(events.best.cache_misses()) << setw(0);
label << " cache_ref=" << setw(10) << uint64_t(events.best.cache_references()) << setw(0);
}
label << " items=" << setw(10) << bench.items_per_iteration() << setw(0);
label << " avg_time=" << setw(10) << uint64_t(events.elapsed_ns()) << setw(0) << " ns";
label << "]";
state.SetLabel(label.str());
}
} // namespace json_benchmark
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include "constants.h"
#include "run_json_benchmark.h"
#include "diff_results.h"
namespace json_benchmark {
//
// Extend this to create a new type of test (e.g. partial_tweets).
//
template<typename I>
struct runner_base {
/** Run once, before all iterations. */
simdjson_warn_unused bool setup(benchmark::State &) { return true; }
/** Run on each iteration. This is what gets benchmarked. */
simdjson_warn_unused bool run(benchmark::State &state) {
return implementation.run(state);
}
/** Called before each iteration, to clear / set up state. */
simdjson_warn_unused bool before_run(benchmark::State &state) { return true; }
/** Called after each iteration, to tear down / massage state. */
simdjson_warn_unused bool after_run(benchmark::State &) { return true; }
/** Get the total number of bytes processed in each iteration. Used for metrics like bytes/second. */
size_t bytes_per_iteration();
/** Get the total number of documents processed in each iteration. Used for metrics like documents/second. */
size_t documents_per_iteration();
/** Get the total number of items processed in each iteration. Used for metrics like items/second. */
size_t items_per_iteration();
I implementation{};
};
}
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include "runner_base.h"
#include "simdjson.h"
namespace json_benchmark {
template<typename I>
struct string_runner : public runner_base<I> {
const simdjson::padded_string &original_json;
simdjson::padded_string json;
string_runner(const simdjson::padded_string &_json) : original_json{_json}, json(original_json.data(), original_json.size()) {}
simdjson_warn_unused bool before_run(benchmark::State &state) {
if (!runner_base<I>::after_run(state)) { return false; };
// Copy the original json in case we did *in situ*
std::memcpy(json.data(), original_json.data(), original_json.size());
return true;
}
/** Get the total number of bytes processed in each iteration. Used for metrics like bytes/second. */
size_t bytes_per_iteration() {
return json.size();
}
/** Get the total number of documents processed in each iteration. Used for metrics like documents/second. */
size_t documents_per_iteration() {
return 1;
}
/** Get the total number of items processed in each iteration. Used for metrics like items/second. */
size_t items_per_iteration() {
return 1;
}
};
} // namespace json_benchmark
-86
View File
@@ -1,86 +0,0 @@
#pragma once
#include "json_benchmark/string_runner.h"
#include "json_benchmark/point.h"
#include <vector>
#include <random>
namespace kostya {
using namespace json_benchmark;
static const simdjson::padded_string &get_built_json_array();
template<typename I>
struct runner : public string_runner<I> {
std::vector<point> result;
runner() : string_runner<I>(get_built_json_array()) {}
bool before_run(benchmark::State &state) {
if (!string_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, I::DiffFlags);
}
size_t items_per_iteration() {
return result.size();
}
};
static void append_coordinate(std::default_random_engine &e, std::uniform_real_distribution<> &dis, std::stringstream &myss) {
using std::endl;
myss << R"( {)" << endl;
myss << R"( "x": )" << dis(e) << "," << endl;
myss << R"( "y": )" << dis(e) << "," << endl;
myss << R"( "z": )" << dis(e) << "," << endl;
myss << R"( "name": ")" << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << " " << int(dis(e)*10000) << "\"," << endl;
myss << R"( "opts": {)" << endl;
myss << R"( "1": [)" << endl;
myss << R"( 1,)" << endl;
myss << R"( true)" << endl;
myss << R"( ])" << endl;
myss << R"( })" << endl;
myss << R"( })";
}
static std::string build_json_array(size_t N) {
using namespace std;
default_random_engine e;
uniform_real_distribution<> dis(0, 1);
stringstream myss;
myss << R"({)" << endl;
myss << R"( "coordinates": [)" << endl;
for (size_t i=1; i<N; i++) {
append_coordinate(e, dis, myss); myss << "," << endl;
}
append_coordinate(e, dis, myss); myss << endl;
myss << R"( ],)" << endl;
myss << R"( "info": "some info")" << endl;
myss << R"(})" << endl;
string answer = myss.str();
cout << "Creating a source file spanning " << (answer.size() + 512) / 1024 << " KB " << endl;
return answer;
}
static const simdjson::padded_string &get_built_json_array() {
static simdjson::padded_string json = build_json_array(524288);
return json;
}
struct simdjson_dom;
template<typename I> simdjson_really_inline static void kostya(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace kostya
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "kostya.h"
namespace kostya {
struct nlohmann_json {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
for (auto point : root["coordinates"]) {
result.emplace_back(json_benchmark::point{point["x"], point["y"], point["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(kostya, nlohmann_json)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-61
View File
@@ -1,61 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "kostya.h"
namespace kostya {
using namespace rapidjson;
struct rapidjson_base {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
Document doc;
simdjson_really_inline double get_double(Value &object, std::string_view key) {
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing double field"; }
if (!field->value.IsNumber()) { throw "Field is not double"; }
return field->value.GetDouble();
}
bool run(Document &root, std::vector<point> &result) {
if (root.HasParseError()) { return false; }
if (!root.IsObject()) { return false; }
auto coords = root.FindMember("coordinates");
if (coords == root.MemberEnd()) { return false; }
if (!coords->value.IsArray()) { return false; }
for (auto &coord : coords->value.GetArray()) {
if (!coord.IsObject()) { return false; }
result.emplace_back(json_benchmark::point{get_double(coord, "x"), get_double(coord, "y"), get_double(coord, "z")});
}
return true;
}
};
struct rapidjson : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(kostya, rapidjson)->UseManualTime();
struct rapidjson_lossless : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag | kParseFullPrecisionFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(kostya, rapidjson_lossless)->UseManualTime();
struct rapidjson_insitu : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(kostya, rapidjson_insitu)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-65
View File
@@ -1,65 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "kostya.h"
namespace kostya {
struct sajson {
static constexpr diff_flags DiffFlags = diff_flags::IMPRECISE_FLOATS;
size_t ast_buffer_size{0};
size_t *ast_buffer{nullptr};
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
using namespace sajson;
auto val = obj.get_value_of_key({key.data(), key.length()});
switch (val.get_type()) {
case TYPE_INTEGER:
case TYPE_DOUBLE:
return val.get_number_value();
default:
throw "field not double";
}
}
bool run(simdjson::padded_string &json, std::vector<point> &result) {
using namespace sajson;
if (!ast_buffer) {
ast_buffer_size = json.size();
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
}
auto doc = parse(
bounded_allocation(ast_buffer, ast_buffer_size),
mutable_string_view(json.size(), json.data())
);
if (!doc.is_valid()) { return false; }
auto root = doc.get_root();
if (root.get_type() != TYPE_OBJECT) { return false; }
auto points = root.get_value_of_key({"coordinates", strlen("coordinates")});
if (points.get_type() != TYPE_ARRAY) { return false; }
for (size_t i=0; i<points.get_length(); i++) {
auto point = points.get_array_element(i);
if (point.get_type() != TYPE_OBJECT) { return false; }
result.emplace_back(json_benchmark::point{
get_double(point, "x"),
get_double(point, "y"),
get_double(point, "z")
});
}
return true;
}
};
BENCHMARK_TEMPLATE(kostya, sajson)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_SAJSON
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
struct simdjson_dom {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
dom::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
for (auto point : parser.parse(json)["coordinates"]) {
result.emplace_back(json_benchmark::point{point["x"], point["y"], point["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(kostya, simdjson_dom)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
struct simdjson_ondemand {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (ondemand::object point : doc.find_field("coordinates")) {
result.emplace_back(json_benchmark::point{point.find_field("x"), point.find_field("y"), point.find_field("z")});
}
return true;
}
};
BENCHMARK_TEMPLATE(kostya, simdjson_ondemand)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "kostya.h"
namespace kostya {
struct yyjson_base {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
simdjson_really_inline double get_double(yyjson_val *obj, std::string_view key) {
yyjson_val *val = yyjson_obj_getn(obj, key.data(), key.length());
if (!val) { throw "missing point field!"; }
if (yyjson_get_type(val) != YYJSON_TYPE_NUM) { throw "Number is not a type!"; }
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
if (!doc) { return false; }
yyjson_val *root = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(root)) { return false; }
yyjson_val *coords = yyjson_obj_get(root, "coordinates");
if (!yyjson_is_arr(coords)) { return false; }
size_t idx, max;
yyjson_val *coord;
yyjson_arr_foreach(coords, idx, max, coord) {
if (!yyjson_is_obj(coord)) { return false; }
result.emplace_back(json_benchmark::point{get_double(coord, "x"), get_double(coord, "y"), get_double(coord, "z")});
}
return true;
}
};
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(kostya, yyjson)->UseManualTime();
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(kostya, yyjson_insitu)->UseManualTime();
} // namespace kostya
#endif // SIMDJSON_COMPETITION_YYJSON
-73
View File
@@ -1,73 +0,0 @@
#pragma once
#include "json_benchmark/string_runner.h"
#include "json_benchmark/point.h"
#include <random>
namespace large_random {
static const simdjson::padded_string &get_built_json_array();
using namespace json_benchmark;
simdjson_unused static std::ostream &operator<<(std::ostream &o, const point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
template<typename I>
struct runner : public string_runner<I> {
std::vector<point> result;
runner() : string_runner<I>(get_built_json_array()) {}
bool before_run(benchmark::State &state) {
if (!string_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, I::DiffFlags);
}
size_t items_per_iteration() {
return result.size();
}
};
static std::string build_json_array(size_t N) {
std::default_random_engine e;
std::uniform_real_distribution<> dis(0, 1);
std::stringstream myss;
myss << "[" << std::endl;
if(N > 0) {
myss << "{ \"x\":" << dis(e) << ", \"y\":" << dis(e) << ", \"z\":" << dis(e) << "}" << std::endl;
}
for(size_t i = 1; i < N; i++) {
myss << "," << std::endl;
myss << "{ \"x\":" << dis(e) << ", \"y\":" << dis(e) << ", \"z\":" << dis(e) << "}";
}
myss << std::endl;
myss << "]" << std::endl;
std::string answer = myss.str();
std::cout << "Creating a source file spanning " << (answer.size() + 512) / 1024 << " KB " << std::endl;
return answer;
}
static const simdjson::padded_string &get_built_json_array() {
static simdjson::padded_string json = build_json_array(1000000);
return json;
}
struct simdjson_dom;
template<typename T> static void large_random(benchmark::State &state) {
run_json_benchmark<runner<T>, runner<simdjson_dom>>(state);
}
} // namespace large_random
-24
View File
@@ -1,24 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "large_random.h"
namespace large_random {
struct nlohmann_json {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
bool run(simdjson::padded_string &json, std::vector<point> &result) {
for (auto point : nlohmann::json::parse(json.data(), json.data() + json.size())) {
result.emplace_back(json_benchmark::point{point["x"], point["y"], point["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, nlohmann_json)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-59
View File
@@ -1,59 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "large_random.h"
namespace large_random {
using namespace rapidjson;
struct rapidjson_base {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
Document doc;
simdjson_really_inline double get_double(Value &object, std::string_view key) {
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing double field"; }
if (!field->value.IsNumber()) { throw "Field is not double"; }
return field->value.GetDouble();
}
bool run(Document &coords, std::vector<point> &result) {
if (coords.HasParseError()) { return false; }
if (!coords.IsArray()) { return false; }
for (auto &coord : coords.GetArray()) {
if (!coord.IsObject()) { return false; }
result.emplace_back(json_benchmark::point{get_double(coord, "x"), get_double(coord, "y"), get_double(coord, "z")});
}
return true;
}
};
struct rapidjson : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(large_random, rapidjson)->UseManualTime();
struct rapidjson_lossless : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag | kParseFullPrecisionFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(large_random, rapidjson_lossless)->UseManualTime();
struct rapidjson_insitu : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(large_random, rapidjson_insitu)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_RAPIDJSON
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "large_random.h"
namespace large_random {
struct sajson {
static constexpr diff_flags DiffFlags = diff_flags::IMPRECISE_FLOATS;
size_t ast_buffer_size{0};
size_t *ast_buffer{nullptr};
simdjson_really_inline double get_double(const ::sajson::value &obj, std::string_view key) {
using namespace sajson;
auto val = obj.get_value_of_key({key.data(), key.length()});
switch (val.get_type()) {
case TYPE_INTEGER:
case TYPE_DOUBLE:
return val.get_number_value();
default:
throw "field not double";
}
}
bool run(simdjson::padded_string &json, std::vector<point> &result) {
using namespace sajson;
if (!ast_buffer) {
ast_buffer_size = json.size();
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
}
auto doc = parse(
bounded_allocation(ast_buffer, ast_buffer_size),
mutable_string_view(json.size(), json.data())
);
if (!doc.is_valid()) { return false; }
auto points = doc.get_root();
if (points.get_type() != TYPE_ARRAY) { return false; }
for (size_t i=0; i<points.get_length(); i++) {
auto point = points.get_array_element(i);
if (point.get_type() != TYPE_OBJECT) { return false; }
result.emplace_back(json_benchmark::point{
get_double(point, "x"),
get_double(point, "y"),
get_double(point, "z")
});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, sajson)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_SAJSON
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_random.h"
namespace large_random {
using namespace simdjson;
struct simdjson_dom {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
dom::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
for (auto point : parser.parse(json)) {
result.emplace_back(json_benchmark::point{point["x"], point["y"], point["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_dom)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
@@ -1,29 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_random.h"
namespace large_random {
using namespace simdjson;
struct simdjson_ondemand {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (ondemand::object coord : doc) {
result.emplace_back(json_benchmark::point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
@@ -1,29 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "large_random.h"
namespace large_random {
using namespace simdjson;
struct simdjson_ondemand_unordered {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
for (ondemand::object coord : doc) {
result.emplace_back(json_benchmark::point{coord["x"], coord["y"], coord["z"]});
}
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand_unordered)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
-62
View File
@@ -1,62 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_YYJSON
#include "large_random.h"
namespace large_random {
struct yyjson_base {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
simdjson_really_inline double get_double(yyjson_val *obj, std::string_view key) {
yyjson_val *val = yyjson_obj_getn(obj, key.data(), key.length());
if (!val) { throw "missing point field!"; }
if (yyjson_get_type(val) != YYJSON_TYPE_NUM) { throw "Number is not a type!"; }
switch (yyjson_get_subtype(val)) {
case YYJSON_SUBTYPE_UINT:
return yyjson_get_uint(val);
case YYJSON_SUBTYPE_SINT:
return yyjson_get_sint(val);
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
}
bool run(yyjson_doc *doc, std::vector<point> &result) {
if (!doc) { return false; }
yyjson_val *coords = yyjson_doc_get_root(doc);
if (!yyjson_is_arr(coords)) { return false; }
// Walk the document, parsing the tweets as we go
size_t idx, max;
yyjson_val *coord;
yyjson_arr_foreach(coords, idx, max, coord) {
if (!yyjson_is_obj(coord)) { return false; }
result.emplace_back(json_benchmark::point{get_double(coord, "x"), get_double(coord, "y"), get_double(coord, "z")});
}
return true;
}
};
struct yyjson : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
}
};
BENCHMARK_TEMPLATE(large_random, yyjson)->UseManualTime();
struct yyjson_insitu : yyjson_base {
bool run(simdjson::padded_string &json, std::vector<point> &result) {
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
}
};
BENCHMARK_TEMPLATE(large_random, yyjson_insitu)->UseManualTime();
} // namespace large_random
#endif // SIMDJSON_COMPETITION_YYJSON
-52
View File
@@ -1,52 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
ondemand::parser parser{};
std::vector<my_point> container{};
simdjson_really_inline double first_double(ondemand::json_iterator &iter) {
if (iter.start_object().error() || iter.field_key().error() || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
simdjson_really_inline double next_double(ondemand::json_iterator &iter) {
if (!iter.has_next_field() || iter.field_key().error() || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
container.clear();
auto iter = parser.iterate_raw(json).value();
if (iter.start_array()) {
do {
container.emplace_back(my_point{first_double(iter), next_double(iter), next_double(iter)});
if (iter.has_next_field()) { throw "Too many fields"; }
} while (iter.has_next_element());
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, Iter);
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
class OnDemand {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
ondemand::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
container.clear();
auto doc = parser.iterate(json);
for (ondemand::object coord : doc) {
container.emplace_back(my_point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, OnDemand);
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
-120
View File
@@ -1,120 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin::stage2;
class Sax {
public:
simdjson_really_inline bool Run(const padded_string &json) noexcept;
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
simdjson_really_inline error_code RunNoExcept(const padded_string &json) noexcept;
error_code Allocate(size_t new_capacity);
std::unique_ptr<uint8_t[]> string_buf{};
size_t capacity{};
dom_parser_implementation dom_parser{};
std::vector<my_point> container{};
};
struct sax_point_reader_visitor {
public:
std::vector<my_point> &points;
enum {GOT_X=0, GOT_Y=1, GOT_Z=2, GOT_SOMETHING_ELSE=4};
size_t idx{GOT_SOMETHING_ELSE};
double buffer[3]={};
explicit sax_point_reader_visitor(std::vector<my_point> &_points) : points(_points) {}
simdjson_really_inline error_code visit_object_start(json_iterator &) {
idx = 0;
return SUCCESS;
}
simdjson_really_inline error_code visit_primitive(json_iterator &, const uint8_t *value) {
if(idx == GOT_SOMETHING_ELSE) { return simdjson::SUCCESS; }
return numberparsing::parse_double(value).get(buffer[idx]);
}
simdjson_really_inline error_code visit_object_end(json_iterator &) {
points.emplace_back(my_point{buffer[0], buffer[1], buffer[2]});
return SUCCESS;
}
simdjson_really_inline error_code visit_document_start(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_key(json_iterator &, const uint8_t * key) {
switch(key[1]) {
// Technically, we should check the other characters
// in the key, but we are cheating to go as fast
// as possible.
case 'x':
idx = GOT_X;
break;
case 'y':
idx = GOT_Y;
break;
case 'z':
idx = GOT_Z;
break;
default:
idx = GOT_SOMETHING_ELSE;
}
return SUCCESS;
}
simdjson_really_inline error_code visit_array_start(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_array_end(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_document_end(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_empty_array(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_empty_object(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_root_primitive(json_iterator &, const uint8_t *) { return SUCCESS; }
simdjson_really_inline error_code increment_count(json_iterator &) { return SUCCESS; }
};
// NOTE: this assumes the dom_parser is already allocated
bool Sax::Run(const padded_string &json) noexcept {
auto error = RunNoExcept(json);
if (error) { std::cerr << error << std::endl; return false; }
return true;
}
error_code Sax::RunNoExcept(const padded_string &json) noexcept {
container.clear();
// Allocate capacity if needed
if (capacity < json.size()) {
SIMDJSON_TRY( Allocate(json.size()) );
}
// Run stage 1 first.
SIMDJSON_TRY( dom_parser.stage1(json.u8data(), json.size(), false) );
// Then walk the document, parsing the tweets as we go
json_iterator iter(dom_parser, 0);
sax_point_reader_visitor visitor(container);
SIMDJSON_TRY( iter.walk_document<false>(visitor) );
return SUCCESS;
}
error_code Sax::Allocate(size_t new_capacity) {
// string_capacity copied from document::allocate
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
if (capacity == 0) { // set max depth the first time only
if (auto error = dom_parser.set_max_depth(DEFAULT_MAX_DEPTH)) { return error; }
}
capacity = new_capacity;
return SUCCESS;
}
BENCHMARK_TEMPLATE(LargeRandom, Sax);
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+10 -22
View File
@@ -1,22 +1,14 @@
// https://github.com/WojciechMula/toys/blob/master/000helpers/linux-perf-events.h
#pragma once
#ifdef __linux__
#ifdef __has_include
#if __has_include(<asm/unistd.h>)
#include <asm/unistd.h> // for __NR_perf_event_open
#else
#warning "Header asm/unistd.h cannot be found though it is a linux system. Are linux headers missing?"
#endif
#else // no __has_include
// Please insure that linux headers have been installed.
#include <asm/unistd.h> // for __NR_perf_event_open
#endif
#include <linux/perf_event.h> // for perf event constants
#include <sys/ioctl.h> // for ioctl
#include <unistd.h> // for syscall
#include <cerrno> // for errno
#include <cstring> // for std::memset
#include <cstring> // for memset
#include <stdexcept>
#include <iostream>
@@ -28,12 +20,11 @@ template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
perf_event_attr attribs{};
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> result{};
bool quiet;
std::vector<uint64_t> ids{};
public:
explicit LinuxEvents(std::vector<int> config_vec, bool _quiet=false) : fd(0), working(true), quiet{_quiet} {
std::memset(&attribs, 0, sizeof(attribs));
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE;
attribs.size = sizeof(attribs);
attribs.disabled = 1;
@@ -48,7 +39,7 @@ public:
int group = -1; // no group
num_events = config_vec.size();
result.resize(config_vec.size());
ids.resize(config_vec.size());
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
@@ -56,7 +47,7 @@ public:
if (fd == -1) {
report_error("perf_event_open");
}
ioctl(fd, PERF_EVENT_IOC_ID, &result[i++]);
ioctl(fd, PERF_EVENT_IOC_ID, &ids[i++]);
if (group == -1) {
group = fd;
}
@@ -90,7 +81,7 @@ public:
}
}
// our actual results are in slots 1,3,5, ... of this structure
// we really should be checking our result obtained earlier to be safe
// we really should be checking our ids obtained earlier to be safe
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
results[i / 2] = temp_result_vec[i];
}
@@ -102,11 +93,8 @@ public:
private:
void report_error(const std::string &context) {
if (!quiet) {
if (working) {
std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
}
}
if (working)
std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
working = false;
}
};
+184
View File
@@ -0,0 +1,184 @@
#include <iostream>
#include <unistd.h>
#include "benchmark.h"
#include "simdjson.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// #define RAPIDJSON_SSE2 // bad
// #define RAPIDJSON_SSE42 // bad
#include "rapidjson/document.h"
#include "rapidjson/reader.h" // you have to check in the submodule
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "sajson.h"
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
using namespace simdjson;
std::string rapid_stringme_insitu(char *json) {
Document d;
d.ParseInsitu(json);
if (d.HasParseError()) {
std::cerr << "problem!" << std::endl;
return ""; // should do something
}
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
return buffer.GetString();
}
std::string rapid_stringme(char *json) {
Document d;
d.Parse(json);
if (d.HasParseError()) {
std::cerr << "problem!" << std::endl;
return ""; // should do something
}
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
return buffer.GetString();
}
std::string simdjson_stringme(simdjson::padded_string & json) {
std::stringstream ss;
dom::parser parser;
dom::element doc = parser.parse(json);
ss << simdjson::minify(doc);
return ss.str();
}
int main(int argc, char *argv[]) {
int c;
bool verbose = false;
bool just_data = false;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[optind];
simdjson::padded_string p;
auto error = simdjson::padded_string::load(filename).get(p);
if (error) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
if (verbose) {
std::cout << "Input has ";
if (p.size() > 1000 * 1000)
std::cout << p.size() / (1000 * 1000) << " MB ";
else if (p.size() > 1000)
std::cout << p.size() / 1000 << " KB ";
else
std::cout << p.size() << " B ";
std::cout << std::endl;
}
char *buffer = simdjson::internal::allocate_padded_buffer(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
int repeat = 50;
size_t volume = p.size();
if (just_data) {
printf(
"name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
size_t strlength = rapid_stringme((char *)p.data()).size();
if (verbose)
std::cout << "input length is " << p.size() << " stringified length is "
<< strlength << std::endl;
BEST_TIME_NOCHECK("despacing with RapidJSON",
rapid_stringme((char *)p.data()), , repeat, volume,
!just_data);
BEST_TIME_NOCHECK(
"despacing with RapidJSON Insitu", rapid_stringme_insitu((char *)buffer),
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
BEST_TIME_NOCHECK(
"despacing with std::minify", simdjson_stringme(p),, repeat, volume, !just_data);
memcpy(buffer, p.data(), p.size());
size_t outlength;
uint8_t *cbuffer = (uint8_t *)buffer;
for (auto imple : simdjson::available_implementations) {
BEST_TIME((std::string("simdjson->minify+")+imple->name()).c_str(), (imple->minify(cbuffer, p.size(), cbuffer, outlength) == simdjson::SUCCESS ? outlength : -1),
outlength, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
}
printf("minisize = %zu, original size = %zu (minified down to %.2f percent "
"of original) \n",
outlength, p.size(), static_cast<double>(outlength) * 100.0 / static_cast<double>(p.size()));
/***
* Is it worth it to minify before parsing?
***/
rapidjson::Document d;
BEST_TIME("RapidJSON Insitu orig", d.ParseInsitu(buffer).HasParseError(),
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
char *mini_buffer = simdjson::internal::allocate_padded_buffer(p.size() + 1);
size_t minisize;
auto minierror = minify(p.data(), p.size(),mini_buffer, minisize);
if (!minierror) { std::cerr << minierror << std::endl; exit(1); }
mini_buffer[minisize] = '\0';
BEST_TIME("RapidJSON Insitu despaced", d.ParseInsitu(buffer).HasParseError(),
false, memcpy(buffer, mini_buffer, p.size()), repeat, volume,
!just_data);
size_t ast_buffer_size = p.size() * 2;
size_t *ast_buffer = (size_t *)malloc(ast_buffer_size * sizeof(size_t));
BEST_TIME(
"sajson orig",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
BEST_TIME(
"sajson despaced",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(minisize, buffer))
.is_valid(),
true, memcpy(buffer, mini_buffer, p.size()), repeat, volume, !just_data);
simdjson::dom::parser parser;
bool automated_reallocation = false;
BEST_TIME("simdjson orig",
parser.parse((const uint8_t *)buffer, p.size(),
automated_reallocation).error(),
simdjson::SUCCESS, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("simdjson despaced",
parser.parse((const uint8_t *)buffer, minisize,
automated_reallocation).error(),
simdjson::SUCCESS, memcpy(buffer, mini_buffer, p.size()), repeat, volume,
!just_data);
free(buffer);
free(ast_buffer);
free(mini_buffer);
}
+4 -8
View File
@@ -67,9 +67,7 @@ void print_usage(ostream& out) {
out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << endl;
out << " implementation supported on the host machine." << endl;
for (auto impl : simdjson::available_implementations) {
if(impl->supported_by_runtime_system()) {
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
}
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
}
}
@@ -117,13 +115,11 @@ struct option_struct {
break;
case 'a': {
const implementation *impl = simdjson::available_implementations[optarg];
if ((!impl) || (!impl->supported_by_runtime_system())) {
if (!impl) {
std::string exit_message = string("Unsupported option value -a ") + optarg + ": expected -a with one of ";
for (auto imple : simdjson::available_implementations) {
if(imple->supported_by_runtime_system()) {
exit_message += imple->name();
exit_message += " ";
}
exit_message += imple->name();
exit_message += " ";
}
exit_usage(exit_message);
}
+2 -3
View File
@@ -25,12 +25,11 @@ int main(int argc, char *argv[]) {
exit(1);
}
const char *filename = argv[1];
auto v = simdjson::padded_string::load(filename);
if (v.error()) {
auto[p, err] = simdjson::padded_string::load(filename);
if (err) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
const simdjson::padded_string& p = v.value_unsafe();
if (test_baseline) {
std::wclog << "Baseline: Getline + normal parse... " << std::endl;
std::cout << "Gigabytes/second\t"
+488
View File
@@ -0,0 +1,488 @@
#include "simdjson.h"
#include <unistd.h>
#include "benchmark.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// #define RAPIDJSON_SSE2 // bad for performance
// #define RAPIDJSON_SSE42 // bad for performance
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "sajson.h"
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
using namespace simdjson;
struct stat_s {
size_t number_count;
size_t object_count;
size_t array_count;
size_t null_count;
size_t true_count;
size_t false_count;
bool valid;
};
typedef struct stat_s stat_t;
bool stat_equal(const stat_t &s1, const stat_t &s2) {
return (s1.valid == s2.valid) && (s1.number_count == s2.number_count) &&
(s1.object_count == s2.object_count) &&
(s1.array_count == s2.array_count) &&
(s1.null_count == s2.null_count) && (s1.true_count == s2.true_count) &&
(s1.false_count == s2.false_count);
}
void print_stat(const stat_t &s) {
if (!s.valid) {
printf("invalid\n");
return;
}
printf("number: %zu object: %zu array: %zu null: %zu true: %zu false: %zu\n",
s.number_count, s.object_count, s.array_count, s.null_count,
s.true_count, s.false_count);
}
really_inline void simdjson_process_atom(stat_t &s,
simdjson::dom::element element) {
if (element.is<double>()) {
s.number_count++;
} else if (element.is<bool>()) {
simdjson::error_code error;
bool v;
if (not (error = element.get(v)) && v) {
s.true_count++;
} else {
s.false_count++;
}
} else if (element.is_null()) {
s.null_count++;
}
}
void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
error_code error;
if (element.is<simdjson::dom::array>()) {
s.array_count++;
dom::array array;
if ((error = element.get(array))) {
std::cerr << error << std::endl;
abort();
}
for (auto child : array) {
if (child.is<simdjson::dom::array>() ||
child.is<simdjson::dom::object>()) {
simdjson_recurse(s, child);
} else {
simdjson_process_atom(s, child);
}
}
} else if (element.is<simdjson::dom::object>()) {
s.object_count++;
dom::object object;
if ((error = element.get(object))) {
std::cerr << error << std::endl;
abort();
}
for (auto field : object) {
if (field.value.is<simdjson::dom::array>() ||
field.value.is<simdjson::dom::object>()) {
simdjson_recurse(s, field.value);
} else {
simdjson_process_atom(s, field.value);
}
}
} else {
simdjson_process_atom(s, element);
}
}
never_inline stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
stat_t s{};
simdjson::dom::parser parser;
simdjson::dom::element doc;
auto error = parser.parse(p).get(doc);
if (error) {
s.valid = false;
return s;
}
s.valid = true;
simdjson_recurse(s, doc);
return s;
}
///
struct Stat {
size_t objectCount;
size_t arrayCount;
size_t numberCount;
size_t stringCount;
size_t trueCount;
size_t falseCount;
size_t nullCount;
size_t memberCount; // Number of members in all objects
size_t elementCount; // Number of elements in all arrays
size_t stringLength; // Number of code units in all strings
};
static void GenStatPlus(Stat &stat, const dom::element &v) {
switch (v.type()) {
case dom::element_type::ARRAY:
for (dom::element child : dom::array(v)) {
GenStatPlus(stat, child);
stat.elementCount++;
}
stat.arrayCount++;
break;
case dom::element_type::OBJECT:
for (dom::key_value_pair kv : dom::object(v)) {
GenStatPlus(stat, dom::element(kv.value));
stat.memberCount++;
stat.stringCount++;
}
stat.objectCount++;
break;
case dom::element_type::INT64:
case dom::element_type::UINT64:
case dom::element_type::DOUBLE:
stat.numberCount++;
break;
case dom::element_type::STRING: {
stat.stringCount++;
auto sv = std::string_view(v);
stat.stringLength += sv.size();
} break;
case dom::element_type::BOOL:
if (bool(v)) {
stat.trueCount++;
} else {
stat.falseCount++;
}
break;
case dom::element_type::NULL_VALUE:
++stat.nullCount;
break;
}
}
static void RapidGenStat(Stat &stat, const rapidjson::Value &v) {
switch (v.GetType()) {
case kNullType:
stat.nullCount++;
break;
case kFalseType:
stat.falseCount++;
break;
case kTrueType:
stat.trueCount++;
break;
case kObjectType:
for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd();
++m) {
stat.stringLength += m->name.GetStringLength();
RapidGenStat(stat, m->value);
}
stat.objectCount++;
stat.memberCount += (v.MemberEnd() - v.MemberBegin());
stat.stringCount += (v.MemberEnd() - v.MemberBegin()); // Key
break;
case kArrayType:
for (Value::ConstValueIterator i = v.Begin(); i != v.End(); ++i)
RapidGenStat(stat, *i);
stat.arrayCount++;
stat.elementCount += v.Size();
break;
case kStringType:
stat.stringCount++;
stat.stringLength += v.GetStringLength();
break;
case kNumberType:
stat.numberCount++;
break;
}
}
never_inline Stat rapidjson_compute_stats_ref(const rapidjson::Value &doc) {
Stat s{};
RapidGenStat(s, doc);
return s;
}
never_inline Stat
simdjson_compute_stats_refplus(const simdjson::dom::element &doc) {
Stat s{};
GenStatPlus(s, doc);
return s;
}
// see
// https://github.com/miloyip/nativejson-benchmark/blob/master/src/tests/sajsontest.cpp
void sajson_traverse(stat_t &stats, const sajson::value &node) {
using namespace sajson;
switch (node.get_type()) {
case TYPE_NULL:
stats.null_count++;
break;
case TYPE_FALSE:
stats.false_count++;
break;
case TYPE_TRUE:
stats.true_count++;
break;
case TYPE_ARRAY: {
stats.array_count++;
auto length = node.get_length();
for (size_t i = 0; i < length; ++i) {
sajson_traverse(stats, node.get_array_element(i));
}
break;
}
case TYPE_OBJECT: {
stats.object_count++;
auto length = node.get_length();
for (auto i = 0u; i < length; ++i) {
sajson_traverse(stats, node.get_object_value(i));
}
break;
}
case TYPE_STRING:
// skip
break;
case TYPE_DOUBLE:
case TYPE_INTEGER:
stats.number_count++; // node.get_number_value();
break;
default:
assert(false && "unknown node type");
}
}
never_inline stat_t sasjon_compute_stats(const simdjson::padded_string &p) {
stat_t answer{};
char *buffer = (char *)malloc(p.size());
if (buffer == nullptr) {
return answer;
}
memcpy(buffer, p.data(), p.size());
auto d = sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer));
answer.valid = d.is_valid();
if (!answer.valid) {
free(buffer);
return answer;
}
answer.number_count = 0;
answer.object_count = 0;
answer.array_count = 0;
answer.null_count = 0;
answer.true_count = 0;
answer.false_count = 0;
sajson_traverse(answer, d.get_root());
free(buffer);
return answer;
}
void rapid_traverse(stat_t &stats, const rapidjson::Value &v) {
switch (v.GetType()) {
case kNullType:
stats.null_count++;
break;
case kFalseType:
stats.false_count++;
break;
case kTrueType:
stats.true_count++;
break;
case kObjectType:
for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd();
++m) {
rapid_traverse(stats, m->value);
}
stats.object_count++;
break;
case kArrayType:
for (Value::ConstValueIterator i = v.Begin(); i != v.End();
++i) { // v.Size();
rapid_traverse(stats, *i);
}
stats.array_count++;
break;
case kStringType:
break;
case kNumberType:
stats.number_count++;
break;
}
}
never_inline stat_t rapid_compute_stats(const simdjson::padded_string &p) {
stat_t answer{};
char *buffer = (char *)malloc(p.size() + 1);
if (buffer == nullptr) {
return answer;
}
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
answer.valid = !d.HasParseError();
if (!answer.valid) {
free(buffer);
return answer;
}
answer.number_count = 0;
answer.object_count = 0;
answer.array_count = 0;
answer.null_count = 0;
answer.true_count = 0;
answer.false_count = 0;
rapid_traverse(answer, d);
free(buffer);
return answer;
}
never_inline stat_t
rapid_accurate_compute_stats(const simdjson::padded_string &p) {
stat_t answer{};
char *buffer = (char *)malloc(p.size() + 1);
if (buffer == nullptr) {
return answer;
}
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag | kParseFullPrecisionFlag>(buffer);
answer.valid = !d.HasParseError();
if (!answer.valid) {
free(buffer);
return answer;
}
answer.number_count = 0;
answer.object_count = 0;
answer.array_count = 0;
answer.null_count = 0;
answer.true_count = 0;
answer.false_count = 0;
rapid_traverse(answer, d);
free(buffer);
return answer;
}
int main(int argc, char *argv[]) {
bool verbose = false;
bool just_data = false;
int c;
while ((c = getopt(argc, argv, "vt")) != -1)
switch (c) {
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr
<< "Using different parsers, we compute the content statistics of "
"JSON documents."
<< std::endl;
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
}
simdjson::padded_string p;
auto error = simdjson::padded_string::load(filename).get(p);
if (error) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
if (verbose) {
std::cout << "Input has ";
if (p.size() > 1000 * 1000)
std::cout << p.size() / (1000 * 1000) << " MB ";
else if (p.size() > 1000)
std::cout << p.size() / 1000 << " KB ";
else
std::cout << p.size() << " B ";
std::cout << std::endl;
}
stat_t s1 = simdjson_compute_stats(p);
if (verbose) {
printf("simdjson: ");
print_stat(s1);
}
stat_t s2 = rapid_compute_stats(p);
if (verbose) {
printf("rapid: ");
print_stat(s2);
}
stat_t s2a = rapid_accurate_compute_stats(p);
if (verbose) {
printf("rapid full: ");
print_stat(s2a);
}
stat_t s3 = sasjon_compute_stats(p);
if (verbose) {
printf("sasjon: ");
print_stat(s3);
}
assert(stat_equal(s1, s2));
assert(stat_equal(s1, s3));
int repeat = 50;
size_t volume = p.size();
if (just_data) {
printf("name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
}
BEST_TIME("simdjson ", simdjson_compute_stats(p).valid, true, ,
repeat, volume, !just_data);
BEST_TIME("RapidJSON ", rapid_compute_stats(p).valid, true, ,
repeat, volume, !just_data);
BEST_TIME("RapidJSON (precise) ", rapid_accurate_compute_stats(p).valid, true,
, repeat, volume, !just_data);
BEST_TIME("sasjon ", sasjon_compute_stats(p).valid, true, ,
repeat, volume, !just_data);
if (!just_data) {
printf("API traversal tests\n");
printf("Based on https://github.com/miloyip/nativejson-benchmark\n");
simdjson::dom::parser parser;
simdjson::dom::element doc;
auto error = parser.parse(p).get(doc);
if (error) {
std::cerr << error << std::endl;
}
size_t refval = simdjson_compute_stats_refplus(doc).objectCount;
BEST_TIME("simdjson ",
simdjson_compute_stats_refplus(doc).objectCount, refval, , repeat,
volume, !just_data);
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
rapidjson::Document d;
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
BEST_TIME("rapid ", rapidjson_compute_stats_ref(d).objectCount,
refval, , repeat, volume, !just_data);
free(buffer);
}
}
+361
View File
@@ -0,0 +1,361 @@
#include "simdjson.h"
#include <unistd.h>
#ifndef _MSC_VER
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif //__linux__
#endif // _MSC_VER
#include <memory>
#include "benchmark.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// #define RAPIDJSON_SSE2 // bad for performance
// #define RAPIDJSON_SSE42 // bad for performance
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "sajson.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#ifdef ALLPARSER
#include "fastjson.cpp"
#include "fastjson_dom.cpp"
#include "gason.cpp"
#include "json11.cpp"
extern "C" {
#include "cJSON.c"
#include "cJSON.h"
#include "jsmn.c"
#include "jsmn.h"
#include "ujdecode.h"
#include "ultrajsondec.c"
}
#include "jsoncpp.cpp"
#include "json/json.h"
#endif
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
#ifdef ALLPARSER
// fastjson has a tricky interface
void on_json_error(void *, UNUSED const fastjson::ErrorContext &ec) {
// std::cerr<<"ERROR: "<<ec.mesg<<std::endl;
}
bool fastjson_parse(const char *input) {
fastjson::Token token;
fastjson::dom::Chunk chunk;
return fastjson::dom::parse_string(input, &token, &chunk, 0, &on_json_error,
NULL);
}
// end of fastjson stuff
#endif
never_inline size_t sum_line_lengths(std::stringstream & is) {
std::string line;
size_t sumofalllinelengths{0};
while(std::getline(is, line)) {
sumofalllinelengths += line.size();
}
return sumofalllinelengths;
}
inline void reset_stream(std::stringstream & is) {
is.clear();
is.seekg(0,std::ios::beg);
}
bool bench(const char *filename, bool verbose, bool just_data, double repeat_multiplier) {
simdjson::padded_string p;
auto error = simdjson::padded_string::load(filename).get(p);
if (error) {
std::cerr << "Could not load the file " << filename << ": " << error << std::endl;
return false;
}
int repeat = static_cast<int>((50000000 * repeat_multiplier) / static_cast<double>(p.size()));
if (repeat < 10) { repeat = 10; }
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
if (verbose) {
std::cout << "Input " << filename << " has ";
if (p.size() > 1000 * 1000)
std::cout << p.size() / (1000 * 1000) << " MB";
else if (p.size() > 1000)
std::cout << p.size() / 1000 << " KB";
else
std::cout << p.size() << " B";
std::cout << ": will run " << repeat << " iterations." << std::endl;
}
size_t volume = p.size();
if (just_data) {
printf("%-42s %20s %20s %20s %20s \n", "name", "cycles_per_byte",
"cycles_per_byte_err", "gb_per_s", "gb_per_s_err");
}
if (!just_data) {
const std::string inputcopy(p.data(), p.data()+p.size());
std::stringstream is;
is.str(inputcopy);
const size_t lc = sum_line_lengths(is);
BEST_TIME("getline ",sum_line_lengths(is) , lc, reset_stream(is),
repeat, volume, !just_data);
}
if (!just_data) {
auto parse_dynamic=[](auto& str){
simdjson::dom::parser parser;
return parser.parse(str).error();
};
BEST_TIME("simdjson (dynamic mem) ", parse_dynamic(p), simdjson::SUCCESS,
, repeat, volume, !just_data);
}
// (static alloc)
simdjson::dom::parser parser;
BEST_TIME("simdjson ", parser.parse(p).error(), simdjson::SUCCESS, , repeat, volume,
!just_data);
rapidjson::Document d;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
#ifndef ALLPARSER
if (!just_data)
#endif
{
memcpy(buffer, p.data(), p.size());
BEST_TIME("RapidJSON ",
d.Parse<kParseValidateEncodingFlag>((const char *)buffer)
.HasParseError(),
false, , repeat, volume,
!just_data);
}
#ifndef ALLPARSER
if (!just_data)
#endif
{
memcpy(buffer, p.data(), p.size());
BEST_TIME("RapidJSON (accurate number parsing) ",
d.Parse<kParseValidateEncodingFlag|kParseFullPrecisionFlag>((const char *)buffer)
.HasParseError(),
false, , repeat, volume,
!just_data);
}
BEST_TIME("RapidJSON (insitu)",
d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError(),
false,
memcpy(buffer, p.data(), p.size()) && (buffer[p.size()] = '\0'),
repeat, volume, !just_data);
BEST_TIME("RapidJSON (insitu, accurate number parsing)",
d.ParseInsitu<kParseValidateEncodingFlag|kParseFullPrecisionFlag>(buffer).HasParseError(),
false,
memcpy(buffer, p.data(), p.size()) && (buffer[p.size()] = '\0'),
repeat, volume, !just_data);
#ifndef ALLPARSER
if (!just_data)
#endif
BEST_TIME("sajson (dynamic mem)",
sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
size_t ast_buffer_size = p.size();
size_t *ast_buffer = (size_t *)malloc(ast_buffer_size * sizeof(size_t));
// (static alloc, insitu)
BEST_TIME(
"sajson",
sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
memcpy(buffer, p.data(), p.size());
size_t expected = json::parse(p.data(), p.data() + p.size()).size();
BEST_TIME("nlohmann-json", json::parse(buffer, buffer + p.size()).size(),
expected, , repeat, volume,
!just_data);
#ifdef ALLPARSER
std::string json11err;
BEST_TIME("dropbox (json11) ",
((json11::Json::parse(buffer, json11err).is_null()) ||
(!json11err.empty())),
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("fastjson ", fastjson_parse(buffer), true,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
JsonValue value;
JsonAllocator allocator;
char *endptr;
BEST_TIME("gason ", jsonParse(buffer, &endptr, &value, allocator),
JSON_OK, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
void *state;
BEST_TIME("ultrajson ",
(UJDecode(buffer, p.size(), NULL, &state) == NULL), false,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
{
std::unique_ptr<jsmntok_t[]> tokens =
std::make_unique<jsmntok_t[]>(p.size());
jsmn_parser jparser;
jsmn_init(&jparser);
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
BEST_TIME(
"jsmn ",
(jsmn_parse(&jparser, buffer, p.size(), tokens.get(), static_cast<unsigned int>(p.size())) > 0),
true, jsmn_init(&jparser), repeat, volume, !just_data);
}
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
cJSON *tree = cJSON_Parse(buffer);
BEST_TIME("cJSON ", ((tree = cJSON_Parse(buffer)) != NULL), true,
cJSON_Delete(tree), repeat, volume, !just_data);
cJSON_Delete(tree);
Json::CharReaderBuilder b;
Json::CharReader *json_cpp_reader = b.newCharReader();
Json::Value root;
Json::String errs;
BEST_TIME("jsoncpp ",
json_cpp_reader->parse(buffer, buffer + volume, &root, &errs), true,
, repeat, volume, !just_data);
delete json_cpp_reader;
#endif
if (!just_data)
BEST_TIME("memcpy ",
(memcpy(buffer, p.data(), p.size()) == buffer), true, , repeat,
volume, !just_data);
#ifdef __linux__
if (!just_data) {
printf("\n \n <doing additional analysis with performance counters (Linux "
"only)>\n");
std::vector<int> evts;
evts.push_back(PERF_COUNT_HW_CPU_CYCLES);
evts.push_back(PERF_COUNT_HW_INSTRUCTIONS);
evts.push_back(PERF_COUNT_HW_BRANCH_MISSES);
evts.push_back(PERF_COUNT_HW_CACHE_REFERENCES);
evts.push_back(PERF_COUNT_HW_CACHE_MISSES);
LinuxEvents<PERF_TYPE_HARDWARE> unified(evts);
std::vector<unsigned long long> results;
std::vector<unsigned long long> stats;
results.resize(evts.size());
stats.resize(evts.size());
std::fill(stats.begin(), stats.end(), 0); // unnecessary
for (decltype(repeat) i = 0; i < repeat; i++) {
unified.start();
auto parse_error = parser.parse(p).error();
if (parse_error)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("simdjson : cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
static_cast<double>(stats[0]) / static_cast<double>(repeat), static_cast<double>(stats[1]) / static_cast<double>(repeat),
static_cast<double>(stats[2]) / static_cast<double>(repeat), static_cast<double>(stats[3]) / static_cast<double>(repeat),
static_cast<double>(stats[4]) / static_cast<double>(repeat), static_cast<double>(volume) * static_cast<double>(repeat) / static_cast<double>(stats[2]),
static_cast<double>(stats[1]) / static_cast<double>(stats[0]), static_cast<double>(stats[1]) / (static_cast<double>(volume) * static_cast<double>(repeat)));
std::fill(stats.begin(), stats.end(), 0);
for (decltype(repeat) i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
unified.start();
if (d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError() !=
false)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("RapidJSON: cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
static_cast<double>(stats[0]) / static_cast<double>(repeat), static_cast<double>(stats[1]) / static_cast<double>(repeat),
static_cast<double>(stats[2]) / static_cast<double>(repeat), static_cast<double>(stats[3]) / static_cast<double>(repeat),
static_cast<double>(stats[4]) / static_cast<double>(repeat), static_cast<double>(volume) * static_cast<double>(repeat) / static_cast<double>(stats[2]),
static_cast<double>(stats[1]) / static_cast<double>(stats[0]), static_cast<double>(stats[1]) / (static_cast<double>(volume) * static_cast<double>(repeat)));
std::fill(stats.begin(), stats.end(), 0); // unnecessary
for (decltype(repeat) i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
unified.start();
if (sajson::parse(sajson::bounded_allocation(ast_buffer, ast_buffer_size),
sajson::mutable_string_view(p.size(), buffer))
.is_valid() != true)
printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("sajson : cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
static_cast<double>(stats[0]) / static_cast<double>(repeat), static_cast<double>(stats[1]) / static_cast<double>(repeat),
static_cast<double>(stats[2]) / static_cast<double>(repeat), static_cast<double>(stats[3]) / static_cast<double>(repeat),
static_cast<double>(stats[4]) / static_cast<double>(repeat), static_cast<double>(volume) * static_cast<double>(repeat) / static_cast<double>(stats[2]),
static_cast<double>(stats[1]) / static_cast<double>(stats[0]), static_cast<double>(stats[1]) / (static_cast<double>(volume) * static_cast<double>(repeat)));
}
#endif // __linux__
free(ast_buffer);
free(buffer);
return true;
}
int main(int argc, char *argv[]) {
bool verbose = false;
bool just_data = false;
double repeat_multiplier = 1;
int c;
while ((c = getopt(argc, argv, "r:vt")) != -1)
switch (c) {
case 'r':
repeat_multiplier = atof(optarg);
break;
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
std::cerr << "The '-t' flag outputs a table." << std::endl;
std::cerr << "The '-r <N>' flag sets the repeat multiplier: set it above 1 to do more iterations, and below 1 to do fewer." << std::endl;
exit(1);
}
int result = EXIT_SUCCESS;
for (int fileind = optind; fileind < argc; fileind++) {
if (!bench(argv[fileind], verbose, just_data, repeat_multiplier)) { result = EXIT_FAILURE; }
printf("\n\n");
}
return result;
}
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
#include "partial_tweets.h"
namespace partial_tweets {
struct nlohmann_json {
using StringType=std::string;
simdjson_really_inline uint64_t nullable_int(nlohmann::json value) {
if (value.is_null()) { return 0; }
return value;
}
bool run(simdjson::padded_string &json, std::vector<tweet<std::string>> &result) {
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
for (auto tweet : root["statuses"]) {
auto user = tweet["user"];
result.emplace_back(partial_tweets::tweet<std::string>{
tweet["created_at"],
tweet["id"],
tweet["text"],
nullable_int(tweet["in_reply_to_status_id"]),
{ user["id"], user["screen_name"] },
tweet["retweet_count"],
tweet["favorite_count"]
});
}
return true;
}
};
BENCHMARK_TEMPLATE(partial_tweets, nlohmann_json)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class OnDemand {
public:
OnDemand() {
if(!displayed_implementation) {
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
displayed_implementation = true;
}
}
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
ondemand::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline uint64_t nullable_int(ondemand::value value) {
if (value.is_null()) { return 0; }
return value;
}
simdjson_really_inline twitter_user read_user(ondemand::object user) {
return { user.find_field("id"), user.find_field("screen_name") };
}
static inline bool displayed_implementation = false;
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
tweets.clear();
// Walk the document, parsing the tweets as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc.find_field("statuses")) {
tweets.emplace_back(partial_tweets::tweet{
tweet.find_field("created_at"),
tweet.find_field("id"),
tweet.find_field("text"),
nullable_int(tweet.find_field("in_reply_to_status_id")),
read_user(tweet.find_field("user")),
tweet.find_field("retweet_count"),
tweet.find_field("favorite_count")
});
}
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, OnDemand);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
-46
View File
@@ -1,46 +0,0 @@
#pragma once
#include "json_benchmark/file_runner.h"
#include "tweet.h"
#include <vector>
namespace partial_tweets {
using namespace json_benchmark;
template<typename I>
struct runner : public file_runner<I> {
std::vector<tweet<typename I::StringType>> result{};
bool setup(benchmark::State &state) {
return this->load_json(state, TWITTER_JSON);
}
bool before_run(benchmark::State &state) {
if (!file_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
size_t items_per_iteration() {
return result.size();
}
};
struct simdjson_dom;
template<typename I> simdjson_really_inline static void partial_tweets(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace partial_tweets
-80
View File
@@ -1,80 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
#include "partial_tweets.h"
namespace partial_tweets {
using namespace rapidjson;
struct rapidjson_base {
using StringType=std::string_view;
Document doc{};
simdjson_really_inline std::string_view get_string_view(Value &object, std::string_view key) {
// TODO use version that supports passing string length?
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing object field"; }
if (!field->value.IsString()) { throw "Field is not a string"; }
return { field->value.GetString(), field->value.GetStringLength() };
}
simdjson_really_inline uint64_t get_uint64(Value &object, std::string_view key) {
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing object field"; }
if (!field->value.IsUint64()) { throw "Field is not uint64"; }
return field->value.GetUint64();
}
simdjson_really_inline uint64_t get_nullable_uint64(Value &object, std::string_view key) {
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing nullable uint64 field"; }
if (field->value.IsNull()) { return 0; }
if (!field->value.IsUint64()) { throw "Field is not nullable uint64"; }
return field->value.GetUint64();
}
simdjson_really_inline partial_tweets::twitter_user<std::string_view> get_user(Value &object, std::string_view key) {
auto field = object.FindMember(key.data());
if (field == object.MemberEnd()) { throw "Missing user field"; }
if (!field->value.IsObject()) { throw "User field is not an object"; }
return { get_uint64(field->value, "id"), get_string_view(field->value, "screen_name") };
}
bool run(Document &root, std::vector<tweet<std::string_view>> &result) {
if (root.HasParseError() || !root.IsObject()) { return false; }
auto statuses = root.FindMember("statuses");
if (statuses == root.MemberEnd() || !statuses->value.IsArray()) { return false; }
for (auto &tweet : statuses->value.GetArray()) {
if (!tweet.IsObject()) { return false; }
result.emplace_back(partial_tweets::tweet<std::string_view>{
get_string_view(tweet, "created_at"),
get_uint64 (tweet, "id"),
get_string_view(tweet, "text"),
get_nullable_uint64 (tweet, "in_reply_to_status_id"),
get_user (tweet, "user"),
get_uint64 (tweet, "retweet_count"),
get_uint64 (tweet, "favorite_count")
});
}
return true;
}
};
struct rapidjson : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(partial_tweets, rapidjson)->UseManualTime();
struct rapidjson_insitu : rapidjson_base {
bool run(simdjson::padded_string &json, std::vector<tweet<std::string_view>> &result) {
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag>(json.data()), result);
}
};
BENCHMARK_TEMPLATE(partial_tweets, rapidjson_insitu)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_RAPIDJSON

Some files were not shown because too many files have changed in this diff Show More