Compare commits

..

203 Commits

Author SHA1 Message Date
Daniel Lemire e9b893ff1b Preparing the actual release. 2021-06-05 11:33:32 -04:00
Daniel Lemire 438c2a28ff Preparing patch release. 2021-06-04 17:09:21 -04:00
Daniel Lemire 45182ec11b Tagging the version. 2021-05-27 16:21:00 -04:00
Daniel Lemire a0c020d46d Patching. 2021-05-27 16:20:07 -04:00
Daniel Lemire 076f41ae4b Patch release candidate. 2021-05-15 15:45:02 -04:00
Daniel Lemire 6a37fc1871 Disabling perf testing on version 0.9 2021-05-14 09:22:48 -04:00
Daniel Lemire c6c29c2827 Better definition for fallthrough. 2021-03-31 14:38:52 -04:00
Daniel Lemire 941e903f28 Prerelease commit. 2021-03-31 13:48:43 -04:00
Daniel Lemire 8a3b2f20e4 Version 0.9.1 2021-03-18 11:31:38 -04:00
Daniel Lemire 62cd5f7984 get_root_value is dead code that should have been removed. 2021-03-18 11:30:40 -04:00
Daniel Lemire 2db4592571 Last commit for version 0.9.0. (#1503)
* Last commit for version 0.9.0.

* Removing space.
2021-03-17 11:08:44 -04:00
Daniel Lemire e35088d6ff Update performance.md 2021-03-16 17:57:23 -04:00
Daniel Lemire 156b1b20e3 Update basics.md 2021-03-16 17:56:32 -04:00
Daniel Lemire 9294e8b098 Update performance.md 2021-03-16 17:52:59 -04:00
Daniel Lemire 6dc98561a9 Port the performance notes to "on demand". (#1496)
* Port the performance notes to "on demand".

* No more white space.

* Trimmed another space.
2021-03-16 17:32:38 -04:00
Daniel Lemire 4cfad7adf2 Update README.md 2021-03-12 15:02:47 -05:00
Daniel Lemire 3ab0d3ed94 Update README.md 2021-03-12 15:01:52 -05:00
Daniel Lemire 430f230940 Update README.md 2021-03-12 15:01:12 -05:00
Daniel Lemire 727644c13a Add files via upload 2021-03-12 14:57:39 -05:00
Daniel Lemire 3ab8eca09b Update basics.md 2021-03-12 14:24:14 -05:00
Daniel Lemire 0dc07b34a7 Update basics.md 2021-03-12 14:23:01 -05:00
Daniel Lemire 221c55b620 Update README.md 2021-03-12 14:21:23 -05:00
Daniel Lemire 02f9b83353 This moves us to On Demand as the default front-end. (#1494)
* This moves us to On Demand as the default front-end.

* Made casting magical

* Adding another section

* Undoing my damage.
2021-03-12 14:19:11 -05:00
John Keiser cfc965ff9a Merge pull request #1490 from simdjson/jkeiser/single-ondemand
Don't compile On Demand with extra flags
2021-03-09 16:03:58 -08:00
Daniel Lemire 033df0ebfb Update README.md 2021-03-09 16:29:36 -05:00
Daniel Lemire ac34640224 Update README.md 2021-03-09 16:29:05 -05:00
John Keiser a987192040 Remove SIMDJSON_CPP 2021-03-09 09:10:27 -08:00
John Keiser 751696d7eb Move implementation selection to implementations.h 2021-03-09 09:10:08 -08:00
Daniel Lemire 50aa1566ae Under Apple's compiler, mixing the undefined-behavior sanitizer with the address sanitizer is calling for trouble (#1493)
* Nicer support for Apple's compiler.

* Extending to SIMDJSON_SANITIZE_UNDEFINED

* Better wording.
2021-03-09 11:39:43 -05:00
Daniel Lemire 0a5bba7235 Provides a more correct simdjson::ondemand implementation message. (#1492) 2021-03-09 11:39:19 -05:00
Daniel Lemire 8b8af6aee5 Making input capacity more robust. (#1488) 2021-03-09 09:58:38 -05:00
Daniel Lemire 8e8fbc4cff fixing issue 1480 (#1485) 2021-03-08 19:31:42 -05:00
John Keiser 985dfab2c4 Don't use TARGET unless the target options are *not* specified
This eliminates the possibility of inlining target failures for ondemand

Also makes it so we always compile common architectures needed by simdjson.cpp in simdjson.h, since amalgamation has no way to reason about whether to include / exclude it.
2021-03-08 13:49:09 -08:00
John Keiser 633161fe86 Don't include target flags if the compiler already has them on 2021-03-08 13:48:58 -08:00
John Keiser f51d50399c Only include builtin implementation from header 2021-03-08 13:48:53 -08:00
John Keiser cf4e538536 Separate builtin implementation from "all implementations" 2021-03-06 13:08:42 -08:00
John Keiser ec5ba79447 Add base.h to allow src/ to pick and choose includes 2021-03-05 11:48:34 -08:00
John Keiser 0948573e63 Merge pull request #1479 from simdjson/jkeiser/raw_json_token
Add value.raw_json_token()
2021-03-05 10:24:36 -08:00
John Keiser bad582c2d3 Add value.raw_json_token() 2021-03-05 09:07:41 -08:00
John Keiser f55893807a Merge pull request #1473 from simdjson/jkeiser/promise-padded
Support user-provided buffers in On Demand
2021-03-04 20:56:34 -08:00
Daniel Lemire dc2f767171 Ensuring that stage 1's json_block and cie are never copied and using explicit constructors (issue 1475) (#1478) 2021-03-04 07:59:36 -05:00
John Keiser f0e92e3bdd Pass "capacity" straight to iterate, support std::string 2021-03-03 12:51:00 -08:00
John Keiser 3db1a214ce Support user-provided buffers via promise_padded 2021-03-03 12:50:56 -08:00
John Keiser 79e94227c2 Merge pull request #1472 from simdjson/jkeiser/ondemand-type
Add ondemand::value.type()
2021-03-03 12:49:20 -08:00
John Keiser 665514692a Remove UTF-8 from source code 2021-03-03 12:10:59 -08:00
John Keiser 9944db6d73 Move json_type to ondemand to prevent target mismatch inline errors 2021-03-02 18:31:17 -08:00
John Keiser 2ed24666b5 Add value.type() 2021-03-02 17:02:50 -08:00
Daniel Lemire 0c199cffc3 Removing mallocs in main library. (#1468) 2021-03-02 13:09:22 -05:00
John Keiser 29fe1866ef Move dom / implementations include into their own spots 2021-03-02 09:53:28 -08:00
Daniel Lemire 4811c8036b Update the tape description and the dump_tape function (#1465)
* Would fix issue 1446

* Trimming spaces.
2021-03-01 18:47:11 -05:00
Daniel Lemire 036151c1e3 Test that we can copy the iterators. (#1466) 2021-03-01 18:46:50 -05:00
rychale 9d22372cc9 Make dom::document_stream::iterator copyable and default-constructible by holding pointer to document_stream instead of ref (#1463)
Co-authored-by: Alexey Rychkov <arychkov@defytrading.com>
2021-03-01 14:40:51 -05:00
Daniel Lemire a3d3e347a2 Adding -mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store at GNU GCC under x86/x64 systems. (#1462) 2021-03-01 14:16:30 -05:00
Daniel Lemire 9577c54999 Provide the CMake install the necessarily information (and flags) to hand Windows DLL and add Windows installation tests (#1457)
* This gives the CMake install the necessarily information (and flags) to know
whether we have a Windows DLL and in such cases how to handle the linkage.
2021-02-26 16:17:05 -05:00
Daniel Lemire 72c40be553 This adds tests without threads under linux. (#1460) 2021-02-26 16:16:10 -05:00
Daniel Lemire 6aa8877bf6 This adds tests for the no-exception variant of the library usage under linux. (#1459)
* This adds tests for the no-exception variant of the library usage under linux.

* Fixed name.

* Syntax fix.
2021-02-26 16:15:34 -05:00
Daniel Lemire ef1e256fa7 Workaround for SIMDJSON_ASSUME "side-effect" warning under some compilers. (#1456) 2021-02-25 08:50:57 -05:00
Daniel Lemire ad37651726 Guarding undefined templates with a static_assert. (#1454)
* Guarding undefined templates with a static_assert.

* Fixing comments.

* Undeprecating (or whatever you want to call it).
2021-02-22 16:54:47 -05:00
John Keiser 92caeb039f Merge pull request #1453 from simdjson/jkeiser/number-overrun
Fix ridiculously unlikely buffer overrun in ondemand
2021-02-22 09:39:43 -08:00
John Keiser cdb607f202 Add comments on confusing "simdjson_unused" 2021-02-22 09:39:09 -08:00
John Keiser bcab8d3abf Check for end object/array at top level
This avoids a very unlikely buffer overrun that can occur in a particular kind of invalid JSON:
- the document is invalid with an unclosed top level array or object
- the last thing in the document is a number that ends at EOF
- the padding is filled entirely with numeric digits
2021-02-22 09:35:21 -08:00
John Keiser 9d747642fe Merge pull request #1452 from simdjson/jkeiser/safe-iter-fix
Fix bug reporting out of order iteration
2021-02-22 09:34:49 -08:00
John Keiser a755203aee Update value_iterator-inl.h 2021-02-22 09:34:20 -08:00
John Keiser 814726e5d4 Merge pull request #1432 from simdjson/jkeiser/safe-lookup
Alert user when they use object indexing incorrectly
2021-02-22 09:31:59 -08:00
John Keiser c4a312e6c3 Fix strict overflow warning 2021-02-21 14:06:58 -08:00
Daniel Lemire 81609393f1 Fixing issue 1449. (#1451) 2021-02-21 16:33:05 -05:00
John Keiser 0634958329 Don't emit out of order iteration error for empty array 2021-02-21 11:43:36 -08:00
John Keiser b352b903e7 Fix bug where iterators didn't always report errors 2021-02-21 11:43:36 -08:00
John Keiser 74d6658f39 Make out of order iteration tests actually test errors in the loop 2021-02-21 11:43:36 -08:00
John Keiser 3076de0405 Use SIMDJSON_DEVELOPMENT_CHECKS instead of SIMDJSON_PRODUCTION
Don't enable in retail
2021-02-20 11:46:01 -08:00
John Keiser 4a0a0ed4c6 Split more tests into separate methods 2021-02-20 11:22:24 -08:00
Vadim Peretokin c5def8f706 Document SIMDJSON_EXCEPTIONS (#1443)
* Document SIMDJSON_EXCEPTIONS

* Add an example

* Enchance clarity
2021-02-17 14:30:58 -05:00
Daniel Lemire 610b3ad302 Adds Visual Studio 2017 to CI (for real) and adapt our build/tests (#1444) 2021-02-15 19:49:12 -05:00
Daniel Lemire 4c63a929bc This makes it possible to a have document instance (DOM) that is separate from the parser if you would like. (#1430)
* This makes it possible to a have document instance that is separate from the parser if you would like.
2021-02-10 14:44:53 -05:00
Daniel Lemire 0f72ff3a57 Adding Visual studio 2017 CI tests to GitHub actions. (#1438)
* Adding Visual studio 2017 CI tests to GitHub actions.

* Fixing the name.
2021-02-09 10:05:45 -05:00
Daniel Lemire 67afcd4edd Adding legacy Win32 / Visual Studio 2017 CI tests to GitHub Actions. (#1439) 2021-02-09 10:05:33 -05:00
David CARLIER 6e9ada2d6c unit test macos build fix (#1434) 2021-02-08 14:12:39 -05:00
John Keiser 9651efe626 Split up tests for compile times 2021-02-06 11:07:14 -08:00
John Keiser df7201ba42 Fix Windows assume error 2021-02-06 11:06:53 -08:00
John Keiser 14315ec5cd Default SIMDJSON_PRODUCTION to OFF for bare header usage 2021-02-06 11:06:37 -08:00
John Keiser 0f10fc9ad9 Fix Windows _assume warning 2021-02-05 18:53:39 -08:00
John Keiser ce678fd986 Fix GCC 7 strict-overflow warning 2021-02-05 18:53:31 -08:00
John Keiser 9d693da852 Only set container depth when a container iteration starts 2021-02-05 17:20:24 -08:00
John Keiser 22742b6bd6 Make max_depth() a simple check 2021-02-05 17:11:03 -08:00
John Keiser a33bf40a7d Add tests for sibling indexing detection 2021-02-05 16:39:52 -08:00
John Keiser 3801ea7777 Disable all OUT_OF_ORDER_ITERATION checks when SIMDJSON_API_USAGE_CHECKS
is off
2021-02-05 16:39:44 -08:00
John Keiser c7935ceed1 Put parser capacity / max_depth back into parser 2021-02-05 16:39:36 -08:00
John Keiser ea119a5679 Start parsing at depth 1 instead of using descend_to for it 2021-02-05 16:39:34 -08:00
John Keiser 7a324da548 Add -DSIMDJSON_PRODUCTION flag 2021-02-05 16:34:27 -08:00
John Keiser 0d1c99a6ad Allow object lookup safety to be disabled
Use cmake -DSIMDJSON_API_USAGE_CHECKS=OFF ..
2021-02-05 10:18:01 -08:00
John Keiser e4626d233c Descend into fields at the value position, not the key 2021-02-05 10:18:01 -08:00
John Keiser 9934f65987 Store start index of each depth for safety 2021-02-05 10:17:28 -08:00
John Keiser b2de2dfd1b Merge pull request #1416 from simdjson/jkeiser/safe-iterators-2
Add safety checks for out of order array/object iteration+indexing
2021-02-05 09:47:03 -08:00
John Keiser 3f2639a655 Merge pull request #1414 from simdjson/jkeiser/array-assert
Fix #1409 (assert when trying to get one value as multiple types)
2021-02-05 09:45:49 -08:00
Daniel Lemire 26b5b19f88 Unneeded. 2021-02-02 17:58:10 -05:00
Daniel Lemire 5449365658 This should make issue 1370 easier to track. (#1429)
* This should make issue 1370 easier to track.

* Avoiding exceptions.
2021-02-02 16:12:16 -05:00
Daniel Lemire 96536239c2 Deleting the function. (#1428) 2021-02-02 09:48:01 -05:00
Daniel Lemire 0e18453e34 Potential optimizations applied to jkeiser/array-assert (#1421)
* Some tuning.
* Using table lookups...
2021-02-01 12:39:10 -05:00
Daniel Lemire 777202e1f1 Why would you use a reference when looping? (#1422)
* Why would you use a reference?

* I missed a few cases.
2021-02-01 12:30:36 -05:00
Daniel Lemire 6f61ed1477 It appears that Qt uses macros for common terms like slots, signals and so forth. (#1425) 2021-02-01 11:31:29 -05:00
Daniel Lemire a509e04f9b Let us actually test the find package functionality. (#1418)
* Let us actually test the find package functionality.

* Specifying the path.

* Fixing the path.
2021-01-28 13:59:35 -05:00
tobim 152eb983ff Fix the CMake generated install target (#1412)
* Re-enable installation of simdjson-config.cmake

* Install a simdjson-config-version.cmake file
2021-01-27 18:39:05 -05:00
Daniel Lemire d6f33e4830 This adds a little test to see if we can compiler with very strict flags (conventional casts) (#1417)
* This adds a little test to see if we can compiler with very strict flags.

* Trimming a leftover old-style cast.

* More cleaning.

* A few more pedantic casts.
2021-01-27 18:37:30 -05:00
Tibbel 5613d30e97 partly replacement old-style-cast to c++ *_cast (#1403)
Co-authored-by: Tibbel <tibbel@ma-gi.de>
2021-01-27 13:33:48 -05:00
John Keiser 1bfbb6448a Check out-of-order error in object index 2021-01-26 20:49:14 -08:00
John Keiser c5b44f44f9 Add partial out-of-order check for field lookup 2021-01-26 20:00:39 -08:00
John Keiser 22b3ea93a8 Emit an error if user tries to iterate arrays out of order 2021-01-26 20:00:19 -08:00
John Keiser fe726b0f80 Split up ondemand_dom_api_tests for sanitize build times 2021-01-26 19:42:37 -08:00
John Keiser 18ecc0032d Reenable test that is now working 2021-01-26 15:15:09 -08:00
John Keiser 1a1532c8cc Return INCORRECT_TYPE when numbers fail to parse
Also add tests for trying to get multiple types in a row
2021-01-26 14:59:13 -08:00
John Keiser e6d2b7759a Fix assertion when getting array after failing to get a scalar
Also remove distinction between & and && for array start, acting like
other types
2021-01-26 14:09:54 -08:00
Daniel Lemire c96ff018fe Version 0.8. 2021-01-22 12:04:08 -05:00
Daniel Lemire 1005c62e90 It seems that we export too many targets. (#1385)
* It seems that we export too many targets.

* Adding missing word.

* Let us try this.

* Restoring dead line.

* Some fixes.

* Update src/CMakeLists.txt

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>

* Update cmake/simdjson-config.nothread.cmake.in

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>

* Update cmake/simdjson-config.cmake.in

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>

* Update CMakeLists.txt

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>

* Update CMakeLists.txt

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>

* Removing useless file

* Simplifying the PR somewhat.

Co-authored-by: Alexander Neumann <30894796+Neumann-A@users.noreply.github.com>
2021-01-20 13:20:49 -05:00
Daniel Lemire 2a714f4e37 Hide the std::pair inheritance in our result instances (#1396)
* Fixing issue 1243

* The tie must go.

* Having std::pair be a protected inheritance breaks on demand.

* Putting it back.

* You really want to use emplace.

* Fixing one botched test.

* Prettier test.

* Using safer code.

* Fixing unsafe code.

* Simplifying the fuzzer.

* Trying another way.

* Ok. It should work without exceptions.

* Removing trailing spaces.
2021-01-18 12:00:02 -05:00
Daniel Lemire 73063e2dab This adds a test to explain better the issue 1341: Support conversion of simdjson_result lvalue to dom::element (#1397)
* This adds a test to explain better the issue.

* Guarding it since it can throw.
2021-01-16 15:08:27 -05:00
Daniel Lemire 6e5d232ccc Fixing forgotten namespace. 2021-01-16 02:50:55 +00:00
Daniel Lemire 8e96e38099 We should trim out old benchmarks. (#1379) 2021-01-15 14:46:05 -05:00
Daniel Lemire 7517b9b122 Updating headers. 2021-01-14 17:37:43 -05:00
John Keiser 55faf4c5bc Recommend simdjson::ondemand over simdjson::builtin::ondemand (#1380)
Co-authored-by: Daniel Lemire <lemire@gmail.com>
2021-01-14 17:33:49 -05:00
John Keiser 92372412d9 Merge pull request #1387 from simdjson/jkeiser/ondemand-scalar-order
Allow JSON values to be parsed later / out of order
2021-01-14 11:02:53 -08:00
Daniel Lemire 990da22249 Merge branch 'master' into jkeiser/ondemand-scalar-order 2021-01-13 14:21:16 -05:00
John Keiser 3849cc400e Merge pull request #1372 from simdjson/jkeiser/ondemand-sajson
Add sajson and nlohmann_json benchmarks
2021-01-13 09:33:53 -08:00
ihsinme 84b0e84447 fix function was exited without releasing (#1389)
* Update jsoncheck.cpp

* Update minefieldcheck.cpp

* Update jsoncheck.cpp

* Update minefieldcheck.cpp
2021-01-12 18:08:27 -05:00
Daniel Lemire 3b8486665a Removing trailing white space. 2021-01-12 18:04:51 -05:00
friendlyanon 451c393ef1 [skip ci] Skip CI for Github (#1336)
* Add conditional to github workflows

* [skip ci] Document and encourage skipping CI

Co-authored-by: friendlyanon <friendlyanon@users.noreply.github.com>
2021-01-11 19:08:01 -05:00
Nicolai Grodzitski 095691160b Add element representation trivially_copyable property test (#1377)
* Add element representation trivially_copyable property test

Releates to issue #1374

* Change test label
2021-01-11 19:06:59 -05:00
Daniel Lemire 7597cb354c Folding the line. 2021-01-11 18:49:35 -05:00
Daniel Lemire bd2a31a0fe Minor edits regarding the On Demand documentation. (#1384)
* Minor edits regarding the On Demand documentation.

* Adding more instructions for CMake

* Tweaking.

* Adding changes requested by John.

* Bringing back detailed explanations of -march=native.
2021-01-11 18:48:02 -05:00
John Keiser be61650102 Add top_tweet benchmark to test laziness 2021-01-11 15:19:26 -08:00
John Keiser 3279c2f15b Remove unnecessary "try_get_XXX" methods
Also don't distinguish between & and && (instead, advance the first time
you encounter a scalar no matter what)
2021-01-11 15:19:26 -08:00
John Keiser 38da15b501 Rename checkpoint() -> position(), add token_position type 2021-01-11 15:19:24 -08:00
John Keiser 0f515785c6 Support reading scalars out of order 2021-01-11 15:17:46 -08:00
John Keiser 66db102c70 Use imprecise double comparison for sajson 2021-01-11 15:12:12 -08:00
John Keiser ab859f7952 Add nlohmann_json benchmarks 2021-01-11 15:12:12 -08:00
John Keiser 6367e55a5f Use new double differ in kostya/large_random benchmarks 2021-01-11 15:12:12 -08:00
Daniel Lemire b61f2799a8 This makes the float errors explicit. 2021-01-11 15:12:12 -08:00
John Keiser 1b4d3bcbb6 Add sajson benchmarks 2021-01-11 15:12:12 -08:00
John Keiser 45479558ba Merge pull request #1376 from simdjson/jkeiser/ondemand-runtime
Use runtime-selected implementation for stage 1 on demand
2021-01-11 14:59:52 -08:00
John Keiser 6fed8d2a26 Use active implementation for stage 1 on demand 2021-01-11 14:57:52 -08:00
Daniel Lemire 7dbe4caf3f Taking a float and adding hundreds of zeros, you may get a truncated value that is just one over the desired value (bug fix) (#1388)
* Found a bug where if take some float and add many zeros, you may get a truncated value that is just one over the desired value.
2021-01-11 17:07:25 -05:00
John Keiser ce1756425f Merge pull request #1360 from simdjson/jkeiser/ondemand-docs
Add On Demand documentation
2021-01-09 13:34:08 -08:00
John Keiser 920f535500 Reamalgamate 2021-01-07 08:49:54 -08:00
Daniel Lemire f29b70c63e Adding GitHub Actions VS - without exceptions. (#1362)
* Adding GitHub Actions VS - without exceptions.

* Rename.
2021-01-06 09:40:35 -05:00
John Keiser 1e690e505b Merge pull request #1371 from simdjson/jkeiser/ondemand-insitu-manualtime
Add yyjson_insitu tests, make insitu tests more accurate
2021-01-05 14:38:52 -08:00
John Keiser cd27bf0745 Add yyjson_insitu tests 2021-01-05 12:16:19 -08:00
John Keiser 62ded15cd8 Rename tweets/text/points -> result 2021-01-05 11:55:57 -08:00
John Keiser bc6907d280 Handle in situ document copies outside of the loop 2021-01-05 11:52:05 -08:00
John Keiser dcd2e13aec Measure time more accurately 2021-01-05 10:45:49 -08:00
John Keiser cfff8a5ed5 Merge pull request #1368 from simdjson/jkeiser/ondemand-rapidjson
Head-to-head rapidjson benchmarks
2021-01-05 09:58:22 -08:00
John Keiser 2d760e75dc Remove public: from structs 2021-01-05 09:10:22 -08:00
John Keiser f071a15591 Add insitu versions of rapidjson benchmark 2021-01-04 20:30:54 -08:00
John Keiser 6a595231b0 Get rid of templates from rapidjson benchmarks 2021-01-04 20:20:24 -08:00
John Keiser 065ea00066 Fix kostya<yyjson> issue 2021-01-04 20:03:22 -08:00
John Keiser 680cd6df34 Add usage benchmarks for rapidjson 2021-01-04 20:03:21 -08:00
John Keiser 22846f7577 Merge pull request #1363 from simdjson/jkeiser/ondemand-benchmarks
Head-to-head yyjson / ondemand benchmarks
2021-01-04 15:24:28 -08:00
John Keiser 5583a3c89b Add error handling to yyjson 2021-01-04 13:05:37 -08:00
John Keiser 25d1c7e622 Fix yyjson double reading 2021-01-04 12:37:25 -08:00
John Keiser 5add8ac255 Rearrange benchmarks to be easier to create 2021-01-04 12:33:41 -08:00
John Keiser 3af54a9978 Add Yyjson benchmarks 2021-01-01 23:04:19 -08:00
John Keiser 1dc4e9a84c Create custom result printers to show actual differences 2021-01-01 22:03:44 -08:00
John Keiser 9af41dd988 Add PartialTweets<Yyjson> benchmark 2021-01-01 22:03:38 -08:00
John Keiser 0039c5b981 Disallow parser.iterate("1"_padded), as it won't work 2021-01-01 19:18:00 -08:00
John Keiser 17f4f82827 Ondemand usage docs (and associated tests)
Also disallowed parsing a temporary padded_string, since the JSON *must*
live through the whole parse.
2021-01-01 19:17:58 -08:00
John Keiser 0314889c6d Merge pull request #1357 from simdjson/jkeiser/ondemand-quickstart
Add quick start for On Demand
2021-01-01 17:39:55 -08:00
John Keiser 7387e9f9a8 Add quick start for On Demand 2021-01-01 14:50:47 -08:00
John Keiser 158a3c53d8 Merge pull request #1351 from simdjson/jkeiser/unordered-lookup
Make `object["field"]` order-insensitive in On Demand
2020-12-24 13:33:09 -08:00
John Keiser 98666e84ea Add unordered version of LargeRandom ondemand benchmark 2020-12-23 09:14:45 -08:00
John Keiser d91491bf13 Update documentation for out-of-order fields 2020-12-23 09:14:45 -08:00
John Keiser 92443772d2 Enable all acceptance tests on mingw 2020-12-23 09:14:45 -08:00
John Keiser 041d59cc17 Create acceptance_tests, all_tests, etc. make targets
And use them for mingw build and test
2020-12-23 09:14:45 -08:00
John Keiser a405173d59 Break up ondemand_basictests into smaller executables
(Allows for faster compilation speeds)
2020-12-23 09:14:44 -08:00
John Keiser 93807bf230 Merge pull request #1352 from simdjson/jkeiser/assert-test-release
Make assert test succeed (not run anything) in Release builds
2020-12-23 08:34:18 -08:00
Daniel Lemire 0b67847b56 Adding clang/msys-ci (#1354) 2020-12-22 17:53:53 -05:00
ihsinme a72cb8a37a Update minifiercompetition.cpp (#1353)
as described in the description for the allocate_padded_buffer function: // The caller is responsible to free the memory (e.g., delete [] (...)).
but your code used the function free.
I propose to fix this error.
2020-12-22 16:50:37 -05:00
John Keiser ad4f718e0c Make assert test succeed (not run anything) in Release builds 2020-12-21 10:52:01 -08:00
John Keiser 5f92cd2b00 Merge pull request #1349 from simdjson/dlemire/prune_validate_utf8
We don't include anything but lookup4 ?
2020-12-21 10:05:31 -08:00
John Keiser a1cf588d5f Fix GCC 7 warning when inlining does its job 2020-12-21 09:19:07 -08:00
John Keiser dfc510f009 Add bench_ondemand_largerandom to check theory about executable format 2020-12-20 11:39:56 -08:00
John Keiser e7e09e444c Use find_field in benchmark 2020-12-20 11:39:56 -08:00
John Keiser 195acc3e45 Add find_field / find_field_unordered to object 2020-12-20 11:39:50 -08:00
John Keiser b8426584fc Make field lookup order-insensitive. 2020-12-19 13:57:29 -08:00
Daniel Lemire 6a2435ab32 Merge branch 'master' into dlemire/prune_validate_utf8 2020-12-19 10:46:14 -05:00
Daniel Lemire 85001c55fb Fixing UTF-8 validation under PPC64 (#1346)
* Entering a new UTF-8 test

* Maybe *I* had a bug in the tests.

* Replacing nulls with 1s.

* Let us try to be more verbose.

* Return 0.

* Fixing issue.

* Adding puzzler scenario.

* Fixing PPC64

Co-authored-by: Daniel Lemire <dlemire@rcs-power9-talos>
2020-12-19 10:42:27 -05:00
Daniel Lemire 8c4cf7757f I meant to remove these files a while ago. 2020-12-18 15:29:08 -05:00
John Keiser f785f76d98 Merge pull request #1342 from simdjson/jkeiser/iter-safety
Safety: assert in debug mode when on demand values are used out of order
2020-12-16 15:58:29 -08:00
Daniel Lemire c90ee57203 This might make the fuzzer error debuggable. (#1345) 2020-12-16 18:31:29 -05:00
John Keiser d670906ff3 Don't keep a separate at_start boolean in object 2020-12-15 11:29:31 -08:00
Daniel Lemire c578f63f34 Fixing issue 1337 (#1338)
* Fixing issue 1337

* This test should always run.

* Removing bad attribute access.
2020-12-14 16:20:36 -05:00
Daniel Lemire 9a404bdcdc Reenabling the optimized kernels (main branch). (#1344)
* Reenabling the optimized kernels (main branch).

* Defining SIMDJSON_CAN_ALWAYS_RUN_PPC64 and SIMDJSON_CAN_ALWAYS_RUN_ARM64

* Adding the bad UTF8 string from the fuzzer.

* Taking into account John's comments.

* Bumping the lib version.

* Update CMakeLists.txt
2020-12-14 14:28:45 -05:00
friendlyanon 91b07ba075 Allow build without download (#1334)
* Add option for downloading dependencies

* Format the boost json import code

Co-authored-by: friendlyanon <friendlyanon@users.noreply.github.com>
2020-12-13 19:07:29 -05:00
John Keiser f9c6dedca1 Add test for out-of-order parse asserts 2020-12-13 13:39:47 -08:00
John Keiser 73c7b7db3d Safety: check index of scalar values before use 2020-12-11 11:57:09 -08:00
John Keiser 6b02b06581 Merge pull request #1330 from simdjson/jkeiser/depth-tracking
Permit partial iteration in On Demand
2020-12-09 12:40:58 -08:00
Paul Dreik 5f7b2bac12 fuzz on power (#1326)
* first try

* use ubuntu 20.04, do the fuzzing

* new try at power fuzz

* hard code clang version

* setting env variables does not seem to work

* use fuzzer-no-link

* switch to Debian Buster for power fuzz

* use non-sanitizer build for power

* me not like yaml

* fix bad syntax
2020-12-07 18:12:36 -05:00
John Keiser 806cb39103 Clean up some more benchmark/test code 2020-12-07 13:44:58 -08:00
John Keiser 2eaeac53e4 Revamp design documentation to match new design 2020-12-07 13:09:44 -08:00
John Keiser 3baba73cf5 Don't call functions in assume (VS2019 Clang doesn't like it) 2020-12-07 11:17:24 -08:00
John Keiser db6bf15361 Allow all array/object/etc. to be copied 2020-12-06 16:29:53 -08:00
John Keiser 3aa3175378 Add tests for recovering from partial child iteration 2020-12-06 16:19:06 -08:00
John Keiser 4c63956624 Enable object["x"]["y"] 2020-12-06 15:23:52 -08:00
John Keiser c89647af9e Make all values use same underlying iterator 2020-12-06 15:23:52 -08:00
John Keiser 1303a88769 Unconditionally track depth 2020-12-06 15:23:52 -08:00
John Keiser aa1eabbb56 Add benchmark that stops early 2020-12-06 15:23:51 -08:00
299 changed files with 20258 additions and 30286 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: -E checkperf
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
- 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: -E "checkperf|ondemand_basictests"
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
CTEST_ARGS: -LE explicitonly
- job_name: VS2019 (Win32, No Exceptions)
platform: Win32
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
CTEST_ARGS: -E "checkperf|ondemand_basictests"
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
CTEST_ARGS: -LE explicitonly
- job_name: VS2015
image: Visual Studio 2015
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -LE explicitonly
build_script:
- mkdir build
+31 -10
View File
@@ -103,7 +103,14 @@ commands:
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L acceptance &&
ctest $CTEST_FLAGS -LE acceptance -E checkperf
ctest $CTEST_FLAGS -LE acceptance -LE explicitonly
cmake_assert_test:
steps:
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L assert
cmake_test_all:
steps:
@@ -112,9 +119,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 -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 &&
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 &&
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
@@ -144,6 +151,16 @@ 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
@@ -174,22 +191,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 -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
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 -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
threadsanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
# dynamic
dynamic-gcc10:
@@ -245,12 +262,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 -E checkperf }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
steps: [ cmake_test ]
workflows:
@@ -292,4 +309,8 @@ 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 -E checkperf
- ctest --output-on-failure -LE explicitonly
+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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- mkdir build
- cd build
@@ -194,7 +194,7 @@ steps:
CXX: clang++-11
CMAKE_FLAGS: -GNinja
BUILD_FLAGS:
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
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 -E checkperf
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
commands:
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
- apt-get update -qq
+4 -1
View File
@@ -10,6 +10,9 @@ 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
@@ -34,4 +37,4 @@ jobs:
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -E checkperf"
./alpine.sh bash -c "cd build_for_alpine && ctest -LE explicitonly"
+4 -1
View File
@@ -11,7 +11,10 @@ on:
- cron: 23 */8 * * *
jobs:
build:
build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-latest
env:
# fuzzers that change behaviour with SIMDJSON_FORCE_IMPLEMENTATION
+5 -2
View File
@@ -15,6 +15,9 @@ 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
@@ -61,5 +64,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 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
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
+7 -4
View File
@@ -15,6 +15,9 @@ 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
@@ -61,11 +64,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 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
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
cd ..
mkdir build64debug
cd build64debug
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF ..
cmake --build . --target 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
cmake --build . --target acceptance_tests --verbose
ctest -L acceptance --output-on-failure
+54
View File
@@ -0,0 +1,54 @@
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
+4 -1
View File
@@ -10,6 +10,9 @@ 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:
@@ -51,4 +54,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 -E checkperf
ctest -j4 --output-on-failure -LE explicitonly
+65
View File
@@ -0,0 +1,65 @@
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,6 +10,9 @@ 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,6 +10,9 @@ 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
+4 -1
View File
@@ -10,6 +10,9 @@ 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
@@ -23,6 +26,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 -E checkperf &&
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
+5 -2
View File
@@ -10,6 +10,9 @@ 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
@@ -21,6 +24,6 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -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
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,34 @@
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,6 +10,9 @@ 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
+8 -3
View File
@@ -10,6 +10,9 @@ 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
@@ -23,6 +26,8 @@ jobs:
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=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
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 .
+46
View File
@@ -0,0 +1,46 @@
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
+31 -21
View File
@@ -10,27 +10,37 @@ 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:
- 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"
- 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
+10 -8
View File
@@ -10,6 +10,9 @@ 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:
@@ -18,18 +21,17 @@ jobs:
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
- name: 'Run CMake with VS16 Clang'
uses: lukka/run-cmake@v3
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 -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
run: ctest -C Release -LE explicitonly --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+21 -6
View File
@@ -10,6 +10,9 @@ 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:
@@ -24,13 +27,25 @@ 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 -E checkperf --output-on-failure
run: ctest -C Release -LE explicitonly --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
@@ -0,0 +1,36 @@
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 -E checkperf"
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
script:
- mkdir build
+23 -6
View File
@@ -1,16 +1,18 @@
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 7)
set(PROJECT_VERSION_PATCH 1)
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(PROJECT_VERSION_MINOR 9)
set(PROJECT_VERSION_PATCH 6)
set(SIMDJSON_SEMANTIC_VERSION "0.9.6" 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_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
include(GNUInstallDirs)
@@ -43,6 +45,21 @@ 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,6 +70,8 @@ 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 -E checkperf"
# docker run -it -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && ctest . --output-on-failure -LE explicitonly"
#
# 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.7.1"
PROJECT_NUMBER = "0.9.6"
# 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
+29 -50
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.7.0/index.html)
[![][license img]][license] [![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.org/api/0.9.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
@@ -11,12 +11,11 @@ simdjson : Parsing gigabytes of JSON per second
<img src="images/logo.png" width="10%" style="float: right">
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
to parse JSON 2.5x faster than RapidJSON and 25x faster than JSON for Modern C++.
to parse JSON 4x faster than RapidJSON and 25x faster than JSON for Modern C++.
* **Fast:** Over 2.5x faster than commonly used production-grade JSON parsers.
* **Fast:** Over 4x 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.
@@ -28,6 +27,7 @@ Table of Contents
-----------------
* [Quick Start](#quick-start)
* [On Demand](#on-demand)
* [Documentation](#documentation)
* [Performance results](#performance-results)
* [Real-world usage](#real-world-usage)
@@ -40,23 +40,29 @@ 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"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
}
```c++
#include "simdjson.h"
using namespace simdjson;
int main(void) {
ondemand::parser parser;
padded_string json = padded_string::load("twitter.json");
ondemand::document tweets = parser.iterate(json);
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
}
```
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
@@ -73,59 +79,31 @@ 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.7.0/annotated.html) contains the automatically generated API documentation.
* [API](https://simdjson.org/api/0.9.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) and
fifty percent less than sajson. 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). 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 9 compiler (with the -O3 flag).
We compare against the best and fastest C++ libraries.
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.
The simdjson library offers full unicode ([UTF-8](https://en.wikipedia.org/wiki/UTF-8)) validation and exact
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 |
number parsing.
<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="90%">
<img src="doc/growing.png" width="60%">
[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).
@@ -148,6 +126,7 @@ 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).
+18 -27
View File
@@ -1,13 +1,7 @@
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
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)
@@ -22,29 +16,26 @@ 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)
add_executable(bench_ondemand bench_ondemand.cpp)
if(SIMDJSON_EXCEPTIONS)
add_executable(bench_ondemand bench_ondemand.cpp)
if(TARGET yyjson)
target_link_libraries(bench_ondemand PRIVATE yyjson)
endif()
if(TARGET rapidjson)
target_link_libraries(bench_ondemand PRIVATE rapidjson)
endif()
if(TARGET sajson)
target_link_libraries(bench_ondemand PRIVATE sajson)
endif()
if(TARGET nlohmann_json)
target_link_libraries(bench_ondemand PRIVATE nlohmann_json)
endif()
endif()
endif()
include(checkperf.cmake)
# deliberately disabling.
# 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.insert(user["screen_name"]);
default_users.emplace(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<dom::object>()) {
image_sizes.insert({ size.value["w"], size.value["h"] });
for (auto size : image["sizes"].get_object()) {
image_sizes.emplace(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.insert(string_view(iter.get_string(), iter.get_string_length()));
default_users.emplace(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.insert({ width, height });
image_sizes.emplace(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.insert({ width, height });
image_sizes.emplace(width, height);
} while (iter.next()); // next size
if (!iter.up()) { return; } // back to sizes
+61 -15
View File
@@ -1,26 +1,72 @@
#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/ondemand.h"
#include "partial_tweets/iter.h"
#include "partial_tweets/dom.h"
#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 "largerandom/ondemand.h"
#include "largerandom/iter.h"
#include "largerandom/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 "kostya/ondemand.h"
#include "kostya/iter.h"
#include "kostya/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 "distinctuserid/ondemand.h"
#include "distinctuserid/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 "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
@@ -1,14 +0,0 @@
#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 result: " + error_message(error));
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON text: " + 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((const uint8_t *)json.data(), json.size());
auto result = parser.parse(reinterpret_cast<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((const uint8_t *)json.data(), json.size(), false);
error = parser.implementation->stage1(reinterpret_cast<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((const uint8_t *)json.data(), json.size());
auto firstresult = parser.parse(reinterpret_cast<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((const uint8_t *)json.data(), json.size());
auto result = parser.parse(reinterpret_cast<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 = (char*)malloc(strlen(filename)+1);
char* filename_copy = reinterpret_cast<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) ) # 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) AND (NOT MSVC) ) # 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)
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation explicitonly)
set_property(TEST checkperf APPEND PROPERTY DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
else()
@@ -0,0 +1,53 @@
#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
@@ -0,0 +1,27 @@
#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
@@ -0,0 +1,58 @@
#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
@@ -0,0 +1,80 @@
#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
@@ -0,0 +1,36 @@
#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
@@ -0,0 +1,37 @@
#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
@@ -0,0 +1,61 @@
#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
@@ -1,52 +0,0 @@
#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
@@ -1,89 +0,0 @@
#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
@@ -1,67 +0,0 @@
#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
@@ -1,406 +0,0 @@
#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
@@ -0,0 +1,40 @@
#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
@@ -0,0 +1,29 @@
#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
@@ -0,0 +1,52 @@
#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
@@ -0,0 +1,66 @@
#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
@@ -0,0 +1,33 @@
#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
@@ -0,0 +1,33 @@
#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
@@ -0,0 +1,53 @@
#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).first.swap(p);
simdjson::padded_string::load(filename).value_unsafe().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
@@ -1,80 +0,0 @@
#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
@@ -0,0 +1,8 @@
#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
@@ -0,0 +1,92 @@
#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
@@ -0,0 +1,48 @@
#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
@@ -0,0 +1,26 @@
#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
@@ -0,0 +1,107 @@
#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
@@ -0,0 +1,40 @@
#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
@@ -0,0 +1,37 @@
#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
@@ -1,69 +0,0 @@
#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
@@ -1,96 +0,0 @@
#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
+38 -47
View File
@@ -1,18 +1,41 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
//
// Interface
//
#include "json_benchmark/string_runner.h"
#include "json_benchmark/point.h"
#include <vector>
#include <random>
namespace kostya {
template<typename T> static void Kostya(benchmark::State &state);
namespace sum {
template<typename T> static void KostyaSum(benchmark::State &state);
}
using namespace simdjson;
using namespace json_benchmark;
static const simdjson::padded_string &get_built_json_array();
template<typename I>
struct runner : public string_runner<I> {
std::vector<point> result;
runner() : string_runner<I>(get_built_json_array()) {}
bool before_run(benchmark::State &state) {
if (!string_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, I::DiffFlags);
}
size_t items_per_iteration() {
return result.size();
}
};
static void append_coordinate(std::default_random_engine &e, std::uniform_real_distribution<> &dis, std::stringstream &myss) {
using std::endl;
@@ -49,47 +72,15 @@ static std::string build_json_array(size_t N) {
return answer;
}
static const padded_string &get_built_json_array() {
static padded_string json = build_json_array(524288);
static const simdjson::padded_string &get_built_json_array() {
static simdjson::padded_string json = build_json_array(524288);
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); }
};
struct simdjson_dom;
simdjson_unused static std::ostream &operator<<(std::ostream &o, const my_point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
template<typename I> simdjson_really_inline static void kostya(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // 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
@@ -0,0 +1,25 @@
#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
@@ -1,74 +0,0 @@
#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
@@ -0,0 +1,61 @@
#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
@@ -0,0 +1,65 @@
#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
@@ -0,0 +1,28 @@
#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
@@ -0,0 +1,29 @@
#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
@@ -0,0 +1,66 @@
#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
@@ -0,0 +1,73 @@
#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
@@ -0,0 +1,24 @@
#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
@@ -0,0 +1,59 @@
#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
@@ -0,0 +1,63 @@
#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
@@ -0,0 +1,28 @@
#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
@@ -0,0 +1,29 @@
#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
@@ -0,0 +1,29 @@
#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
@@ -0,0 +1,64 @@
#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
@@ -1,69 +0,0 @@
#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,7 +7,6 @@
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin;
class Iter {
public:
@@ -48,45 +47,6 @@ 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
@@ -1,80 +0,0 @@
#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
+1 -35
View File
@@ -7,7 +7,6 @@
namespace largerandom {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
public:
@@ -25,7 +24,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["x"], coord["y"], coord["z"]});
container.emplace_back(my_point{coord.find_field("x"), coord.find_field("y"), coord.find_field("z")});
}
return true;
@@ -33,39 +32,6 @@ 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,7 +7,6 @@
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> ids{};
std::vector<uint64_t> result{};
bool quiet;
public:
@@ -48,7 +48,7 @@ public:
int group = -1; // no group
num_events = config_vec.size();
ids.resize(config_vec.size());
result.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, &ids[i++]);
ioctl(fd, PERF_EVENT_IOC_ID, &result[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 ids obtained earlier to be safe
// we really should be checking our result 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
@@ -1,196 +0,0 @@
#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);
}
+3 -2
View File
@@ -25,11 +25,12 @@ int main(int argc, char *argv[]) {
exit(1);
}
const char *filename = argv[1];
auto[p, err] = simdjson::padded_string::load(filename);
if (err) {
auto v = simdjson::padded_string::load(filename);
if (v.error()) {
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
@@ -1,502 +0,0 @@
#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
@@ -1,429 +0,0 @@
#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
@@ -1,51 +0,0 @@
#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
@@ -1,64 +0,0 @@
#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
@@ -1,93 +0,0 @@
#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
@@ -0,0 +1,40 @@
#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
+13 -15
View File
@@ -7,7 +7,6 @@
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
@@ -26,16 +25,15 @@ 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 std::move(value);
return 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 twitter_user read_user(ondemand::object user) {
return { user.find_field("id"), user.find_field("screen_name") };
}
static inline bool displayed_implementation = false;
};
@@ -44,15 +42,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["statuses"]) {
for (ondemand::object tweet : doc.find_field("statuses")) {
tweets.emplace_back(partial_tweets::tweet{
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"]
tweet.find_field("created_at"),
tweet.find_field("id"),
tweet.find_field("text"),
nullable_int(tweet.find_field("in_reply_to_status_id")),
read_user(tweet.find_field("user")),
tweet.find_field("retweet_count"),
tweet.find_field("favorite_count")
});
}
return true;
+33 -28
View File
@@ -1,41 +1,46 @@
#pragma once
//
// Interface
//
namespace partial_tweets {
template<typename T> static void PartialTweets(benchmark::State &state);
} // namespace partial_tweets
//
// Implementation
//
#include "json_benchmark/file_runner.h"
#include "tweet.h"
#include <vector>
#include "event_counter.h"
#include "domnoexcept.h"
#include "json_benchmark.h"
namespace partial_tweets {
using namespace simdjson;
using namespace json_benchmark;
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;
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);
}
JsonBenchmark<T, DomNoExcept>(state, json);
bool before_run(benchmark::State &state) {
if (!file_runner<I>::before_run(state)) { return false; }
result.clear();
return true;
}
bool run(benchmark::State &) {
return this->implementation.run(this->json, result);
}
template<typename R>
bool diff(benchmark::State &state, runner<R> &reference) {
return diff_results(state, result, reference.result, diff_flags::NONE);
}
size_t items_per_iteration() {
return result.size();
}
};
struct simdjson_dom;
template<typename I> simdjson_really_inline static void partial_tweets(benchmark::State &state) {
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
}
} // namespace partial_tweets
+80
View File
@@ -0,0 +1,80 @@
#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
@@ -0,0 +1,96 @@
#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
@@ -1,69 +0,0 @@
#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