Compare commits

..

5 Commits

Author SHA1 Message Date
Daniel Lemire 17b03de3cd Patch release 0.7.1 2020-12-14 10:44:01 -05:00
Daniel Lemire 15297c12d1 Updating according to John's comments. 2020-12-13 20:01:10 -05:00
Daniel Lemire 2a84f35e3f Definining SIMDJSON_CAN_ALWAYS_RUN_PPC64 and SIMDJSON_CAN_ALWAYS_RUN_ARM64 2020-12-13 19:38:38 -05:00
Daniel Lemire 7939ad5307 Fixing comment. 2020-12-13 18:58:28 -05:00
Daniel Lemire c67add57d8 By default, it seems that optimized kernels are disabled. 2020-12-13 18:50:14 -05:00
299 changed files with 30286 additions and 19813 deletions
+9 -9
View File
@@ -13,25 +13,25 @@ environment:
matrix:
- job_name: VS2019
CMAKE_ARGS: -A %Platform%
CMAKE_ARGS: -A %Platform%
- 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
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CTEST_ARGS: -E "checkperf|ondemand_basictests"
- 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 -DSIMDJSON_EXCEPTIONS=OFF
CTEST_ARGS: -E "checkperf|ondemand_basictests"
- 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
+10 -31
View File
@@ -103,14 +103,7 @@ commands:
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:
@@ -119,9 +112,9 @@ commands:
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 &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
@@ -151,16 +144,6 @@ jobs:
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
@@ -191,22 +174,22 @@ jobs:
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: --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 }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
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 }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
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_THREADS=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
@@ -262,12 +245,12 @@ jobs:
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
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: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
workflows:
@@ -309,8 +292,4 @@ workflows:
# testing "just the library"
- justlib-gcc10
# testing asserts
- assert-gcc10
- assert-clang10
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows
+1 -1
View File
@@ -23,4 +23,4 @@ task:
- make
test_script:
- cd build
- ctest --output-on-failure -LE explicitonly
- ctest --output-on-failure -E checkperf
+19 -19
View File
@@ -9,7 +9,7 @@ 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
@@ -30,7 +30,7 @@ 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
@@ -51,7 +51,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
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
@@ -78,7 +78,7 @@ steps:
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
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
@@ -101,7 +101,7 @@ 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
@@ -124,7 +124,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
@@ -143,7 +143,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
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
@@ -170,7 +170,7 @@ steps:
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
@@ -194,7 +194,7 @@ steps:
CXX: clang++-11
CMAKE_FLAGS: -GNinja
BUILD_FLAGS:
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CXXFLAGS: -std=c++20 -stdlib=libc++
commands:
- mkdir build
@@ -214,7 +214,7 @@ steps:
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
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
@@ -239,7 +239,7 @@ steps:
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
@@ -261,7 +261,7 @@ 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
@@ -283,7 +283,7 @@ steps:
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
@@ -303,7 +303,7 @@ steps:
environment:
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CC: gcc
CXX: g++
commands:
@@ -331,7 +331,7 @@ steps:
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
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
@@ -357,7 +357,7 @@ 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 -E checkperf
CXXFLAGS: -stdlib=libc++
commands:
- mkdir build
@@ -378,7 +378,7 @@ steps:
CXX: clang++-9
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:
- mkdir build
@@ -399,7 +399,7 @@ steps:
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:
- mkdir build
@@ -419,7 +419,7 @@ 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
+1 -4
View File
@@ -10,9 +10,6 @@ on:
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
@@ -37,4 +34,4 @@ jobs:
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -LE explicitonly"
./alpine.sh bash -c "cd build_for_alpine && ctest -E checkperf"
+1 -4
View File
@@ -11,10 +11,7 @@ on:
- 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
+2 -5
View File
@@ -15,9 +15,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
@@ -64,5 +61,5 @@ 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 parse_many_test jsoncheck basictests ondemand_basictests numberparsingcheck stringparsingcheck errortests integer_tests pointercheck --verbose
ctest -R "(parse_many_test|jsoncheck|basictests|stringparsingcheck|numberparsingcheck|errortests|integer_tests|pointercheck)" --output-on-failure
+4 -7
View File
@@ -15,9 +15,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
@@ -64,11 +61,11 @@ 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
cmake --build . --target parse_many_test jsoncheck basictests ondemand_basictests numberparsingcheck stringparsingcheck errortests integer_tests pointercheck --verbose
ctest -R "(parse_many_test|jsoncheck|basictests|stringparsingcheck|numberparsingcheck|errortests|integer_tests|pointercheck)" --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 parse_many_test jsoncheck basictests ondemand_basictests numberparsingcheck stringparsingcheck errortests integer_tests pointercheck --verbose
ctest -R "(parse_many_test|jsoncheck|basictests|stringparsingcheck|numberparsingcheck|errortests|integer_tests|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
+1 -4
View File
@@ -10,9 +10,6 @@ on:
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:
@@ -54,4 +51,4 @@ jobs:
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
ctest -j4 --output-on-failure -E checkperf
-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"
-3
View File
@@ -10,9 +10,6 @@ on:
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
@@ -10,9 +10,6 @@ on:
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
+1 -4
View File
@@ -10,9 +10,6 @@ on:
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
@@ -26,6 +23,6 @@ jobs:
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 &&
ctest -j --output-on-failure -E checkperf &&
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
+2 -5
View File
@@ -10,9 +10,6 @@ on:
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
@@ -24,6 +21,6 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
ctest --output-on-failure -R checkperf
-34
View File
@@ -1,34 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) without exceptions
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_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -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 &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
-34
View File
@@ -1,34 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9) without threads
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_ENABLE_THREADS=OFF -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 &&
mkdir testfindpackage &&
cd testfindpackage &&
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
@@ -10,9 +10,6 @@ on:
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
+3 -8
View File
@@ -10,9 +10,6 @@ on:
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
@@ -26,8 +23,6 @@ jobs:
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 &&
cmake --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 &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
ctest -j --output-on-failure -E checkperf &&
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
-46
View File
@@ -1,46 +0,0 @@
name: VS15-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-vs15
runs-on: windows-2016
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 15 2017, arch: Win32, static: ON}
- {gen: Visual Studio 15 2017, arch: Win32, static: OFF}
- {gen: Visual Studio 15 2017, arch: x64, static: ON}
- {gen: Visual Studio 15 2017, arch: x64, static: OFF}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
+21 -31
View File
@@ -10,37 +10,27 @@ on:
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
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 16 2019, arch: Win32, static: ON}
- {gen: Visual Studio 16 2019, arch: Win32, static: OFF}
- {gen: Visual Studio 16 2019, arch: x64, static: ON}
- {gen: Visual Studio 16 2019, arch: x64, static: OFF}
steps:
- name: checkout
uses: actions/checkout@v2
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=${{matrix.static}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
- 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 -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+8 -10
View File
@@ -10,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-vs16
runs-on: windows-latest
steps:
@@ -21,17 +18,18 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16 Clang'
uses: lukka/run-cmake@v3
- 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
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
run: ctest -C Release -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+6 -21
View File
@@ -10,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-vs16
runs-on: windows-latest
steps:
@@ -27,25 +24,13 @@ jobs:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -LE explicitonly --output-on-failure
run: ctest -C Release -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
- name: 'Install with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildWithCMakeArgs: '--target install'
- name: 'Test Installation with CMake'
uses: lukka/run-cmake@v3
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/tests/installation_tests/find/CMakeLists.txt'
cmakeBuildType: Release
buildWithCMake: true
buildDirectory: '${{ github.workspace }}/tests/installation_tests/find/buildDirectory'
cmakeAppendedArgs: -G Ninja
buildWithCMakeArgs: '--config Release --verbose'
-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@v3
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 -1
View File
@@ -176,7 +176,7 @@ install:
- if [[ "${STATIC}" == "on" ]]; then
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_BUILD_STATIC=ON";
fi
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
- export CTEST_FLAGS="-j4 --output-on-failure -E checkperf"
script:
- mkdir build
+5 -22
View File
@@ -1,18 +1,16 @@
cmake_minimum_required(VERSION 3.13)
# CMP0025: Compiler id for Apple Clang is now AppleClang.
# https://cmake.org/cmake/help/v3.17/policy/CMP0025.html
cmake_policy(SET CMP0025 NEW)
project(simdjson
DESCRIPTION "Parsing gigabytes of JSON per second"
LANGUAGES CXX C
)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 9)
set(PROJECT_VERSION_MINOR 7)
set(PROJECT_VERSION_PATCH 1)
set(SIMDJSON_SEMANTIC_VERSION "0.9.1" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "8.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "8" CACHE STRING "simdjson library soversion")
set(SIMDJSON_SEMANTIC_VERSION "0.7.1" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "5.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "5" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
include(GNUInstallDirs)
@@ -45,21 +43,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
#
-2
View File
@@ -70,8 +70,6 @@ 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.
+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.9.1"
PROJECT_NUMBER = "0.7.1"
# 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
+50 -29
View File
@@ -3,7 +3,7 @@
[![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)
![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.9.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.7.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
@@ -11,11 +11,12 @@ 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 4x faster than RapidJSON and 25x faster than JSON for Modern C++.
to parse JSON 2.5x faster than RapidJSON and 25x faster than JSON for Modern C++.
* **Fast:** Over 4x faster than commonly used production-grade JSON parsers.
* **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).
* **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.
@@ -27,7 +28,6 @@ Table of Contents
-----------------
* [Quick Start](#quick-start)
* [On Demand](#on-demand)
* [Documentation](#documentation)
* [Performance results](#performance-results)
* [Real-world usage](#real-world-usage)
@@ -40,29 +40,23 @@ 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 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).
```
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
```
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;
}
```c++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
}
```
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
@@ -79,31 +73,59 @@ 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.9.0/annotated.html) contains the automatically generated API documentation.
* [API](https://simdjson.org/api/0.7.0/annotated.html) contains the automatically generated API documentation.
Performance results
-------------------
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org). To our knowledge, simdjson is the first fully-validating JSON parser
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org) and
fifty percent less than sajson. To our knowledge, simdjson is the first fully-validating JSON parser
to run at [gigabytes per second](https://en.wikipedia.org/wiki/Gigabyte) (GB/s) on commodity processors. It can parse millions of JSON documents per second on a single core.
The following figure represents parsing speed in GB/s for parsing various files
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 10 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries on benchmarks that load and process the data.
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 9 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries.
The simdjson library offers full unicode ([UTF-8](https://en.wikipedia.org/wiki/UTF-8)) validation and exact
number parsing.
number parsing. The RapidJSON library is tested in two modes: fast and
exact number parsing. The sajson library offers fast (but not exact)
number parsing and partial unicode validation. In this data set, the file
sizes range from 65KB (github_events) all the way to 3.3GB (gsoc-2018).
Many files are mostly made of numbers: canada, mesh.pretty, mesh, random
and numbers: in such instances, we see lower JSON parsing speeds due to the
high cost of number parsing. The simdjson library uses exact number parsing which
is particular taxing.
<img src="doc/gbps.png" width="90%">
On a Skylake processor, the parsing speeds (in GB/s) of various processors on the twitter.json file are as follows, using again GNU GCC 9.1 (with the -O3 flag). The popular JSON for Modern C++ library is particularly slow: it obviously trades parsing speed for other desirable features.
| parser | GB/s |
| ------------------------------------- | ---- |
| simdjson | 2.5 |
| RapidJSON UTF8-validation | 0.29 |
| RapidJSON UTF8-valid., exact numbers | 0.28 |
| RapidJSON insitu, UTF8-validation | 0.41 |
| RapidJSON insitu, UTF8-valid., exact | 0.39 |
| sajson (insitu, dynamic) | 0.62 |
| sajson (insitu, static) | 0.88 |
| dropbox | 0.13 |
| fastjson | 0.27 |
| gason | 0.59 |
| ultrajson | 0.34 |
| jsmn | 0.25 |
| cJSON | 0.31 |
| JSON for Modern C++ (nlohmann/json) | 0.11 |
<img src="doc/rome.png" width="60%">
The simdjson library offers high speed whether it processes tiny files (e.g., 300 bytes)
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="60%">
<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).
@@ -126,7 +148,6 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [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.
- [cysimdjson](https://github.com/TeskaLabs/cysimdjson): high-speed 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).
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
+26 -16
View File
@@ -1,7 +1,13 @@
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
link_libraries(simdjson simdjson-flags)
if (TARGET benchmark::benchmark)
add_executable(bench_sax bench_sax.cpp)
target_link_libraries(bench_sax PRIVATE simdjson-internal-flags simdjson-include-source benchmark::benchmark)
endif (TARGET benchmark::benchmark)
link_libraries(simdjson simdjson-flags)
add_executable(benchfeatures benchfeatures.cpp)
add_executable(get_corpus_benchmark get_corpus_benchmark.cpp)
add_executable(perfdiff perfdiff.cpp)
@@ -16,25 +22,29 @@ target_compile_definitions(parse_nonumberparsing PRIVATE SIMDJSON_SKIPNUMBERPARS
add_executable(parse_nostringparsing parse.cpp)
target_compile_definitions(parse_nostringparsing PRIVATE SIMDJSON_SKIPSTRINGPARSING)
if (TARGET competition-all)
add_executable(distinctuseridcompetition distinctuseridcompetition.cpp)
target_link_libraries(distinctuseridcompetition PRIVATE competition-core)
add_executable(minifiercompetition minifiercompetition.cpp)
target_link_libraries(minifiercompetition PRIVATE competition-core)
add_executable(parseandstatcompetition parseandstatcompetition.cpp)
target_link_libraries(parseandstatcompetition PRIVATE competition-core)
add_executable(parsingcompetition parsingcompetition.cpp)
target_link_libraries(parsingcompetition PRIVATE competition-core)
add_executable(allparsingcompetition parsingcompetition.cpp)
target_link_libraries(allparsingcompetition PRIVATE competition-all)
target_compile_definitions(allparsingcompetition PRIVATE ALLPARSER)
endif()
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()
add_executable(bench_ondemand bench_ondemand.cpp)
endif()
include(checkperf.cmake)
+6 -6
View File
@@ -503,7 +503,7 @@ static void twitter_default_profile(State& state) {
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; }
@@ -522,8 +522,8 @@ static void twitter_image_sizes(State& state) {
dom::array media;
if (not (error = tweet["entities"]["media"].get(media))) {
for (dom::object image : media) {
for (auto size : image["sizes"].get_object()) {
image_sizes.emplace(size.value["w"], size.value["h"]);
for (auto size : image["sizes"].get<dom::object>()) {
image_sizes.insert({ size.value["w"], size.value["h"] });
}
}
}
@@ -607,7 +607,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
}
@@ -645,7 +645,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 });
}
}
}
@@ -699,7 +699,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
+15 -61
View File
@@ -1,72 +1,26 @@
#include "simdjson.h"
#include <iostream>
#include <sstream>
#include <random>
#include <vector>
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 "partial_tweets/ondemand.h"
#include "partial_tweets/iter.h"
#include "partial_tweets/dom.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 "largerandom/ondemand.h"
#include "largerandom/iter.h"
#include "largerandom/dom.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 "kostya/ondemand.h"
#include "kostya/iter.h"
#include "kostya/dom.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 "distinctuserid/ondemand.h"
#include "distinctuserid/dom.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();
+14
View File
@@ -0,0 +1,14 @@
#include "simdjson.h"
#include "simdjson.cpp"
#include <iostream>
#include <sstream>
#include <random>
#include <vector>
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#include <benchmark/benchmark.h>
SIMDJSON_POP_DISABLE_WARNINGS
#include "partial_tweets/sax.h"
#include "largerandom/sax.h"
BENCHMARK_MAIN();
+6 -6
View File
@@ -315,13 +315,13 @@ struct benchmarker {
// 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());
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;
// Run it once to get hot buffers
if(hotbuffers) {
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto result = parser.parse((const uint8_t *)json.data(), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -331,7 +331,7 @@ struct benchmarker {
// Stage 1 (find structurals)
collector.start();
error = parser.implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.size(), false);
error = parser.implementation->stage1((const uint8_t *)json.data(), json.size(), false);
event_count stage1_count = collector.end();
stage1 << stage1_count;
if (error) {
@@ -367,7 +367,7 @@ struct benchmarker {
void run_loop(size_t iterations) {
dom::parser parser;
auto firstresult = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto firstresult = parser.parse((const uint8_t *)json.data(), json.size());
if (firstresult.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
}
@@ -375,7 +375,7 @@ struct benchmarker {
collector.start();
// some users want something closer to "number of documents per second"
for(size_t i = 0; i < iterations; i++) {
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
auto result = parser.parse((const uint8_t *)json.data(), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
@@ -446,7 +446,7 @@ struct benchmarker {
void print(bool tabbed_output) const {
if (tabbed_output) {
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
char* filename_copy = (char*)malloc(strlen(filename)+1);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Validated CRT_SECURE safe here
strcpy(filename_copy, filename);
+2 -2
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 (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,7 +89,7 @@ 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()
@@ -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
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include <vector>
#include <cstdint>
#include "event_counter.h"
#include "json_benchmark.h"
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());
}
//
// Interface
//
namespace distinct_user_id {
template<typename T> static void DistinctUserID(benchmark::State &state);
} // namespace
//
// Implementation
//
#include "dom.h"
namespace distinct_user_id {
using namespace simdjson;
template<typename T> static void DistinctUserID(benchmark::State &state) {
//
// Load the JSON file
//
constexpr const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
error_code error;
padded_string json;
if ((error = padded_string::load(TWITTER_JSON).get(json))) {
std::cerr << error << std::endl;
state.SkipWithError("error loading");
return;
}
JsonBenchmark<T, Dom>(state, json);
}
} // namespace distinct_user_id
+89
View File
@@ -0,0 +1,89 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinctuserid.h"
namespace distinct_user_id {
using namespace simdjson;
simdjson_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);
}
}
}
simdjson_really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
simdjson_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);
}
}
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<int64_t> &Result() { return ids; }
simdjson_really_inline size_t ItemCount() { return ids.size(); }
private:
dom::parser parser{};
std::vector<int64_t> ids{};
};
void print_vec(const std::vector<int64_t> &v) {
for (auto i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
}
simdjson_really_inline bool Dom::Run(const padded_string &json) {
ids.clear();
dom::element doc = parser.parse(json);
simdjson_recurse(ids, doc);
remove_duplicates(ids);
return true;
}
BENCHMARK_TEMPLATE(DistinctUserID, Dom);
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinctuserid.h"
namespace distinct_user_id {
using namespace simdjson;
using namespace simdjson::builtin;
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<int64_t> &Result() { return ids; }
simdjson_really_inline size_t ItemCount() { return ids.size(); }
private:
ondemand::parser parser{};
std::vector<int64_t> ids{};
static inline bool displayed_implementation = false;
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
ids.clear();
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc["statuses"]) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not:
//
// You might think that you do not need the braces, but
// you do, otherwise you will get the wrong answer. That is
// because you can only have one active object or array
// at a time.
{
ondemand::object user = tweet["user"];
int64_t id = user["id"];
ids.push_back(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()) {
ondemand::object retweet_content = retweet;
ondemand::object reuser = retweet_content["user"];
int64_t rid = reuser["id"];
ids.push_back(rid);
}
}
remove_duplicates(ids);
return true;
}
BENCHMARK_TEMPLATE(DistinctUserID, OnDemand);
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
+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 can 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
simdjson_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);
}
}
}
simdjson_really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
simdjson_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);
}
}
simdjson_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;
}
simdjson_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;
}
simdjson_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");
}
}
simdjson_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;
}
simdjson_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;
}
simdjson_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;
}
}
simdjson_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;
}
simdjson_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;
}
simdjson_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);
}
-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
+1 -1
View File
@@ -9,7 +9,7 @@ simdjson_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;
+80
View File
@@ -0,0 +1,80 @@
#pragma once
template<typename B, typename R> static void JsonBenchmark(benchmark::State &state, const simdjson::padded_string &json) {
event_collector collector(true);
event_aggregate events;
// Warmup and equality check (make sure the data is right!)
B bench;
if (!bench.Run(json)) { state.SkipWithError("warmup tweet reading failed"); return; }
{
R reference;
if (!reference.Run(json)) { state.SkipWithError("reference tweet reading failed"); return; }
if (bench.Result() != reference.Result()) { state.SkipWithError("results are not the same"); return; }
}
// Run the benchmark
for (simdjson_unused auto _ : state) {
collector.start();
if (!bench.Run(json)) { state.SkipWithError("tweet reading failed"); return; }
events << collector.end();
}
state.SetBytesProcessed(json.size() * state.iterations());
state.SetItemsProcessed(bench.ItemCount() * state.iterations());
state.counters["best_bytes_per_sec"] = benchmark::Counter(double(json.size()) / events.best.elapsed_sec());
state.counters["best_items_per_sec"] = benchmark::Counter(double(bench.ItemCount()) / events.best.elapsed_sec());
state.counters["docs_per_sec"] = benchmark::Counter(1.0, benchmark::Counter::kIsIterationInvariantRate);
state.counters["best_docs_per_sec"] = benchmark::Counter(1.0 / events.best.elapsed_sec());
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(json.size());
state.counters["instructions_per_cycle"] = events.instructions() / events.cycles();
state.counters["cycles_per_byte"] = events.cycles() / double(json.size());
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(json.size());
state.counters["best_instructions_per_cycle"] = events.best.instructions() / events.best.cycles();
state.counters["best_cycles_per_byte"] = events.best.cycles() / double(json.size());
state.counters["best_frequency"] = events.best.cycles() / events.best.elapsed_sec();
}
state.counters["bytes"] = benchmark::Counter(double(json.size()));
state.counters["items"] = benchmark::Counter(double(bench.ItemCount()));
// Build the label
using namespace std;
stringstream label;
label << fixed << setprecision(2);
label << "[best:";
label << " throughput=" << setw(6) << (double(json.size()) / 1000000000.0 / events.best.elapsed_sec()) << " GB/s";
label << " doc_throughput=" << setw(6) << uint64_t(1.0 / 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.ItemCount() << setw(0);
label << " avg_time=" << setw(10) << uint64_t(events.elapsed_ns()) << setw(0) << " ns";
label << "]";
state.SetLabel(label.str());
}
-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,107 +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 (stage 1): " << simdjson::active_implementation->name() << std::endl;
std::cout << "simdjson::ondemand implementation (stage 2): " << 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
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
class Dom {
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:
dom::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
container.clear();
for (auto point : parser.parse(json)["coordinates"]) {
container.emplace_back(my_point{point["x"], point["y"], point["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, Dom);
namespace sum {
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
dom::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
sum = { 0, 0, 0 };
count = 0;
for (auto coord : parser.parse(json)["coordinates"]) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, Dom);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
using namespace simdjson::builtin;
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 simdjson_result<double> first_double(ondemand::json_iterator &iter, const char *key) {
if (!iter.start_object() || ondemand::raw_json_string(iter.field_key()) != key || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
simdjson_really_inline simdjson_result<double> next_double(ondemand::json_iterator &iter, const char *key) {
if (!iter.has_next_field() || ondemand::raw_json_string(iter.field_key()) != key || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
container.clear();
using std::cerr;
using std::endl;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("coordinates")) { cerr << "find coordinates field failed" << endl; return false; }
if (iter.start_array()) {
do {
container.emplace_back(my_point{first_double(iter, "x"), next_double(iter, "y"), next_double(iter, "z")});
if (iter.skip_container()) { return false; } // Skip the rest of the coordinates object
} while (iter.has_next_element());
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, Iter);
namespace sum {
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("coordinates")) { return false; }
if (!iter.start_array()) { return false; }
do {
if (!iter.start_object() || !iter.find_field_raw("x")) { return false; }
sum.x += iter.consume_double();
if (!iter.has_next_field() || !iter.find_field_raw("y")) { return false; }
sum.y += iter.consume_double();
if (!iter.has_next_field() || !iter.find_field_raw("z")) { return false; }
sum.z += iter.consume_double();
if (iter.skip_container()) { return false; } // Skip the rest of the coordinates object
count++;
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, Iter);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+47 -38
View File
@@ -1,41 +1,18 @@
#pragma once
#include "json_benchmark/string_runner.h"
#include "json_benchmark/point.h"
#include <vector>
#include <random>
#if SIMDJSON_EXCEPTIONS
//
// Interface
//
namespace kostya {
template<typename T> static void Kostya(benchmark::State &state);
namespace sum {
template<typename T> static void KostyaSum(benchmark::State &state);
}
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();
}
};
using namespace simdjson;
static void append_coordinate(std::default_random_engine &e, std::uniform_real_distribution<> &dis, std::stringstream &myss) {
using std::endl;
@@ -72,15 +49,47 @@ static std::string build_json_array(size_t N) {
return answer;
}
static const simdjson::padded_string &get_built_json_array() {
static simdjson::padded_string json = build_json_array(524288);
static const padded_string &get_built_json_array() {
static padded_string json = build_json_array(524288);
return json;
}
struct simdjson_dom;
struct my_point {
double x;
double y;
double z;
simdjson_really_inline bool operator==(const my_point &other) const {
return x == other.x && y == other.y && z == other.z;
}
simdjson_really_inline bool operator!=(const my_point &other) const { return !(*this == other); }
};
template<typename I> simdjson_really_inline static void kostya(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
simdjson_unused static std::ostream &operator<<(std::ostream &o, const my_point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
} // namespace kostya
//
// Implementation
//
#include <vector>
#include "event_counter.h"
#include "dom.h"
#include "json_benchmark.h"
namespace kostya {
template<typename T> static void Kostya(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
namespace sum {
template<typename T> static void KostyaSum(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
}
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
-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
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
using namespace simdjson::builtin;
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();
using std::cout;
using std::endl;
auto doc = parser.iterate(json);
for (ondemand::object coord : doc["coordinates"]) {
container.emplace_back(my_point{coord["x"], coord["y"], coord["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, OnDemand);
namespace sum {
class OnDemand {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto doc = parser.iterate(json);
for (ondemand::object coord : doc["coordinates"]) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, OnDemand);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
-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
-66
View File
@@ -1,66 +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 double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // 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
-64
View File
@@ -1,64 +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 double(yyjson_get_uint(val));
case YYJSON_SUBTYPE_SINT:
return double(yyjson_get_sint(val));
case YYJSON_SUBTYPE_REAL:
return yyjson_get_real(val);
default:
SIMDJSON_UNREACHABLE();
}
SIMDJSON_UNREACHABLE();
return 0.0; // 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
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
class Dom {
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:
dom::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
container.clear();
for (auto point : parser.parse(json)) {
container.emplace_back(my_point{point["x"], point["y"], point["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, Dom);
namespace sum {
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
dom::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
sum = { 0, 0, 0 };
count = 0;
for (auto coord : parser.parse(json)) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandomSum, Dom);
} // namespace sum
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+40
View File
@@ -7,6 +7,7 @@
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin;
class Iter {
public:
@@ -47,6 +48,45 @@ simdjson_really_inline bool Iter::Run(const padded_string &json) {
BENCHMARK_TEMPLATE(LargeRandom, Iter);
namespace sum {
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_array()) { return false; }
do {
if (!iter.start_object() || iter.field_key().value() != "x" || iter.field_value()) { return false; }
sum.x += iter.consume_double();
if (!iter.has_next_field() || iter.field_key().value() != "y" || iter.field_value()) { return false; }
sum.y += iter.consume_double();
if (!iter.has_next_field() || iter.field_key().value() != "z" || iter.field_value()) { return false; }
sum.z += iter.consume_double();
if (*iter.advance() != '}') { return false; }
count++;
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(LargeRandomSum, Iter);
} // namespace sum
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
//
// Interface
//
namespace largerandom {
template<typename T> static void LargeRandom(benchmark::State &state);
namespace sum {
template<typename T> static void LargeRandomSum(benchmark::State &state);
}
using namespace simdjson;
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 padded_string &get_built_json_array() {
static padded_string json = build_json_array(1000000);
return json;
}
struct my_point {
double x;
double y;
double z;
simdjson_really_inline bool operator==(const my_point &other) const {
return x == other.x && y == other.y && z == other.z;
}
simdjson_really_inline bool operator!=(const my_point &other) const { return !(*this == other); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const my_point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
} // namespace largerandom
//
// Implementation
//
#include <vector>
#include "event_counter.h"
#include "dom.h"
#include "json_benchmark.h"
namespace largerandom {
template<typename T> static void LargeRandom(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
namespace sum {
template<typename T> static void LargeRandomSum(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
}
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+35 -1
View File
@@ -7,6 +7,7 @@
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
public:
@@ -24,7 +25,7 @@ simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
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")});
container.emplace_back(my_point{coord["x"], coord["y"], coord["z"]});
}
return true;
@@ -32,6 +33,39 @@ simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
BENCHMARK_TEMPLATE(LargeRandom, OnDemand);
namespace sum {
class OnDemand {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto doc = parser.iterate(json);
for (ondemand::object coord : doc.get_array()) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandomSum, OnDemand);
} // namespace sum
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+1
View File
@@ -7,6 +7,7 @@
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin;
using namespace simdjson::builtin::stage2;
class Sax {
+4 -4
View File
@@ -28,7 +28,7 @@ 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{};
std::vector<uint64_t> ids{};
bool quiet;
public:
@@ -48,7 +48,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 +56,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 +90,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];
}
+196
View File
@@ -0,0 +1,196 @@
#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;
auto error = parser.parse(json).get(doc);
if (error) { std::cerr << error << std::endl; abort(); }
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);
if(buffer == nullptr) {
std::cerr << "Out of memory!" << std::endl;
abort();
}
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) {
if(imple->supported_by_runtime_system()) {
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);
if(mini_buffer == nullptr) {
std::cerr << "Out of memory" << std::endl;
abort();
}
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);
}
+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"
+502
View File
@@ -0,0 +1,502 @@
#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);
}
simdjson_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);
}
}
simdjson_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 error_code GenStatPlus(Stat &stat, const dom::element &v);
static error_code GenStatPlus(Stat &stat, const simdjson_result<dom::element> &r) {
dom::element v;
SIMDJSON_TRY( r.get(v) );
return GenStatPlus(stat, v);
}
static error_code GenStatPlus(Stat &stat, const dom::element &v) {
switch (v.type()) {
case dom::element_type::ARRAY: {
dom::array a;
SIMDJSON_TRY( v.get(a) )
for (auto child : a) {
GenStatPlus(stat, child);
stat.elementCount++;
}
stat.arrayCount++;
} break;
case dom::element_type::OBJECT: {
dom::object o;
SIMDJSON_TRY( v.get(o) );
for (dom::key_value_pair kv : o) {
GenStatPlus(stat, kv.value);
stat.stringLength += kv.key.size();
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++;
std::string_view sv;
SIMDJSON_TRY( v.get(sv) );
stat.stringLength += sv.size();
} break;
case dom::element_type::BOOL: {
bool b;
SIMDJSON_TRY( v.get(b) );
if (b) {
stat.trueCount++;
} else {
stat.falseCount++;
}
} break;
case dom::element_type::NULL_VALUE:
++stat.nullCount;
break;
}
return SUCCESS;
}
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;
}
}
simdjson_never_inline Stat rapidjson_compute_stats_ref(const rapidjson::Value &doc) {
Stat s{};
RapidGenStat(s, doc);
return s;
}
simdjson_never_inline Stat
simdjson_compute_stats_refplus(const simdjson::dom::element &doc) {
Stat s{};
auto error = GenStatPlus(s, doc);
if (error) { std::cerr << error << std::endl; abort(); }
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");
}
}
simdjson_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;
}
}
simdjson_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;
}
simdjson_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; abort(); }
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);
}
}
+429
View File
@@ -0,0 +1,429 @@
#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
#include "yyjson.h"
// #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 HAS_BOOST_JSON
#include <boost/json/parser.hpp>
#include <boost/json/monotonic_resource.hpp>
#endif
#ifdef ALLPARSER
#include "fastjson/core.h"
#include "fastjson/dom.h"
#include "fastjson/fastjson.h"
#include "gason.h"
#include "json11.hpp"
#include "cJSON.h"
#include "jsmn.h"
#include "ujdecode.h"
extern "C" {
#include "ultrajson.h"
}
#include "json/json.h"
#endif
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
#ifdef ALLPARSER
// fastjson has a tricky interface
void on_json_error(void *, simdjson_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
simdjson_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) {
std::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 *)std::malloc(p.size() + 1);
std::memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
#ifndef ALLPARSER
if (!just_data)
#endif
{
std::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
{
std::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,
std::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,
std::memcpy(buffer, p.data(), p.size()) &&
(buffer[p.size()] = '\0'),
repeat, volume, !just_data);
#ifdef HAS_BOOST_JSON
{
const boost::json::string_view sv(p.data(), p.size());
boost::json::parser p;
auto execute = [&p](auto sv) -> bool {
boost::json::error_code ec;
boost::json::monotonic_resource mr;
p.reset( &mr );
p.write(sv,ec);
if(!ec)
auto jv=p.release();
return !!ec;
};
BEST_TIME("Boost.json", execute(sv), false, , repeat, volume, !just_data);
}
#endif
{
auto execute = [&p]() -> bool {
yyjson_doc *doc = yyjson_read(p.data(), p.size(), 0);
bool is_ok = doc != nullptr;
yyjson_doc_free(doc);
return is_ok;
};
BEST_TIME("yyjson", execute(), true, , 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, std::memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
size_t ast_buffer_size = p.size();
size_t *ast_buffer = (size_t *)std::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, std::memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
std::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, std::memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("fastjson ", fastjson_parse(buffer), true,
std::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, std::memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
void *state;
BEST_TIME("ultrajson ",
(UJDecode(buffer, p.size(), NULL, &state) == NULL), false,
std::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);
std::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);
}
std::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 ",
(std::memcpy(buffer, p.data(), p.size()) == buffer), true, ,
repeat, volume, !just_data);
#ifdef __linux__
if (!just_data) {
std::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)
std::printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
std::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++) {
std::memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
unified.start();
if (d.ParseInsitu<kParseValidateEncodingFlag>(buffer).HasParseError() !=
false)
std::printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
std::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++) {
std::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)
std::printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
std::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__
std::free(ast_buffer);
std::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;
}
std::printf("\n\n");
}
return result;
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class Dom {
public:
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:
dom::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline uint64_t nullable_int(dom::element element) {
if (element.is_null()) { return 0; }
return element;
}
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
tweets.clear();
for (dom::element tweet : parser.parse(json)["statuses"]) {
auto user = tweet["user"];
tweets.emplace_back(partial_tweets::tweet{
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(PartialTweets, Dom);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class DomNoExcept {
public:
simdjson_really_inline bool Run(const simdjson::padded_string &json) noexcept;
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
dom::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline simdjson_result<uint64_t> nullable_int(simdjson_result<dom::element> result) noexcept {
dom::element element;
SIMDJSON_TRY( result.get(element) );
if (element.is_null()) { return 0; }
return element.get_uint64();
}
simdjson_really_inline error_code RunNoExcept(const simdjson::padded_string &json) noexcept;
};
simdjson_really_inline bool DomNoExcept::Run(const simdjson::padded_string &json) noexcept {
auto error = RunNoExcept(json);
if (error) { std::cerr << error << std::endl; return false; }
return true;
}
simdjson_really_inline error_code DomNoExcept::RunNoExcept(const simdjson::padded_string &json) noexcept {
tweets.clear();
dom::array tweet_array;
SIMDJSON_TRY( parser.parse(json)["statuses"].get_array().get(tweet_array) );
for (auto tweet_element : tweet_array) {
dom::object tweet;
SIMDJSON_TRY( tweet_element.get_object().get(tweet) );
dom::object user;
SIMDJSON_TRY( tweet["user"].get_object().get(user) );
partial_tweets::tweet t;
SIMDJSON_TRY( tweet["created_at"] .get_string().get(t.created_at) );
SIMDJSON_TRY( tweet["id"] .get_uint64().get(t.id) );
SIMDJSON_TRY( tweet["text"] .get_string().get(t.text) );
SIMDJSON_TRY( nullable_int(tweet["in_reply_to_status_id"]).get(t.in_reply_to_status_id) );
SIMDJSON_TRY( user["id"] .get_uint64().get(t.user.id) );
SIMDJSON_TRY( user["screen_name"] .get_string().get(t.user.screen_name) );
SIMDJSON_TRY( tweet["retweet_count"] .get_uint64().get(t.retweet_count) );
SIMDJSON_TRY( tweet["favorite_count"].get_uint64().get(t.favorite_count) );
tweets.push_back(t);
}
return SUCCESS;
}
} // namespace partial_tweets
+93
View File
@@ -0,0 +1,93 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
class Iter {
public:
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 std::move(value);
}
simdjson_really_inline twitter_user read_user(ondemand::object && user) {
// Move user into a local object so it gets destroyed (and moves the iterator)
ondemand::object u = std::move(user);
return { u["id"], u["screen_name"] };
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
tweets.clear();
// Walk the document, parsing the tweets as we go
// { "statuses":
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("statuses")) { return false; }
// { "statuses": [
if (!iter.start_array()) { return false; }
do {
tweet tweet;
if (!iter.start_object() || !iter.find_field_raw("created_at")) { return false; }
tweet.created_at = iter.consume_string();
if (!iter.has_next_field() || !iter.find_field_raw("id")) { return false; }
tweet.id = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("text")) { return false; }
tweet.text = iter.consume_string();
if (!iter.has_next_field() || !iter.find_field_raw("in_reply_to_status_id")) { return false; }
if (!iter.is_null()) {
tweet.in_reply_to_status_id = iter.consume_uint64();
}
if (!iter.has_next_field() || !iter.find_field_raw("user")) { return false; }
{
if (!iter.start_object() || !iter.find_field_raw("id")) { return false; }
tweet.user.id = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("screen_name")) { return false; }
tweet.user.screen_name = iter.consume_string();
if (iter.skip_container()) { return false; } // Skip the rest of the user object
}
if (!iter.has_next_field() || !iter.find_field_raw("retweet_count")) { return false; }
tweet.retweet_count = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("favorite_count")) { return false; }
tweet.favorite_count = iter.consume_uint64();
tweets.push_back(tweet);
if (iter.skip_container()) { return false; } // Skip the rest of the tweet object
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, Iter);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
-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
+15 -13
View File
@@ -7,6 +7,7 @@
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
@@ -25,15 +26,16 @@ private:
ondemand::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline uint64_t nullable_int(ondemand::value value) {
simdjson_really_inline uint64_t nullable_int(ondemand::value && value) {
if (value.is_null()) { return 0; }
return value;
return std::move(value);
}
simdjson_really_inline twitter_user read_user(ondemand::object user) {
return { user.find_field("id"), user.find_field("screen_name") };
simdjson_really_inline twitter_user read_user(ondemand::object && user) {
// Move user into a local object so it gets destroyed (and moves the iterator)
ondemand::object u = std::move(user);
return { u["id"], u["screen_name"] };
}
static inline bool displayed_implementation = false;
};
@@ -42,15 +44,15 @@ simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
// Walk the document, parsing the tweets as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc.find_field("statuses")) {
for (ondemand::object tweet : doc["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")
tweet["created_at"],
tweet["id"],
tweet["text"],
nullable_int(tweet["in_reply_to_status_id"]),
read_user(tweet["user"]),
tweet["retweet_count"],
tweet["favorite_count"]
});
}
return true;
+28 -33
View File
@@ -1,46 +1,41 @@
#pragma once
#include "json_benchmark/file_runner.h"
//
// Interface
//
namespace partial_tweets {
template<typename T> static void PartialTweets(benchmark::State &state);
} // namespace partial_tweets
//
// Implementation
//
#include "tweet.h"
#include <vector>
#include "event_counter.h"
#include "domnoexcept.h"
#include "json_benchmark.h"
namespace partial_tweets {
using namespace json_benchmark;
using namespace simdjson;
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);
template<typename T> static void PartialTweets(benchmark::State &state) {
//
// Load the JSON file
//
constexpr const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
error_code error;
padded_string json;
if ((error = padded_string::load(TWITTER_JSON).get(json))) {
std::cerr << error << std::endl;
state.SkipWithError("error loading");
return;
}
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);
JsonBenchmark<T, DomNoExcept>(state, json);
}
} // 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
-96
View File
@@ -1,96 +0,0 @@
#pragma once
#ifdef SIMDJSON_COMPETITION_SAJSON
#include "partial_tweets.h"
namespace partial_tweets {
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_uint52(const ::sajson::value &obj, std::string_view key) {
auto val = obj.get_value_of_key({key.data(), key.length()});
switch (val.get_type()) {
case ::sajson::TYPE_INTEGER: {
int64_t result;
if (!val.get_int53_value(&result) || result < 0) { throw "field is not uint52"; }
return uint64_t(result);
}
default:
throw "field not integer";
}
}
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;
}
simdjson_really_inline uint64_t get_nullable_str_uint64(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_NULL) { return 0; }
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;
}
simdjson_really_inline partial_tweets::twitter_user<std::string_view> get_user(const ::sajson::value &obj, std::string_view key) {
auto user = obj.get_value_of_key({key.data(), key.length()});
if (user.get_type() != ::sajson::TYPE_OBJECT) { throw "user is not an object"; }
return { get_str_uint64(user, "id_str"), get_string_view(user, "screen_name") };
}
bool run(simdjson::padded_string &json, std::vector<tweet<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) { 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) { return false; }
result.emplace_back(partial_tweets::tweet<std::string_view>{
get_string_view(tweet, "created_at"),
get_str_uint64 (tweet, "id_str"),
get_string_view(tweet, "text"),
get_nullable_str_uint64(tweet, "in_reply_to_status_id_str"),
get_user (tweet, "user"),
get_uint52 (tweet, "retweet_count"),
get_uint52 (tweet, "favorite_count")
});
}
return true;
}
};
BENCHMARK_TEMPLATE(partial_tweets, sajson)->UseManualTime();
} // namespace partial_tweets
#endif // SIMDJSON_COMPETITION_SAJSON
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include "partial_tweets.h"
#include "sax_tweet_reader_visitor.h"
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
using namespace simdjson::builtin::stage2;
class Sax {
public:
simdjson_really_inline bool Run(const padded_string &json) noexcept;
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.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<tweet> tweets{};
};
// 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 {
tweets.clear();
// Allocate capacity if needed
if (capacity < json.size()) {
SIMDJSON_TRY( Allocate(json.size()) );
}
// Run stage 1 first.
SIMDJSON_TRY( dom_parser.stage1((uint8_t *)json.data(), json.size(), false) );
// Then walk the document, parsing the tweets as we go
json_iterator iter(dom_parser, 0);
sax_tweet_reader_visitor visitor(tweets, string_buf.get());
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(PartialTweets, Sax);
} // namespace partial_tweets

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