Compare commits

...

1270 Commits

Author SHA1 Message Date
Daniel Lemire 2cc3b4c650 Moving it to 0.6.1. 2020-11-03 22:16:07 -05:00
Daniel Lemire 146191d791 Using decimal notation. 2020-11-03 22:09:42 -05:00
Daniel Lemire 80db053be3 Fixing build errors under legacy libc++. 2020-11-03 21:57:23 -05:00
Daniel Lemire 6a86ef5a7d Issue release 2020-10-23 09:32:25 -04:00
Daniel Lemire 500e5d6759 Update README.md 2020-10-23 09:21:16 -04:00
Daniel Lemire e3f2c9f29a Update README.md 2020-10-23 09:18:47 -04:00
Daniel Lemire 3458e6248b Update README.md 2020-10-23 09:15:04 -04:00
Daniel Lemire 14039d05a9 Adding a new benchmark for ondemand: distinct user id (#1239)
* Adding a distinct user id benchmark

* reenabling everything

* Removing an unnecessary "value()".

* Better tests of the examples and some fixes.

* Guarding exception code.
2020-10-23 08:47:01 -04:00
Daniel Lemire c592da4937 Adds yyjson to our internal benchmarks. (#1244) 2020-10-21 16:23:20 -04:00
Daniel Lemire 61fb4244f2 There are a couple of extra value() that should not be there (#1242) 2020-10-21 12:37:22 -04:00
Daniel Lemire a503e022d8 Update README.md 2020-10-21 11:43:36 -04:00
Daniel Lemire 0942dc0764 This fixes a typo and makes the types more explicit (#1241) 2020-10-20 17:41:37 -04:00
Daniel Lemire 0d6919dd99 Reenable the on-demand tests and allows us to convert a raw string into a C++ string. (#1232)
* Reenable the on-demand tests and allows us to convert a raw string into a C++ string.

* Fixing a 1-byte buffer overrun.

* More documentation.

* Adding more tests.

* Enabling the new tests

* Committing a nicer example.

* Not yet happy but this should fix our failures.

* Duh.

* Ok. Making it easier to get string_view instances from field instances.

* It is a struct.

* Trying to satisfy VS.

* Adopting John's name.
2020-10-19 20:22:24 -04:00
Daniel Lemire 3e8e797bc2 Typo. 2020-10-19 17:30:52 -04:00
Daniel Lemire 0a907ec694 Tweaking further the documentation. (#1237)
* Tweaking further the documentation.

* More details.

* Another sentence.

* Saving.

* Tweaking more
2020-10-19 16:51:04 -04:00
Paul Dreik f1b4a54991 add fuzz element (#1204)
* add definitions for is_number and tie (by lemire)
* add fuzzer for element
* update fuzz documentation
* fix UB in creating an empty padded string
* don't bother null terminating padded_string, it is done by the std::memset already
*  refactor fuzz data splitting into a separate class
2020-10-17 05:48:50 +02:00
Paul Dreik 58e7106df1 remove unused function parse_unsigned 2020-10-16 22:17:11 +02:00
Paul Dreik 7bf391c54a fix potential use of uninitialized value warning, avoid casting away const
This fixes a "potentially use of uninitialized value" warning, as well as a cstyle cast to non-const.
2020-10-16 22:14:42 +02:00
Daniel Lemire 07a6e098c8 This would allow users to find out what builtin is. (#1227)
* This would allow users to find out what builtin is.

* Trying another approach.

* Added instructions.

* Cleaning up the printout.

* Let us be less invasive.

* Adding a comment.
2020-10-15 21:58:42 -04:00
Daniel Lemire e4897d6b54 We have hardcoded 32 (#1236) 2020-10-15 21:57:10 -04:00
Daniel Lemire 23026d966b Tweaking. 2020-10-15 21:55:23 -04:00
Daniel Lemire 3cd98df30d This adds new tests regarding ordering. (#1233)
* This adds new tests regarding ordering.

* Updating the documentation with more examples.

* Adding compilation tests.

* Pruning code for exceptions.

* Guarding exceptionless.
2020-10-15 16:41:14 -04:00
Daniel Lemire 001be23258 Being more specific regarding the padding. (#1228)
* Being more specific regarding the padding.

* Even more precise.
2020-10-14 13:35:51 -04:00
Daniel Lemire c85b6682e0 This is a cleaner on-demand documentation (for discussion). (#1226)
* This is a cleaner on-demand documentation (for discussion).

* Added stable APIs.
2020-10-14 13:35:28 -04:00
Daniel Lemire bb2bc98a22 Fix issue https://github.com/simdjson/simdjson/issues/1127 (#1224) 2020-10-13 09:18:54 -04:00
Daniel Lemire 43da4f7ccc Corrected number 2020-10-12 17:59:13 -04:00
Daniel Lemire 37e6d1e9c7 new number parsing (#1222)
* Remove our dependency on strtod_l by bundling our own slow path.

* Ok. Let us drop strtod entirely.

* Trimming down the powers to -342.

* Removing useless line.

* Many more comments.

* Adding some DLL exports.

* Let the gods help those who rely on windows+gcc.

* Marking the subnormals as unlikely. This is pretty much "performance neutral", but it might help just a bit with twitter.json.
2020-10-10 12:47:49 -04:00
Paul Dreik 1d9926698e update how boost.json is invoked, fix missing separators (#1203)
* initial try at adding boost json to the benchmark

* clean up

* qualify memcpy etc. with std::

* clang format

* extra space

* update benchmark with help from Vinnie Falco from Boost.json

* add missing separators
2020-10-09 18:22:37 -04:00
Daniel Lemire b04f64e02c Simplifying slightly the logic in the cmake (#1219) 2020-10-09 18:10:58 -04:00
Daniel Lemire c6d710b14b Merge branch 'master' of github.com:simdjson/simdjson 2020-10-09 16:46:51 -04:00
Daniel Lemire 2e07850622 Updating cxxopts to latest. 2020-10-09 16:46:30 -04:00
Daniel Lemire 1b6888281e Update README.md 2020-10-09 10:54:48 -04:00
Daniel Lemire ce94411dff Tweaking the documentation to better answer https://github.com/simdjson/simdjson/issues/1218 2020-10-09 10:02:56 -04:00
Paul Dreik 58a3098cd8 fix broken fuzzing github action job (#1221) 2020-10-09 12:44:17 +02:00
Paul Dreik 8a68163905 simplify fuzzing only dynamically supported implementations (#1201)
This refactors the dynamic check of which implementations are supported at runtime.

It also reduces duplicated effort in the CI fuzzing job, the differential fuzzers don't need to run with different values of SIMDJSON_FORCE_IMPLEMENTATION.

There is also a convenience script to run the fuzzers locally, to quickly check that the fuzzers still build, run and no easy to find bugs are there. It should be handy not only when developing the fuzzers, but also when modifying simdjson.
2020-10-09 05:29:54 +02:00
Paul Dreik 1f98e64b71 fix merge conflict on master (#1217) 2020-10-07 10:05:31 +02:00
John Keiser a9480a768b Merge pull request #947 from simdjson/jkeiser/stream-parse
On-Demand Parsing
2020-10-06 16:04:27 -07:00
John Keiser ed94514fc2 Remove ondemand_basictests from Win32 for now 2020-10-06 14:38:28 -07:00
Daniel Lemire 1f41cc2030 Making it clearer that parse_many is meant for *small* documents. (#1205)
* Making it clearer that parse_many is meant for *small* documents.

* Update parse_many.md
2020-10-06 17:19:34 -04:00
John Keiser 6455ec36ec Reamalgamate 2020-10-06 11:29:46 -07:00
John Keiser 676a3d068c Add non-top-level array iteration test 2020-10-06 11:29:46 -07:00
John Keiser 5533f8d87b Add object iteration error tests 2020-10-06 11:29:46 -07:00
John Keiser 93af7b61ce Add array iteration error tests 2020-10-06 11:29:46 -07:00
John Keiser 364ad5529d Add basic error tests 2020-10-06 11:29:46 -07:00
John Keiser 1974a46fe0 Remove validate tests and unimplemented cast tests from ondemand 2020-10-06 11:29:46 -07:00
John Keiser 5327ab9903 Remove ondemand minify and format tests 2020-10-06 11:29:46 -07:00
John Keiser 235d191bae Add Twitter exception tests 2020-10-06 11:29:46 -07:00
John Keiser 4eb80ec75a Add DOM API exception tests 2020-10-06 11:29:45 -07:00
John Keiser 2900459222 Separate twitter tests from DOM API tests 2020-10-06 11:29:45 -07:00
John Keiser 9088792b0e Disable value["x"] (unsafe, convert to object first) 2020-10-06 11:29:45 -07:00
John Keiser b41fe7beab Add object indexing tests 2020-10-06 11:29:45 -07:00
John Keiser 00f9bb8a07 Add null tests 2020-10-06 11:29:45 -07:00
John Keiser a90e1637cb Add boolean tests 2020-10-06 11:29:45 -07:00
John Keiser 4bad5c0241 Add numeric value tests 2020-10-06 11:29:45 -07:00
John Keiser ce09d82fc7 Test strings 2020-10-06 11:29:45 -07:00
John Keiser 5b926b8196 Support array iteration over document 2020-10-06 11:29:45 -07:00
John Keiser c719ccdb48 Add tests for empty object/array 2020-10-06 11:29:45 -07:00
John Keiser 9f1786aeb1 Add .get(T) to value/document 2020-10-06 11:29:45 -07:00
John Keiser 0bb83e06bc Fix root number parsing 2020-10-06 11:29:45 -07:00
John Keiser 2ba67c2bc2 [WIP] && and & versions of each operator 2020-10-06 11:29:45 -07:00
John Keiser cae91983ec Fix issue with early destruction 2020-10-06 11:29:45 -07:00
John Keiser 3190ef0c1f Check benchmark results in release builds 2020-10-06 11:29:45 -07:00
John Keiser c7c1372833 Allow reuse of value to try multiple types 2020-10-06 11:29:45 -07:00
John Keiser ba02cda55f Allow direct document iteration 2020-10-06 11:29:45 -07:00
John Keiser 512a94afaa Move error to json_iterator 2020-10-06 11:29:45 -07:00
John Keiser 6d978c383a Kinder, gentler implementation selection
- Allow user to specify SIMDJSON_BUILTIN_IMPLEMENTATION
- Make cmake -DSIMDJSON_IMPLEMENTATION=haswell *only* specify haswell
- Move negative implementation selection to
-DSIMDJSON_EXCLUDE_IMPLEMENTATION
- Automatically select SIMDJSON_BUILTIN_IMPLEMENTATION if
SIMDJSON_IMPLEMENTATION is set
- Move implementation enablement mostly to implementation files
- Make implementation enablement and selection simpler and more robust
- Fix bug where programs linked against simdjson were not passed
SIMDJSON_XXX_IMPLEMENTATION or SIMDJSON_EXCEPTIONS
2020-10-06 11:29:45 -07:00
John Keiser 938678f87f Complete draft design doc 2020-10-06 11:29:45 -07:00
John Keiser 9dcf5fca5b Add ondemand rationale to beginning of document 2020-10-06 11:29:45 -07:00
John Keiser 88f0dc4726 Add API docs, ensure parser and field methods called with & 2020-10-06 11:29:45 -07:00
John Keiser 8ae7910aba Namespace documentation 2020-10-06 11:29:45 -07:00
John Keiser b70e85fd10 Only include source in bench_sax 2020-10-06 11:29:45 -07:00
John Keiser 99bc591366 Don't enable fallback test unless fallback is explicitly selected 2020-10-06 11:29:45 -07:00
John Keiser f4963cd1c5 Ensure CI builds all tested implementations 2020-10-06 11:29:45 -07:00
John Keiser 4859cb8528 Add SIMDJSON_ONDEMAND_SAFETY_RAILS 2020-10-06 11:29:45 -07:00
John Keiser c42b91980b Fix VC++ forward friend declarations 2020-10-06 11:29:45 -07:00
John Keiser 8b3c8820e0 Fix numberparsingcheck to define found_invalid_number before simdjson.h 2020-10-06 11:29:39 -07:00
John Keiser 29bc78a486 Add ondemand to arm64/fallback/westmere 2020-10-04 12:47:30 -07:00
John Keiser 30fe86ed32 Use simdjson::builtin instead of haswell/begin+end 2020-10-04 12:47:30 -07:00
John Keiser 85cefd5a00 Alias simdjson::builtin to simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION 2020-10-04 12:47:30 -07:00
John Keiser b5a328e0ca Set SIMDJSON_BUILTIN_IMPLEMENTATION to minimum supported 2020-10-04 12:47:30 -07:00
John Keiser 209a2e8fc3 Fix ARM compile 2020-10-04 12:47:30 -07:00
John Keiser 8b978e6aea Don't call functions max() (conflicts with Windows macro) 2020-10-04 12:47:30 -07:00
John Keiser b4df0e7c9e Fix domnoexcept to actually be noexcept 2020-10-04 12:47:30 -07:00
John Keiser 76aeda6b00 Fix [] on simdjson_result<value> to require temp object 2020-10-04 12:47:30 -07:00
John Keiser 1a0ad6d9c3 Fix bug document.get_xxx() 2020-10-04 12:47:30 -07:00
John Keiser 6b219e3e25 Use ::stage2 where it's needed 2020-10-04 12:47:30 -07:00
John Keiser 1e90691013 Add parse_unsigned/parse_integer/parse_double to SKIP_NUMBERPARSING 2020-10-04 12:47:30 -07:00
John Keiser 3577c87c88 Fix amalgamation with generic/ files in include/ 2020-10-04 12:47:30 -07:00
John Keiser baf6607e74 Make ondemand build without #include "simdjson.cpp" 2020-10-04 12:47:30 -07:00
John Keiser a700848bae Move ondemand implementation to include/ 2020-10-04 12:47:30 -07:00
John Keiser 9bbfd5804e Put haswell/westmere/fallback/amd64 in simdjson namespace 2020-10-04 12:47:30 -07:00
John Keiser b234d74f43 Remove unnamed namespace from ondemand 2020-10-04 12:47:30 -07:00
John Keiser cd49ff330d Fix g++-7 quickstart errors 2020-10-04 12:47:30 -07:00
John Keiser 49faf7af1a Make simdjson_result implementation-specific 2020-10-04 12:47:30 -07:00
John Keiser c892b83c93 Make ondemand classes usable from simdjson_result 2020-10-04 12:47:30 -07:00
John Keiser 985b52331a Require object to be exact and in order 2020-10-04 12:47:30 -07:00
John Keiser 021dded9dd Add Kostya benchmarks 2020-10-04 12:47:30 -07:00
John Keiser 8fd0cdc732 Iterate value without going through indirection
Avoids issues with value being released early
2020-10-04 12:47:30 -07:00
John Keiser fe7a4d42d3 Fix top level values 2020-10-04 12:47:30 -07:00
John Keiser e89d6353af Add a "sum" benchmark with no appending to vector 2020-10-04 12:47:30 -07:00
John Keiser c5bb74d184 Pave the way for non-record-based benchmarks 2020-10-04 12:47:30 -07:00
Daniel Lemire 874349c928 Making the code cleaner. 2020-10-04 12:47:30 -07:00
Daniel Lemire 157604b3a5 I think that this is better (fairer) code. 2020-10-04 12:47:30 -07:00
John Keiser b935544d65 Make benchmark output easier to follow 2020-10-04 12:47:30 -07:00
Daniel Lemire 03271df579 This adds a frequency column (useful because if the frequency tanks, then other numbers are suspect). 2020-10-04 12:47:30 -07:00
John Keiser 0633d3a07d Make branch miss numbers integers 2020-10-04 12:47:30 -07:00
John Keiser 045377a594 Fix errors with g++ 2020-10-04 12:47:30 -07:00
John Keiser b5c8030f19 Fix LargeRandom<OnDemand> 2020-10-04 12:47:30 -07:00
John Keiser 7c2072789c Fix issue with strings not including their first character 2020-10-04 12:47:30 -07:00
John Keiser f75e856d2b Compare records to ensure benchmarks work 2020-10-04 12:47:30 -07:00
John Keiser 44d689bc6e Make instructions / cycle counters more useful 2020-10-04 12:47:30 -07:00
John Keiser 9e433c2f19 Move benchmarks into their own directories 2020-10-04 12:47:30 -07:00
John Keiser 21b6279b74 Add document-level number/atom parsing 2020-10-04 12:47:30 -07:00
John Keiser 4d89076bdc Check for EOF when skipping containers
Revert that?

Or not
2020-10-04 12:47:30 -07:00
John Keiser 71e4ff7e03 Fix compile errors on C++11/noexcept 2020-10-04 12:47:30 -07:00
John Keiser d2dfda6583 Ensure iterator is kept alive while iterators are active 2020-10-04 12:47:30 -07:00
John Keiser 6a855f528b Ensure only leaf values can push the iterator 2020-10-04 12:47:30 -07:00
John Keiser fb93109c2d Use default constructors/assignment where possible 2020-10-04 12:47:30 -07:00
John Keiser bd190af7a3 Require iterators to finish cleanly and release 2020-10-04 12:47:30 -07:00
John Keiser 44268b0c6b Remove object::has_next 2020-10-04 12:47:30 -07:00
John Keiser 6451e5e7d1 Remove array::has_next 2020-10-04 12:47:30 -07:00
John Keiser 2b3c4c68e4 Ride the lightning 2020-10-04 12:47:30 -07:00
John Keiser 7030cf2433 Release object/array after finished 2020-10-04 12:47:30 -07:00
John Keiser 26d7881b80 Require value to be deleted after most value conversions 2020-10-04 12:47:29 -07:00
John Keiser 21fe42b28c Release the iterator after use 2020-10-04 12:47:29 -07:00
John Keiser d5ecf68d26 Make json_iterator_ref unique 2020-10-04 12:47:29 -07:00
John Keiser 0ddff4ec7d json_iterator * -> json_iterator_ref 2020-10-04 12:47:29 -07:00
John Keiser 283ac3191f Rename parse->iterate, add iterate_raw 2020-10-04 12:47:29 -07:00
John Keiser 4dd0c80dad Move current_string_buf_loc to json_iterator 2020-10-04 12:47:29 -07:00
John Keiser 3b53c6ca47 Use json_iterator as shared state instead of document 2020-10-04 12:47:29 -07:00
John Keiser 4e3b4809ea [WIP] Nascent design doc for on demand 2020-10-04 12:47:29 -07:00
John Keiser a90b8fb449 Remove depth tracking from ondemand api 2020-10-04 12:47:29 -07:00
John Keiser 1da509027e Add root number/atom parsing functions 2020-10-04 12:47:29 -07:00
John Keiser 5b96e4761e Remove side-effecting assumption 2020-10-04 12:47:29 -07:00
John Keiser 311ea79238 Fix noexceptions builds 2020-10-04 12:47:29 -07:00
John Keiser 98be2c91df Fix SAX benchmarks to actually push to vector 2020-10-04 12:47:29 -07:00
John Keiser 2657e5e226 Fix points SAX to actually record points 2020-10-04 12:47:29 -07:00
John Keiser cfcb0d4fb7 Use json_iterator in array/object 2020-10-04 12:47:29 -07:00
John Keiser 97d03f3215 token_iterator -> json_iterator 2020-10-04 12:47:29 -07:00
John Keiser 4065529bdf Don't try to compile Haswell benchmarks on ARM 2020-10-04 12:47:29 -07:00
John Keiser 0a6260b1d8 Fix clang 6 compile issue 2020-10-04 12:47:29 -07:00
John Keiser 12caf2510e Mark unused variables 2020-10-04 12:47:29 -07:00
John Keiser a58d2f710d Fix C++11 error 2020-10-04 12:47:29 -07:00
John Keiser 6be2db8c42 Fix SAX benchmark to actually add tweets 2020-10-04 12:47:29 -07:00
John Keiser 5cf68416d8 Don't bother comparing field names in parserandom 2020-10-04 12:47:29 -07:00
John Keiser ebcb3c6b3b On-demand parse implementation 2020-10-04 12:47:29 -07:00
Paul Dreik 04267e0f6b add boost.json to benchmark (#1202)
Add boost.json to the benchmark.
It was accepted into boost 20201003, see https://lists.boost.org/Archives/boost/2020/10/250129.php.

The upstream repo is (expected to eventually be migrated to boost): https://github.com/CPPAlliance/json
2020-10-04 10:00:09 +02:00
Daniel Lemire a540e6afc5 Testing on minimalist alpine (linux) images (#1200)
* Tweaking header includes to make it safer.

* Adding the actual tests.

* Fixing my syntax.
2020-10-02 13:32:09 -04:00
Daniel Lemire f1841e48b3 Minor fixes to some headers (tweak) (#1198) 2020-10-02 12:29:05 -04:00
Daniel Lemire 9865bb6904 Make it possible to check that an implementation is supported at runtime (#1197)
* Make it possible to check that an implementation is supported at runtime.

* add CI fuzzing on arm 64 bit

This adds fuzzing on drone.io arm64

For some reason, leak detection had to be disabled. If it is enabled, the fuzzer falsely reports a crash at the end of fuzzing.

Closes: #1188

* Guarding the implementation accesses.

* Better doc.

* Updating cxxopts.

* Make it possible to check that an implementation is supported at runtime.

* Guarding the implementation accesses.

* Better doc.

* Updating cxxopts.

* We need to accomodate cxxopts

Co-authored-by: Paul Dreik <github@pauldreik.se>
2020-10-02 11:04:51 -04:00
Paul Dreik e06ddea784 add CI fuzzing on arm 64 bit
This adds fuzzing on drone.io arm64

For some reason, leak detection had to be disabled. If it is enabled, the fuzzer falsely reports a crash at the end of fuzzing.

Closes: #1188
2020-10-01 10:12:37 +02:00
Daniel Lemire 8b5a89c136 Parsing floats with 19 significant digits should be fine. (#1191)
* Parsing floats with 19 significant digits should be fine.

* Adding more tests with very long mantissa.
2020-09-29 19:42:43 -04:00
Daniel Lemire da093c1982 Fixing "undefined behavior" issue in new fast_itoa functions (#1186)
* Fixing "undefined behavior" issue.

* Simplifying our custom atoi

* Fixing minor bug
2020-09-29 19:17:03 -04:00
Daniel Lemire 048fb6278a This adds two tests to verify a new fuzzer issue. (So far I could not verify.) (#1194) 2020-09-29 11:45:41 -04:00
Paul Dreik f1b0778f79 add utf8 fuzzer
This enables the utf8 fuzzer, now when #1187 is fixed
2020-09-27 21:11:13 +02:00
Daniel Lemire 0e584fa4a5 Attempt to fix issue 1187. (#1192) 2020-09-27 12:04:47 -04:00
Paul Dreik f44386008c add minifier fuzzers (#1172)
This adds a minifier fuzzer. There is also an utf-8 fuzzer, but it is disabled until  #1187 is fixed.

Run all fuzzers bug the utf-8 one in the github CI fuzz.
2020-09-26 14:25:00 +02:00
Daniel Lemire 60c139a844 Faster and more correct serialization (#1168)
* Adding new files.

* Better.

* Fixing minifier and adding tests.

* Adding benchmarks.

* Including the array header.

* Replacing old stream-based code by the new code.

* Doubling up the itoa.

* Hidden away to_chars in internal namespace.

* Removing the repetitions.

* Documented the atoi functions.

* Tuning the escape sequences.

* Moving the operators off the main namespace.

* Added more tests.

* Tweaking the implementation so that it works with and without exp.

* The string_builder template and mini_formatter class
 are not part of  our public API and are subject to change
 at any time!

* Adding a benchmark and some optimization.

* Cleaning.

* Strictly speaking, this header is needed.
2020-09-23 10:00:39 -04:00
Daniel Lemire f410213003 Improve documentation on padding
- Improves and clarifies the documentation on padding.
 - Use std:: prefix for memcpy, strlen etc.

Related to issues #1175 and #1178
2020-09-23 09:07:14 +02:00
Daniel Lemire 19cb5d57db Some minor documentation fixes. (#1177) 2020-09-17 13:17:35 -04:00
Paul Dreik 30b912fc81 fuzz at_pointer
This adds a fuzzer for at_pointer() which recently had a bug.

The #1142 bug had been found with this fuzzer

Also, it polishes the github action job:

    cross pollinate the fuzzer corpora (lets fuzzers reuse results from other fuzzers)
    use github action syntax instead of bash checks
    only run on push if on master
2020-09-16 21:17:43 +02:00
Daniel Lemire 7fc07e2d5e Correcting typo 2020-09-16 11:11:49 -04:00
Daniel Lemire 72c83d9430 This avoids locale-dependent number parsing at the standard library level (#1157)
* This avoids locale-dependent number parsing at the standard library level.

* Adding missing cast.

* Inserting the missing "endif"

* Trial and error.

* Another attempt.

* Another tweak.

* Another fix.

* Restricting it even more.

* Tweaking our symbol checks.

* Somewhat smarter tests.

* Nice comments.

* Minor simplification.

* Adding cerr.
2020-09-15 11:36:18 -04:00
Daniel Lemire bfbac12f76 We were forgetting to check the end bytes at the end of the UTF8 validation. (#1173)
* We were forgetting to check the end bytes at the end of the UTF8 validation.

* Silencing the sanitizer

* Better explanation.
2020-09-15 11:33:09 -04:00
Daniel Lemire 461f7dc9f9 Remove unnecessary comment. 2020-09-14 10:44:10 -04:00
Daniel Lemire 3e5497e2f9 Fixes issue 1170 and makes the usage of minify easier. (#1171)
* Fixes issue 1170 and makes the usage of minify easier.

* This should get the fallback implementation to detect unclosed strings.
2020-09-12 16:20:20 -04:00
Paul Dreik 6ecbcc7c19 add multi implementation fuzzer (#1162)
This adds a fuzzer which parses the same input using all the available implementations (haswell, westmere, fallback on x64).

This should get the otherwise uncovered sourcefiles (mostly fallback) to show up in the fuzz coverage.
For instance, the fallback directory has only one line covered.
As of the 20200909 report, 1866 lines are covered out of 4478.

Also, it will detect if the implementations behave differently:

    by making sure they all succeed, or all error
    turning the parsed data into text again, should produce equal results

While at it, I corrected some minor things:

    clean up building too many variants, run with forced implementation (closes #815 )
    always store crashes as artefacts, good in case the fuzzer finds something
    return value of the fuzzer function should always be 0
    reduce log spam
    introduce max size for the seed corpus and the CI fuzzer
2020-09-11 23:46:22 +02:00
John Keiser 8cef02e8e8 Merge pull request #1167 from simdjson/jkeiser/isolate-checkperf-more
Isolate checkperf more
2020-09-11 11:58:37 -07:00
John Keiser caabfd14b3 Isolate checkperf more 2020-09-11 08:53:41 -07:00
Daniel Lemire 2ffbaa9578 This will isolate the perf checks in CI (#1164)
* This will isolate the perf checks.

* Fixed typo
2020-09-10 18:15:45 -04:00
Daniel Lemire c40aeaec3a Fix for issue 1147 (#1153)
* This must be a typo

* Improving documentation of the string conversion.

* Minor update.
2020-09-03 13:18:15 -04:00
John Keiser 80e84a3ad0 Merge pull request #1143 from simdjson/jkeiser/classify
Simplify operator classification lookup on Intel
2020-09-03 10:08:14 -07:00
Daniel Lemire 0552335ec1 Fixing the issue. (#1151) 2020-09-02 18:41:59 -04:00
Daniel Lemire 7aea774b21 Adding a tests and a fix for empty strings in at_pointer (#1148)
* Adding a test.

* More tests.
2020-09-02 17:04:56 -04:00
Daniel Lemire 4d4ed92055 Removes 5 KB of tables in the number parsing routine (#1139)
* Removes 5 KB of tables at the expense, and a load, at the expense
of a multiplication and a shift. I have not benchmarked this new
code, but my expectation is that it should be largely performance
neutral. The motivation is to reduce the size of the library slightly.
There is also a matter of elegance.
2020-09-02 15:47:11 -04:00
John Keiser f0ec26992a Remove bit_or (bad perf on Windows) 2020-09-01 08:43:09 -07:00
John Keiser 62e8332b34 Use simd8x64 abstractions in classification 2020-09-01 08:43:09 -07:00
John Keiser 0925f71987 Simplify operator classification lookup on Intel 2020-09-01 08:43:07 -07:00
Daniel Lemire 4c11652808 This must be a typo (#1140) 2020-08-28 20:35:13 -04:00
Daniel Lemire 5b10c38e43 Make parse_many safer. (#1137) 2020-08-20 22:22:46 -04:00
Daniel Lemire 3316df9195 Adding test for issue 1133 and improving documentation (#1134)
* Adding test.

* Saving.

* With exceptions.

* Added extensive tests.

* Better documentation.

* Tweaking CI

* Cleaning.

* Do not assume make.

* Let us make the build verbose

* Reorg

* I do not understand how circle ci works.

* Breaking it up.

* Better syntax.
2020-08-20 14:03:14 -04:00
Daniel Lemire 5d355f1a8b release candidate (#1132) 2020-08-19 18:12:23 -04:00
John Keiser 2ff91103ca Remove SIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT (#1131) 2020-08-19 17:11:13 -04:00
Daniel Lemire a954d50ad4 This improves our documentation. (#1128)
* This improves our documentation.

* Removing tags for doxygen.

* You need a recent cmake remark.
2020-08-19 14:02:08 -04:00
John Keiser 5be4d37aff Merge pull request #1129 from simdjson/jkeiser/inl
Move inline/* to *-inl.h
2020-08-19 09:52:34 -07:00
John Keiser 1e6c9dbcfa Reamalgamate 2020-08-19 09:16:25 -07:00
John Keiser 708a56872d Move inline/* to *-inl.h 2020-08-19 09:09:31 -07:00
John Keiser 0a2bca3f73 Merge pull request #1101 from simdjson/jkeiser/yakety-sax
Basic SAX interface with benchmarks
2020-08-19 09:05:44 -07:00
Daniel Lemire 1ec710c985 Updating the documentation for hackers. 2020-08-19 10:59:24 -04:00
Daniel Lemire d5a44f9ad4 Merge branch 'master' of github.com:simdjson/simdjson 2020-08-19 10:36:01 -04:00
Daniel Lemire e64dca7144 Tweaking. 2020-08-19 10:35:49 -04:00
John Keiser b2779c35df Fix issue with unsupported unreachable on Windows 2020-08-18 21:35:12 -07:00
John Keiser 9b11e119d4 Make skip_double() comment more explicit 2020-08-18 21:25:03 -07:00
John Keiser 988c62baed Encapsulate significant_digits() 2020-08-18 21:25:03 -07:00
John Keiser eb3e640003 Return bool from compute_float_64 2020-08-18 21:25:03 -07:00
John Keiser 9475b947f5 Return error codes from parse_number 2020-08-18 21:25:03 -07:00
John Keiser 18564f1ae2 Don't benchmark unless haswell is available 2020-08-18 21:25:03 -07:00
John Keiser 638f1deb62 Add DOM tweet reader for comparison 2020-08-18 21:25:03 -07:00
John Keiser 7e74d30f45 [WIP] tweet reader SAX benchmark 2020-08-18 21:25:03 -07:00
John Keiser ce8d0f8135 Add visit_primitive() helper in iterator 2020-08-18 21:25:03 -07:00
John Keiser 872127b722 Move is_array management together with depth 2020-08-18 21:25:03 -07:00
John Keiser e180dc44bc Move container logging into json_iterator 2020-08-18 21:25:03 -07:00
John Keiser 268b8845a9 Document tape_builder 2020-08-18 21:25:03 -07:00
John Keiser 74c47995a3 Document json_iterator 2020-08-18 21:25:03 -07:00
John Keiser 24f5936cbf Give is_array responsibility to json_iterator 2020-08-18 21:25:03 -07:00
John Keiser bdfa8aca28 Separate interface from implementation to make interface clearer 2020-08-18 21:25:03 -07:00
John Keiser 15eb1ad922 Preface visitor methods with visit() 2020-08-18 21:25:03 -07:00
John Keiser 6ec98ee8b1 Add error codes to all things 2020-08-18 21:25:03 -07:00
John Keiser c5862d6de9 Remove empty_object/empty_array 2020-08-18 21:25:03 -07:00
John Keiser 57eb55446f Cache string value locally 2020-08-18 21:25:03 -07:00
John Keiser abd1399a7f Don't check depth at the end (unnecessary check) 2020-08-18 21:25:03 -07:00
John Keiser 57eba21ee5 Fall through goto labels where possible 2020-08-18 21:25:03 -07:00
John Keiser 1b56211a70 Give start_*/end_* error codes 2020-08-18 21:25:03 -07:00
John Keiser d8974d53b2 Keep value around between states 2020-08-18 21:25:03 -07:00
John Keiser 5ecd17f49e Log unconsumed input as an error 2020-08-18 21:25:03 -07:00
John Keiser 6bb99aec3c Merge structural_parser+iterator into json_iterator 2020-08-18 21:25:03 -07:00
John Keiser a67e83e24e Remove parse_* from visitor method names 2020-08-18 21:25:03 -07:00
John Keiser 5a3c3134ec Move value into common place to be shared across states 2020-08-18 21:25:03 -07:00
John Keiser ce8b9ee8c4 Move finish() out to walk_document 2020-08-18 21:25:03 -07:00
John Keiser 970dfc9f67 builder -> visitor, parser -> iter 2020-08-18 21:25:03 -07:00
John Keiser 04d39c0961 Make tape_builder primary entry point to stage 2 2020-08-18 21:25:01 -07:00
John Keiser d6339aa015 Set is_array in builder 2020-08-18 17:41:48 -07:00
John Keiser 11076bf337 containing_scope -> open_container 2020-08-18 17:41:16 -07:00
Daniel Lemire 8a8eea53a2 Prefixing macros (issue 1035) (#1124)
* Renaming partially done.

* More prefixing.

* I thought that this was fixed.

* Missed one.

* Missed a few.

* Missed another one.

* Minor fixes.
2020-08-18 18:25:36 -04:00
Daniel Lemire 09bd7e8ef8 Verification and fix for issue 1063 (JSON Pointers) (#1064)
* Specification is not followed.

* Fixes.

* Do not pass string_view by reference.

* Better documentation.

* The example is written for exceptions.

* Better documentation.

* Updating with deprecation.

* Updating example.

* Updating example.
2020-08-18 17:23:18 -04:00
John Keiser 9356619380 Merge pull request #1110 from simdjson/jkeiser/number-corruption
Fix potential buffer overrun with heavily customized input and padding
2020-08-18 14:17:25 -07:00
Daniel Lemire fc15147cf5 This allows the users to disable threading. (#1122)
* This allows the users to disable threading.

* This would disable bash scripts under FreeBSD. (#1118)

* This would disable bash scripts under FreeBSD.

* Let us also disable GIT.

* Let us try to just disable GIT

* Nope. We must have both bash and git disabled.

* This allows the users to disable threading.
2020-08-18 16:43:08 -04:00
John Keiser ab6b7a8044 Make last_structural() helper 2020-08-18 10:12:42 -07:00
John Keiser 07c2fe726e Fail if hash is unclosed at start 2020-08-18 10:10:01 -07:00
John Keiser fa355603fb Add test for corruption while parsing a number 2020-08-18 10:10:01 -07:00
Daniel Lemire 109bb505d8 Adding a new test file. (#922)
* Adding a new test file.

* Renaming.
2020-08-18 10:42:45 -04:00
Daniel Lemire 4a6eebc0e4 This corrects a small typo in the documentation. (#1121)
* This corrects a small typo in the documentation.

* Modifying the test as well.
2020-08-18 08:36:15 -04:00
PavelP 78ce2b473e Copy README.md/amalgamate_demo.cpp only if output dir isn't the same as ${SCRIPTPATH} (#1113)
+ use test's `-ef` file operator to check if ${SCRIPTPATH} and ${AMALGAMATE_OUTPUT_PATH} are not the same
2020-08-18 08:35:21 -04:00
Daniel Lemire 6beb5f5587 This adds debug tests in CI. (#1123)
* This adds debug tests in CI.

* Removing silly labels.

* Typo
2020-08-17 13:36:30 -04:00
Daniel Lemire 501fed6c4f This would disable bash scripts under FreeBSD. (#1118)
* This would disable bash scripts under FreeBSD.

* Let us also disable GIT.

* Let us try to just disable GIT

* Nope. We must have both bash and git disabled.
2020-08-17 11:50:57 -04:00
Daniel Lemire b1bd8e9ee2 Update issue templates 2020-08-17 08:16:39 -04:00
Daniel Lemire daeca1bb18 Basics. (#1116) 2020-08-14 17:28:09 -04:00
Daniel Lemire 17f6d5208f Documenting and fixing the case where a string is immediately followed by a scalar (#1106)
* Documenting and fixing.

* More cleaning.

* Being a bit cleaner.
2020-08-14 16:19:57 -04:00
John Keiser bee4d7a12b Merge pull request #1108 from simdjson/jkeiser/ncgdoc
Remove information about nonexistent computed gotos :)
2020-08-12 10:39:20 -07:00
Daniel Lemire f32e3e0c7c Please don't use GNU-specific flags. (#1109) 2020-08-11 09:12:39 -04:00
John Keiser 1b69612246 Remove information about nonexistent computed gotos :) 2020-08-10 16:29:24 -07:00
Daniel Lemire 9e93509a56 Fix number parsing (too lenient). (#1107)
* Fix number parsing (too lenient).

* Minor tweak.

* These are Booleans.

* Tweaking test config
2020-08-10 18:10:11 -04:00
Daniel Lemire ef45cd3342 Let us be explicit about standard compliance (#1099)
* Let us be explicit about standard compliance

* More explicit.
2020-08-06 18:24:36 -04:00
Daniel Lemire 46fe2e6b44 Update README.md 2020-08-06 18:03:10 -04:00
John Keiser 1133c2cc1d Merge pull request #1070 from pps83/bugfix/Do-not-update-README-and-amalgamate_demo
Do not update README.md and amalgamate_demo.cpp from amalgamate.sh
2020-08-06 12:43:56 -07:00
Daniel Lemire fde10553e0 Update HACKING.md 2020-08-06 15:06:31 -04:00
Daniel Lemire 83615ff351 Fixes issue 1088 (#1096) 2020-08-06 11:42:13 -04:00
Pavel P 352eb4cb6d Do not update README.md and amalgamate_demo.cpp from amalgamate.sh
README.md and amalgamate_demo.cpp are always the same, there is no point to keep a copy and recreate them from amalgamate.sh
2020-08-06 09:11:20 +06:00
Daniel Lemire 75c75ac00c This is dead code. (#1095) 2020-08-05 16:48:10 -04:00
John Keiser d9bcf52db2 Merge pull request #1062 from simdjson/jkeiser/reamalgamate
Fix C++ 20 compilation
2020-08-04 21:08:24 -07:00
Daniel Lemire 43f0362e6d Update README.md 2020-08-04 19:44:52 -04:00
John Keiser ed5e313c73 Reamalgamate 2020-08-04 13:17:23 -07:00
John Keiser c0010f60e6 Add view support to simdjson_result<array/object> 2020-08-04 13:17:23 -07:00
John Keiser 75301e4cf5 Fix C++20 compilation 2020-08-04 13:17:23 -07:00
John Keiser 875c8fdcbe Merge pull request #1071 from pps83/quick-example-at-the-top-of-simdjson.h
Add a quick example at the top of simdjson.h
2020-08-04 11:07:21 -07:00
John Keiser a0b1642dc0 Merge pull request #1090 from simdjson/jkeiser/sax
Split stage 2 in SAX fashion
2020-08-04 10:58:59 -07:00
John Keiser 1d7e54f8c9 Merge pull request #1089 from simdjson/jkeiser/stage2misc
Special case empty objects and arrays
2020-08-04 10:57:11 -07:00
Daniel Lemire 77c8581bc0 Flipping the flags 2020-08-04 12:59:23 -04:00
John Keiser 5dd625916b Decrement depth just before checking 2020-08-03 23:09:21 -07:00
John Keiser e3d7718cf3 Simplify value switch statements 2020-08-03 23:09:21 -07:00
John Keiser 9eccd7b1fb Inline start_object/start_array 2020-08-03 23:09:21 -07:00
John Keiser 5b05d126b4 Consolidate start_object calls 2020-08-03 23:09:20 -07:00
John Keiser 03aaf189c1 Use parse_primitive (negative perf!) 2020-08-03 23:09:20 -07:00
John Keiser 6ef9395419 "parser.parser" -> "parser.dom_parser" 2020-08-03 23:09:20 -07:00
John Keiser 3a56e13b78 Make parse() a method 2020-08-03 23:09:19 -07:00
John Keiser ec28acba3d De-templatize stage2::structural_parser 2020-08-03 23:09:15 -07:00
John Keiser ee6647ce40 Make parse part of structural_parser 2020-08-03 17:50:51 -07:00
John Keiser 03d54f8f6e Use SAX model for stage 2 2020-08-03 17:50:51 -07:00
John Keiser 553e6d7549 Don't check max depth on startup 2020-08-03 17:49:14 -07:00
John Keiser e6896ee71e Keep current JSON after checking primitive type 2020-08-03 13:30:13 -07:00
John Keiser e6762f9b48 Advance immediately upon evaluating a character 2020-08-03 13:26:56 -07:00
John Keiser 099bb1afef Pass buffer to primitive parse functions 2020-08-03 12:56:35 -07:00
John Keiser 9c33093c91 Name goto labels consistently 2020-08-03 11:47:38 -07:00
John Keiser 634d8038b9 Increment depth before starting a scope 2020-08-03 11:35:46 -07:00
John Keiser ad46154f2f Hardcode document start/end creation 2020-08-03 10:23:32 -07:00
Daniel Lemire c7fbb4615c Update README.md 2020-08-03 12:51:21 -04:00
John Keiser fa81068ea8 Simplify structural_parser.start() 2020-08-03 09:49:15 -07:00
John Keiser 70c2a1c9f9 Short-circuit empty objects/arrays 2020-08-03 09:36:18 -07:00
Pavel P 164fcb49d9 + reformat sample code 2020-08-03 19:48:41 +06:00
John Keiser 64cf18aa1e Merge pull request #1079 from simdjson/jkeiser/no-error
[3/3] Return errors immediately instead of using goto
2020-08-02 12:22:53 -07:00
John Keiser 66a68ce264 Return errors immediately instead of using goto 2020-08-02 12:04:12 -07:00
John Keiser 86162aaddb Merge pull request #1078 from simdjson/jkeiser/no-computed-goto
[2/3] Remove computed GOTOs
2020-08-02 11:49:35 -07:00
John Keiser 9cc7a94a94 Merge pull request #1065 from simdjson/jkeiser/anonymous-namespace
[1/3] Wrap simdjson kernels in anonymous namespaces
2020-08-02 11:37:11 -07:00
John Keiser 6bca1225e6 Add unlikely in strategic places 2020-08-01 18:19:36 -07:00
John Keiser 379a4e6a01 namespace { -> unnamed namespace 2020-08-01 14:46:23 -07:00
John Keiser 460cfcaf3e Make parse_structurals inline 2020-08-01 14:43:50 -07:00
John Keiser 8e69103822 Remove computed GOTO 2020-08-01 14:43:50 -07:00
John Keiser 2f67dab2b6 Remove extraneous machine addresses 2020-08-01 14:43:50 -07:00
John Keiser bb65ebd8be Remove computed gotos from parse_value 2020-08-01 14:43:50 -07:00
John Keiser c46ea0390c Move { and [ to the start of the switch 2020-08-01 14:43:50 -07:00
John Keiser bc8a6dd2e3 Remove dead code 2020-08-01 14:43:10 -07:00
John Keiser b1478c37f6 Fix arm64 build 2020-08-01 14:43:10 -07:00
John Keiser 4e944a9f3c Eliminate unused functions in fallback 2020-08-01 14:43:10 -07:00
John Keiser c7fa9b5fe8 Make entire implementation namespaces anonymous 2020-08-01 14:43:10 -07:00
John Keiser 65148b123b Put anonymous namespace in front of everything 2020-08-01 14:43:10 -07:00
Daniel Lemire 2f92a34bb7 Turns out that passing dom::element by reference can be a performance killer. (#1086)
* Turns out that passing dom::element by reference can be a performance killer.

* Tweaking.
2020-08-01 10:31:47 -04:00
Daniel Lemire 54ed24f481 Update README.md 2020-07-31 15:47:42 -04:00
Daniel Lemire 268df9f67a Update basics.md 2020-07-31 15:43:34 -04:00
Daniel Lemire 84dc398d32 Adding a couple of tests. 2020-07-31 15:29:10 -04:00
Daniel Lemire f6a3205d10 Avoid allocations in global objects (#1082) 2020-07-30 13:14:56 -04:00
Christoph Reiter 522cb66582 Make MSYS2 CI jobs actually use MSYS2 (#1081)
The jobs were executed in powershell using the globally installed cmake.
This makes things actually run in a MSYS2 shell.

This also removes the msys/cygwin job because it doesn't build
(it complains about undeclared posix_memalign)
2020-07-29 19:34:14 -04:00
Daniel Lemire f873a140ce Tweaking 2020-07-27 16:24:27 -04:00
PavelP 36dfc5bbd1 Add missing strings.h include for non-windows builds (required by strcasecmp) (#1067)
addresses #1066
2020-07-27 16:23:06 -04:00
Daniel Lemire f80668e87f This removes the crazy alignment requirements. (#1073)
* This removes the crazy alignment requirements.
2020-07-27 16:19:01 -04:00
Daniel Lemire dcb5d47ee6 Being clearer. 2020-07-26 15:58:35 -04:00
PavelP e95c22eb21 Add MSYS target for msys2 github workflow (#1075)
* Add MSYS target for msys2 github workflow

* Minimized differences in mingw/mingw64 CI workflows
2020-07-26 15:33:57 -04:00
Daniel Lemire 9fb83e61ea We want the tests to run on PR. 2020-07-26 14:39:47 -04:00
Pavel P 1513cdf7bc Add a quick example at the top of simdjson.h 2020-07-26 14:31:03 -04:00
Daniel Lemire e33af1a3f8 Adding strings.h header. (#1074) 2020-07-25 15:27:19 -04:00
Daniel Lemire 857d77a10a Adding msys2 tests. 2020-07-25 14:32:30 -04:00
PavelP 0e431a0250 Use Unix line endings for c/c++ code (#1069)
This is required for amalgamate.sh to produce correct results
2020-07-25 13:53:31 -04:00
John Keiser 3acfc0b630 Merge pull request #1045 from simdjson/jkeiser/generic-2
Define namespaces inside generic files
2020-07-24 12:42:39 -07:00
Daniel Lemire 2ce5f69def fix recently introduced overflow (#1060)
* Various fixes.

* Clearer comment.
2020-07-24 13:59:24 -04:00
John Keiser 7d347be902 Untangle amalgamated headers 2020-07-24 02:56:41 -07:00
John Keiser a456d78fe0 really_inline more things 2020-07-24 02:56:41 -07:00
John Keiser bf67c967d6 Inline jsoncharutils per-implementation 2020-07-24 02:56:41 -07:00
John Keiser 44b7a7145c Include bitmanip/simd everywhere 2020-07-24 02:56:39 -07:00
John Keiser 3867ee71ed Include files where they are used 2020-07-24 02:56:37 -07:00
John Keiser 464f4813e3 Define namespaces inside generic files 2020-07-24 02:56:36 -07:00
John Keiser af8b52e7e8 Target region for entire compilation of an implementation 2020-07-24 02:48:25 -07:00
Daniel Lemire 796588900c Reenabling C++ 20 features. (#1059) 2020-07-21 18:12:04 -04:00
Daniel Lemire 4beb2ed507 Make simd8 64 uncopyable and other Visual Studio optimizations (#1031)
* Working on making simd8x64 immutable


* Even less invasive
2020-07-21 18:11:21 -04:00
Daniel Lemire 0ff6833e96 Update basics.md 2020-07-21 17:29:10 -04:00
Daniel Lemire e2cfcc52b3 Disabling cxx20 (#1058)
* Disabling C++ 20 features.

* Updating single-header.
2020-07-21 17:15:31 -04:00
Daniel Lemire fc8a46025e Better documentation of default_batch_size (#1056)
* Better documentation of default_batch_size

* Retweaking.
2020-07-21 15:15:21 -04:00
Joe Jevnik d2bea0c228 Add support for C++ 20 ranges. (#1050)
C++ 20 adds a new feature called "ranges", which provides components for dealing
with sequences of values: https://en.cppreference.com/w/cpp/ranges.

A range is like a normal object containing `begin` and `end`, except there are
also composable operations like maps, filters, joins, etc.
The iterator objects returned by a range's `begin` and `end` require a more
strict set of operations than is needed for a range-for loop.

This PR adds the extra operations needed to support turning `dom::array` and
`dom::object` into a range.
This PR does not depend on any C++ 20 behavior, the added operators are all
valid C++ 11, and are already part of the LegacyIterator concepts.
This PR adds extra code behind: `#if defined(__cpp_lib_ranges)` guards, which is
the new C++ 20 specified feature test macro for ranges support. When ranges
support is detected, extra compile time checks are added to ensure that
`dom::array` and `dom::object` satisfy the range concept. No runtime tests have
been added yet because these compile time checks should be sufficient.

If desired, the `static_assert` code could be moved out of the actual code
headers and put into a test file.
2020-07-21 13:27:39 -04:00
Daniel Lemire f016c2b72f Update README.md 2020-07-21 12:02:30 -04:00
Daniel Lemire be62058696 Adds some C++20 tests (only headers). (#1053)
* Adds some C++20 tests (only headers).

* Tweaking.
2020-07-21 10:37:52 -04:00
Daniel Lemire af18d5ed81 This adds a validation benchmark (#1040) 2020-07-20 18:56:39 -04:00
Daniel Lemire e9c91a1ce2 lookup4 (new UTF-8 validation) (#993)
* lookup4

* Self-document lookup4 and clean up extra bits

* Maintenance, to match against upcoming PR.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
Co-authored-by: John Keiser <john@johnkeiser.com>
2020-07-20 18:20:07 -04:00
Daniel Lemire 29767b2886 Moving gcc 7 out of circle ci (#1052) 2020-07-20 17:49:55 -04:00
Daniel Lemire 96a31c69c5 Update README.md 2020-07-17 15:41:54 -04:00
Daniel Lemire 534632dc52 Minor tweak on number parsing (#1041)
* Tweak.
2020-07-17 12:14:10 -04:00
Daniel Lemire 8bf5f3d869 Trying to document more carefully the use of memcpy. (#1038)
* Trying to document more carefully the use of memcpy.

* Patching spelling.
2020-07-17 09:58:34 -04:00
Daniel Lemire c4f92322f5 Update README.md 2020-07-17 09:27:19 -04:00
Vitaly Baranov 1e4aa116e5 Choose active implementation only once. (#1044) 2020-07-16 18:17:56 -04:00
John Keiser 90cc1411da Merge pull request #1018 from simdjson/jkeiser/simplify-integer-parse
Remove some branches from number parsing
2020-07-16 12:21:43 -07:00
Daniel Lemire d13ce6768c Update README.md 2020-07-16 13:05:28 -04:00
gerrymanoim fd4a7f2150 DOC: Add another python binding to README (#1043) 2020-07-16 13:04:18 -04:00
Vitaly Baranov 6bd64c6873 Fix clang warning -Wused-but-marked-unused. (#1042)
* Fix clang warning -Wused-but-marked-unused.

* Fix build.
2020-07-15 13:28:51 -04:00
Daniel Lemire ba58d868e5 Update performance.md 2020-07-14 15:00:31 -04:00
Ben McMorran c50799ba3b Fix TOC links in basics documentation
The "++" in "C++" gets stripped from the generated anchors, so the links in the table of contents didn't work.
2020-07-13 17:02:35 -04:00
Daniel Lemire 039d82ff1b Returning basictests to its original function: basic tests (only) (#1010)
* The initial motivation behind basictests was for a quick set of sanity tests to check whether your code made sense. It
was not meant for thorough testing to find corner cases. However, over time, it grew to include such expensive tests.
This PR takes them out. It also allows us to bring back basictests to MinGW tests, since it is now cheap.

This is not an exercise in software engineering and making things prettier. This is a pragmatic change to improve our
test coverage and quality of life.

* Adds many more cheap tests.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-07-13 09:39:35 -04:00
Vitaly Baranov a2f0933d01 Fix undefined behavior: load of misaligned address in atomparsing.h (#1037) 2020-07-13 08:46:52 -04:00
Daniel Lemire 77e1e3cc18 Update performance.md 2020-07-12 18:35:15 -04:00
Daniel Lemire 7bdd41350a Update performance.md 2020-07-12 18:31:45 -04:00
John Keiser 6797a6ab56 Use const uint8_t * in number parsing 2020-07-10 09:17:23 -07:00
John Keiser 86b5928f5e Use parse_digit for decimal and exp parsing as well 2020-07-10 09:16:43 -07:00
John Keiser 6dbd15aa71 Move SIMDJSON_SKIPNUMBERPARSING method out 2020-07-09 15:55:10 -07:00
John Keiser 22e5b081c4 Remove is_integer 2020-07-09 15:55:10 -07:00
John Keiser d848f33c48 Simplify integer parsing 2020-07-09 15:55:10 -07:00
John Keiser c64367536d Eliminate "found_minus" parse_number() parameter 2020-07-09 15:55:09 -07:00
John Keiser fc0102b079 Use common parse_digit() funtion in int parsing 2020-07-09 15:33:22 -07:00
Daniel Lemire 62a39639c2 Update performance.md 2020-07-09 11:47:33 -04:00
Daniel Lemire 158aaff384 Update performance.md 2020-07-09 11:46:35 -04:00
Daniel Lemire fd836145fe Update performance.md 2020-07-09 11:45:47 -04:00
Daniel Lemire 697bafdd0a Update performance.md 2020-07-08 08:32:41 -04:00
Daniel Lemire 9675dcac44 Update performance.md 2020-07-06 19:03:18 -04:00
Daniel Lemire 48849d7866 Update README.md 2020-07-06 18:59:45 -04:00
Daniel Lemire d0ce2f0b5a Fixing clang under visual studio (#1028)
* Lots of fixes

* Removing some lambdas

* Removing some functional programming.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-07-06 18:58:19 -04:00
Daniel Lemire a19f635a6a clang is busted under appveyor (#1029)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-07-06 18:28:23 -04:00
Daniel Lemire 676ed59342 Adding more github actions (#1027)
* Adding more github actions


Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-07-06 18:20:06 -04:00
Daniel Lemire 4015f46b7d Adding a new flag to tell Visual Studio to include debugging information (#1026)
in the release builds. This makes it easier to profile inside Intel
VTune when needed.
2020-07-06 17:53:57 -04:00
Daniel Lemire 770cee7139 Fail when we "force inline" and visual studio bypasses us. (#1025) 2020-07-06 17:53:32 -04:00
Daniel Lemire a4619a54a7 Update README.md 2020-07-04 11:53:53 -04:00
Daniel Lemire f7d99f97a3 Update performance.md 2020-07-04 11:52:40 -04:00
Daniel Lemire 8b7df0c12e Update performance.md 2020-07-03 23:14:01 -04:00
Daniel Lemire bd780817f7 Update performance.md 2020-07-02 15:33:36 -04:00
John Keiser 82fb45aa2a Merge pull request #990 from simdjson/jkeiser/fast-large-integer
Don't reparse large integers
2020-07-01 12:49:43 -07:00
Daniel Lemire 74870a8189 Fixing issue 1013. (#1016)
* Fixing issue 1013.

* Bumping to 0.4.6

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-07-01 14:14:51 -04:00
John Keiser 7a9f6b48f4 Replace TODOs with comments about why we DIDNTDO 2020-07-01 10:31:10 -07:00
John Keiser d3c089130d Check overflow without reparsing integers 2020-07-01 09:51:48 -07:00
John Keiser e0f3060527 Add negative/positive integer writing 2020-07-01 09:51:48 -07:00
John Keiser 4c1256acc4 Reduce nesting somewhat with different if() order 2020-07-01 09:51:48 -07:00
John Keiser 85f6f5bd29 Use macros to remove #ifdefs on every write 2020-07-01 09:51:48 -07:00
John Keiser 4d9eac663a Use a macro to get rid of #ifdefs on each invalid number check 2020-07-01 09:51:48 -07:00
Daniel Lemire 0ef4d90ad0 Fix for issue 1014. (#1015)
* Fix for issue 1014.

* Explanation.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-30 19:36:26 -04:00
Daniel Lemire 1a1e7edb02 Tweaking the recently introduced mingw tests. (#1011)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-30 12:47:44 -04:00
Daniel Lemire 51b835f71b Correcting typo (#1007)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-30 10:19:37 -04:00
Daniel Lemire e38fe3d361 Version update 2020-06-30 09:41:20 -04:00
myd7349 cc042c9936 CMAKE_VS_PLATFORM_TOOLSET might be empty (#1009)
According to https://gitlab.kitware.com/cmake/cmake/-/issues/17976,
CMAKE_VS_PLATFORM_TOOLSET is set only when using a Visual Studio generator.

When we use Ninja as the generator, CMAKE_VS_PLATFORM_TOOLSET will be empty.
As a result:
if(${CMAKE_VS_PLATFORM_TOOLSET} STREQUAL "v140")
will be treated as:
if( STREQUAL "v140")

We may also quote it like this:
if("${CMAKE_VS_PLATFORM_TOOLSET}" STREQUAL "v140")
but that won't make the warnings disappeared in VS2015.
2020-06-30 09:40:19 -04:00
Daniel Lemire 599e3bc937 Update README.md 2020-06-29 21:52:08 -04:00
Daniel Lemire 1fa0d940bc Update README.md 2020-06-29 21:44:05 -04:00
Daniel Lemire 7dc4a9525b Update README.md 2020-06-29 21:43:07 -04:00
Daniel Lemire b6f1f4ef64 Update basics.md 2020-06-29 21:41:50 -04:00
Daniel Lemire 3faae67663 New release 2020-06-29 21:11:58 -04:00
Daniel Lemire ccc94c9b05 Mingw tests (32-bit and 64-bit) (#1004) 2020-06-29 21:10:54 -04:00
Daniel Lemire 1fd30db726 This example in our documentation would not compile (#1005)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-29 16:25:11 -04:00
Daniel Lemire 0ba76ac066 This enables building the library under Visual Studio 2015 (#1002)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-29 08:43:47 -04:00
Daniel Lemire 8b661fe556 Merge pull request #1001 from simdjson/dlemire/justlib_install
We do not properly test just library.
2020-06-28 13:12:42 -04:00
Daniel Lemire 077907b7c3 Preparing a new patch release. 2020-06-28 12:40:13 -04:00
Daniel Lemire 172d669780 We do not properly test just library. 2020-06-28 12:25:17 -04:00
Daniel Lemire 6b85b9a416 Merge pull request #998 from simdjson/issue976
Final steps.
2020-06-26 21:05:06 -04:00
Daniel Lemire 5d3001279c Update CONTRIBUTORS 2020-06-26 20:36:19 -04:00
Daniel Lemire 4582a13360 Final steps. 2020-06-26 20:31:24 -04:00
Daniel Lemire 3a064535ae Merge pull request #994 from simdjson/issue976
Fix for issue 976 (something like 32-bit support)
2020-06-26 20:02:15 -04:00
Daniel Lemire 444ec4ad27 Stupid me 2020-06-26 19:29:28 -04:00
Daniel Lemire 94e910586d Removing a cast. 2020-06-26 19:06:39 -04:00
Daniel Lemire bb5ce007e6 Something better. 2020-06-26 19:03:28 -04:00
Daniel Lemire deaa74d378 Re-enabling tests generally. 2020-06-26 18:57:34 -04:00
Daniel Lemire 13e1794e91 Merge pull request #995 from simdjson/dlemire/issue988
This introduces a new option to forcefully disable threads for people who also want them enabled
2020-06-26 18:37:18 -04:00
Daniel Lemire 67b3595008 Making it clear that it may disappear. 2020-06-26 13:29:50 -04:00
Daniel Lemire 6c33f518a8 This introduces a new option to forcefully disable threads. 2020-06-26 13:23:44 -04:00
Daniel Lemire 88da62ba09 Better documentation in the code. 2020-06-26 13:02:12 -04:00
Daniel Lemire b6997a56df Patching things up and adding tests. 2020-06-26 12:15:16 -04:00
Daniel Lemire 74178fd1c2 Merge pull request #991 from knapply/master
Permit 32-bit GCC compilation
2020-06-26 09:50:18 -04:00
Brendan Knapp 34c59bfa90 Merge pull request #1 from CountOnes/master
Minor fixes to avoid 32-bit warnings.
2020-06-25 18:43:40 -07:00
Daniel Lemire 2956bce047 Minor fixes to avoid 32-bit warnings. 2020-06-25 21:12:26 -04:00
Brendan Knapp 41f33ecbb9 Permit 32-bit GCC compilation 2020-06-25 17:07:17 -07:00
Daniel Lemire 86241e2871 Merge pull request #987 from simdjson/issue985
Removing optional since it is not C++11, and it is not used
2020-06-25 11:04:36 -04:00
Daniel Lemire 1e32897d3e Merge pull request #986 from simdjson/issue984
Fixing issue 984
2020-06-25 11:04:19 -04:00
Daniel Lemire 1b63a9a9b5 Removing optional since it is not C++11 2020-06-25 10:25:57 -04:00
Daniel Lemire 4c9f11b78a Missing character. 2020-06-25 10:15:13 -04:00
Daniel Lemire 32348c2b0b Elaborating. 2020-06-25 10:14:29 -04:00
Daniel Lemire 5e690c5d04 Fixing the string_view issue. 2020-06-25 10:02:10 -04:00
Daniel Lemire 4ec56484a1 Merge pull request #979 from simdjson/issue977
This removes git as a dependency to our CMake
2020-06-24 20:45:15 -04:00
Daniel Lemire 8f2a5649fe Merge pull request #983 from TkTech/patch-1
Fix documentation links in basics.md
2020-06-24 20:44:46 -04:00
Daniel Lemire c3b25e12a5 Update implementation-selection.md 2020-06-24 20:42:04 -04:00
Daniel Lemire 6d3e33d440 Update parse_many.md 2020-06-24 20:41:38 -04:00
Daniel Lemire c11f7ce54f Update performance.md 2020-06-24 20:41:06 -04:00
Tyler Kennedy 84806cc174 Fix documentation links in basics.md
Links to other files need to be either relative to themselves (doc/performance.md -> performance.md) or absolute (doc/performance.md -> /doc/performance.md). This change fixes the documentation when read on GitHub.
2020-06-24 20:20:14 -04:00
Daniel Lemire 16f12f7d59 Tweak. 2020-06-24 19:31:51 -04:00
Daniel Lemire 8609b8e589 Adding a ninja test. 2020-06-24 19:29:08 -04:00
Daniel Lemire 29e744fdbb Adding warning message. 2020-06-24 19:23:02 -04:00
Daniel Lemire 515b87bcbe Disabling perfcheck for ninja 2020-06-24 18:45:47 -04:00
Daniel Lemire 5fa9faca20 Merge pull request #980 from simdjson/dlemire/put_back_perf_tests
Putting back the perf tests.
2020-06-24 17:19:24 -04:00
Daniel Lemire a4ebdfd47c Merge pull request #975 from simdjson/dlemire/nitpicking_amal_demo
Nitpicking: demo should not crash on missing input
2020-06-24 17:18:40 -04:00
Daniel Lemire 188d8d4b64 More verbose. 2020-06-24 17:09:18 -04:00
Daniel Lemire ce5581d428 This makes it so that if we are under someone else, we just build the library. 2020-06-24 17:00:19 -04:00
Daniel Lemire 5b4acf14ea Removing space. 2020-06-24 16:51:28 -04:00
Daniel Lemire 5fc6cb15b8 This should make things even more robust. If .git is not found, just disable all git work. 2020-06-24 16:12:19 -04:00
Daniel Lemire f6e9a8eee4 Making the cmake more verbose so we can figure out what is happening. 2020-06-24 15:44:22 -04:00
Daniel Lemire 6c0950cb2e Putting back the perf tests. 2020-06-24 15:28:28 -04:00
Daniel Lemire cb8a9ef2c0 This removes git as a dependency 2020-06-24 15:13:47 -04:00
Daniel Lemire 470cbbe9ff Nitpicking. 2020-06-23 22:07:40 -04:00
Daniel Lemire e01f1434fb Bumping up the version number 2020-06-23 20:55:52 -04:00
John Keiser 187084ce46 Merge pull request #970 from simdjson/jkeiser/singleheader-tests
Make singleheader tests be test-only
2020-06-23 17:07:03 -07:00
John Keiser b6f9382b5b Merge pull request #972 from simdjson/jkeiser/effc++
Compile with -Weffc++ in CI
2020-06-23 17:06:35 -07:00
Daniel Lemire 57f68381ed Merge pull request #973 from simdjson/dlemire/testing_static_lib
Because we are dynamic by default, we never test static!
2020-06-23 19:50:49 -04:00
Daniel Lemire 3e35729eb6 Merge pull request #968 from simdjson/issue961
Fixing issue 961
2020-06-23 19:48:43 -04:00
Daniel Lemire 544fa57641 Damn merge conflicts. 2020-06-23 19:15:47 -04:00
Daniel Lemire 7e94309046 Update basics.md 2020-06-23 19:08:14 -04:00
Daniel Lemire 21eff5b825 Introducing some variety. 2020-06-23 18:58:38 -04:00
Daniel Lemire b408d7c95e Because we are dynamic by default, we never test static! 2020-06-23 18:40:57 -04:00
John Keiser d9929edbc1 Run -Weffc++ in CI 2020-06-23 13:44:25 -07:00
John Keiser 843b73dedb Make singleheader tests be test-only 2020-06-23 13:35:27 -07:00
John Keiser 02a8145b18 Merge pull request #962 from simdjson/jkeiser/issue953
Fix #953: simdjson.cpp fails -Wall compiled on its own
2020-06-23 13:30:04 -07:00
Daniel Lemire cfcb315b14 Merge pull request #967 from simdjson/dlemire/improving_documentation
This improves slightly the documentation, adding instructions for CMake users.
2020-06-23 15:49:42 -04:00
Daniel Lemire c8a70a0a73 Tweaking the documentation. 2020-06-23 14:39:16 -04:00
Daniel Lemire b84a3a0230 Merge branch 'master' into issue961 2020-06-23 14:33:06 -04:00
Daniel Lemire 49d70232f8 Merge pull request #969 from simdjson/dlemire/minor_pre0.4_cleaning
Very minor cleaning.
2020-06-23 14:30:47 -04:00
Daniel Lemire 8cc9f496ee Merge branch 'master' into dlemire/improving_documentation 2020-06-23 13:07:29 -04:00
Daniel Lemire 1547f2ec80 Pleasing John 2020-06-23 13:05:19 -04:00
John Keiser 42a8b40de0 Reamalgamate 2020-06-23 09:53:36 -07:00
John Keiser b4b968ff44 Fix #953 2020-06-23 09:53:24 -07:00
John Keiser 52e3c063c5 Add repro for #953
Depend on singleheader-files via custom target to avoid races
2020-06-23 09:53:21 -07:00
John Keiser 257089884f Merge pull request #958 from simdjson/jkeiser/is
Make simdjson_result<element>.is() return bool
2020-06-23 09:51:37 -07:00
John Keiser c650ea9765 Merge pull request #960 from simdjson/jkeiser/idiomatic-get
Convert simdjson to use .get()
2020-06-23 09:49:41 -07:00
John Keiser e369d45b9c Fix non-compileable examples 2020-06-23 09:48:17 -07:00
John Keiser 2d84b6f6d9 Make simdjson_result<element>.is() return bool 2020-06-23 09:09:24 -07:00
John Keiser eef1171944 Merge pull request #954 from simdjson/jkeiser/parse-many-result
Return error from parse_many
2020-06-23 09:06:20 -07:00
John Keiser 12ccdcf858 Include document_stream line in parse_many docs 2020-06-23 08:49:47 -07:00
Daniel Lemire f1a03bfb04 Very minor cleaning. 2020-06-23 11:05:58 -04:00
Daniel Lemire 696b0e29e4 Fixing issue 961 2020-06-23 10:47:32 -04:00
Daniel Lemire 5eb748ae17 This improves slightly the documentation, adding instructions for CMake users. 2020-06-23 09:33:15 -04:00
Daniel Lemire aa4340ef5c Merge pull request #957 from simdjson/dlemire/install_just_lib
This might enable folks to just build the library.
2020-06-23 09:12:28 -04:00
Daniel Lemire 0062e54e93 Merge pull request #959 from simdjson/dlemire/utf8_val
Expose the UTF-8 string validation functions
2020-06-22 21:34:02 -04:00
Daniel Lemire dada5090b0 These compilers are insane. 2020-06-22 20:25:55 -04:00
Daniel Lemire 1c4593c648 These compilers are really pedantic. 2020-06-22 20:04:37 -04:00
Daniel Lemire e7004cef76 Removing a test so that it is all ASCII. 2020-06-22 16:55:16 -04:00
Daniel Lemire 2bb101bd19 Code reformatting. 2020-06-22 16:50:57 -04:00
Daniel Lemire 26baf70912 Pedantic compiler 2020-06-22 16:45:32 -04:00
Daniel Lemire 89c2582376 Extending the documentation. 2020-06-22 16:32:00 -04:00
Daniel Lemire 33e003616d Fixing the name of the variable 2020-06-22 16:29:38 -04:00
Daniel Lemire bf03d77ab9 Passing by value the string_view 2020-06-22 16:28:35 -04:00
Daniel Lemire a6cbf1f922 Going generic... 2020-06-22 16:25:11 -04:00
Daniel Lemire d6f056f266 Fixing documentation issues. 2020-06-22 16:17:11 -04:00
Daniel Lemire 69a247d500 Adding tests. 2020-06-22 16:12:37 -04:00
Daniel Lemire a76c67c19f Fixing... 2020-06-22 15:57:54 -04:00
Daniel Lemire b836164a38 Fix. 2020-06-22 02:12:49 +00:00
Daniel Lemire 058507badf Putting back the loop 2020-06-21 21:21:49 -04:00
Daniel Lemire ad40e90790 Patching. 2020-06-21 20:14:00 -04:00
John Keiser 0c9dc11550 Use really_inline to help g++ detect initialized variable 2020-06-21 16:27:05 -07:00
John Keiser 1ff55c2729 Replace auto [x,error] with .get() everywhere 2020-06-21 16:26:59 -07:00
Daniel Lemire 066269153e Explaining decision. 2020-06-21 18:02:34 -04:00
Daniel Lemire 38bb08778a With an example. 2020-06-21 17:57:22 -04:00
Daniel Lemire 5dbcdf1484 Ok 2020-06-21 17:52:30 -04:00
Daniel Lemire f03a6ab5a4 Tweaking. 2020-06-21 17:39:24 -04:00
John Keiser 6fa5abcd7e Replace x.get<T>() with x.get(v) or T(x) 2020-06-21 14:36:38 -07:00
Daniel Lemire 5dc07ed295 It builds. 2020-06-21 17:20:33 -04:00
Daniel Lemire 064d4255d5 Ok. 2020-06-21 17:09:06 -04:00
Daniel Lemire 04139eb82e Ok. 2020-06-21 17:05:55 -04:00
John Keiser 1b1a122b1f Fix copy constructor issue on older gcc 2020-06-21 12:06:14 -07:00
Daniel Lemire ccb132320c Merge branch 'dlemire/install_just_lib' of github.com:lemire/simdjson into dlemire/install_just_lib 2020-06-21 14:40:55 -04:00
Daniel Lemire c0e7f824df Enabling the tests. 2020-06-21 14:40:38 -04:00
Daniel Lemire 05bba71eaf Update simdjson-flags.cmake 2020-06-21 14:28:51 -04:00
Daniel Lemire 7ebe5c4bcf This might enable folks to just build the library. 2020-06-21 14:20:58 -04:00
John Keiser ae1bd891e7 Remove deprecated uses of parse_many 2020-06-21 11:19:06 -07:00
John Keiser 9899e5021d Allow use of document_stream with tie() 2020-06-20 21:15:05 -07:00
John Keiser 94440e0170 Return simdjson_result from load_many/parse_many 2020-06-20 20:51:53 -07:00
John Keiser c25928e44f Merge pull request #950 from simdjson/jkeiser/tie-type
replace get<T>.tie(v,e) with get(v)
2020-06-20 20:50:56 -07:00
John Keiser a7fc7d4ffb Switch from get(v,e) to e = get(v) 2020-06-20 17:57:09 -07:00
John Keiser f336103f63 Convert tools/docs/benchmarks to bool get() idiom 2020-06-20 17:55:46 -07:00
John Keiser 56e2b38048 Add bool result from tie()/get(), get<T>(T&,error_code&) 2020-06-20 17:55:46 -07:00
John Keiser a5ccff720a Merge pull request #949 from simdjson/jkeiser/get-type
Add non-template get_xxx/is_xxx methods to element
2020-06-20 17:35:27 -07:00
John Keiser 1d8c2d6c22 Make get_xxx the primary functions 2020-06-20 13:29:12 -07:00
John Keiser 0b8c357eff Add get_X and is_X methods 2020-06-19 13:27:33 -07:00
John Keiser efc168f473 Make test changes only 2020-06-19 13:27:33 -07:00
John Keiser d8428f98d9 Add cast_tester.h 2020-06-19 13:27:33 -07:00
John Keiser 60f17d26a3 Move test macros to a header 2020-06-19 13:27:00 -07:00
John Keiser 05bc664c11 Don't extend from tape_ref in public classes 2020-06-19 13:25:52 -07:00
Daniel Lemire 2cc84b6e51 Merge pull request #943 from simdjson/dlemire/fewer_perf_tests
Fewer performance tests.
2020-06-18 22:04:50 -04:00
Daniel Lemire 5ccdbef7d5 Merge pull request #936 from simdjson/dlemire/new_examples
New examples.
2020-06-18 18:29:06 -04:00
Daniel Lemire c13c2650a2 Merge pull request #940 from simdjson/issue938
Verifying (and fixing) issue 938
2020-06-18 18:25:31 -04:00
Daniel Lemire ec6c998a3a Merge pull request #942 from simdjson/dlemire/better_doxygen_home_page
Tweaks doxygen so that we have a better main page.
2020-06-18 18:25:04 -04:00
Daniel Lemire 2f6091419f Merge pull request #944 from simdjson/issue680
Document the complexity of array.at
2020-06-18 18:24:08 -04:00
Daniel Lemire 2022dd7d74 Merge pull request #945 from simdjson/issue678
Fixing issue 678
2020-06-18 18:23:56 -04:00
Daniel Lemire b8202dab3b Merge pull request #946 from simdjson/issue937
Fixing issue 937
2020-06-18 18:20:44 -04:00
Daniel Lemire ef688a74fe Minor tweak to the documentation. 2020-06-18 18:18:12 -04:00
John Keiser f632e7c043 Put C++11 capable version back, change name to readme style 2020-06-18 12:50:49 -07:00
Daniel Lemire 04a19f9813 Fixes https://github.com/simdjson/simdjson/issues/937 2020-06-17 18:06:13 -04:00
Daniel Lemire 2cbc591c9d Fixing issue 678 2020-06-17 16:17:17 -04:00
Daniel Lemire 3f00e79bcb Merge branch 'master' into dlemire/better_doxygen_home_page 2020-06-17 16:02:49 -04:00
Daniel Lemire 3586fc4910 Fix for issue 680 2020-06-17 18:49:22 +00:00
Daniel Lemire c9a6bbeb64 Merge pull request #935 from simdjson/dlemire/tuning_the_documentation
Some tweaks to the documentation
2020-06-17 14:33:23 -04:00
Daniel Lemire 0655a135e6 Reverting. 2020-06-17 17:52:07 +00:00
Daniel Lemire d3e8bb1889 Fewer performance tests. 2020-06-17 17:44:28 +00:00
Daniel Lemire 14ceacac73 Tweaking. 2020-06-17 13:27:17 -04:00
Daniel Lemire e4f33b5970 Tweaking the message. 2020-06-17 12:36:36 -04:00
Daniel Lemire 4474f8ef18 Cleaning a bit the examples. 2020-06-17 16:24:55 +00:00
John Keiser 76c9f4f5a6 Merge pull request #941 from simdjson/jkeiser/forgot
Remove unnecessary functions
2020-06-17 09:09:28 -07:00
Daniel Lemire 942ef3b7f2 Merge pull request #939 from simdjson/dlemire/lookup3
Introducing lookup3 (UTF-8 validation).
2020-06-17 11:19:09 -04:00
Daniel Lemire 0b9df6d8c4 It turns out that we need fairly complicated logic. 2020-06-17 15:17:10 +00:00
Daniel Lemire b5ea504ad2 Tweaks doxygen so that we have a better main page. 2020-06-17 11:07:21 -04:00
Daniel Lemire 803b0c4bdb Light touch. 2020-06-17 11:00:13 -04:00
Daniel Lemire 6537d0dc76 Avoiding the unused errors. 2020-06-17 14:19:58 +00:00
John Keiser f8f36c085c Remove unnecessary functions 2020-06-17 07:11:53 -07:00
John Keiser 7339f67dd7 Merge pull request #462 from simdjson/jkeiser/if-backslash
Wrap backslash processing in a branch
2020-06-17 07:07:58 -07:00
Daniel Lemire 0d4e501239 Fixing the bug. 2020-06-17 10:06:16 -04:00
Daniel Lemire 8d609607e2 Verifying the bug. 2020-06-16 20:04:09 -04:00
Daniel Lemire 71a889ed73 Introducing lookup3 (UTF-8 validation). 2020-06-16 19:08:25 -04:00
Daniel Lemire 27a75a9085 Tweaking. 2020-06-15 17:54:34 -04:00
Daniel Lemire 954d6c326d New examples. 2020-06-15 17:45:15 -04:00
Daniel Lemire 7ea05d038e New API traversal tests. (#931)
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-15 13:15:52 -04:00
Daniel Lemire 33930ff046 Adding link. 2020-06-15 13:07:53 -04:00
Daniel Lemire 16f41ea059 Added a word. 2020-06-14 18:48:42 -04:00
Daniel Lemire 0a7270fc29 More tweaks. 2020-06-14 18:47:22 -04:00
Daniel Lemire 23fbd9d004 Some tweaks. 2020-06-14 18:28:09 -04:00
John Keiser 610c79fbf3 Don't use backslash branch on ARM 2020-06-13 07:51:28 -07:00
John Keiser fd44c2a2ff Merge pull request #927 from simdjson/dlemire/exposingthestringminifier
Exposing the string minifier.
2020-06-13 07:47:20 -07:00
John Keiser a86a82b39c Rename minify class to minifier so the minify() method is cleared up 2020-06-12 17:05:25 -07:00
Daniel Lemire 89b059b1ea Testing with GCC 10 and clang 10 (#926)
* Testing with GCC 10 and clang 10

* Fixing spurious space

* gcc10 does not need the cmake installation.

* We don't want to run the perf test on ARM. I ignore them systematically. ARM performance
should be assessed manually.

* Switching to GCC 10 and Clang 10

* Disabling some tests under sanitizers when they involve rapidjson or other parsers.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-12 17:58:53 -04:00
Daniel Lemire bd2d0f769f One unlikely too many (#930) 2020-06-12 17:58:10 -04:00
Daniel Lemire d830422489 Put back the amalgamation files and add tests (#929)
Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-12 17:57:45 -04:00
Daniel Lemire d1a54249e7 New API traversal tests. 2020-06-12 17:42:57 -04:00
Daniel Lemire 4dfbf98e4e Using a worker instead of a thread per batch (#920)
In the parse_many function, we have one thread doing the stage 1, while the main thread does stage 2. So if stage 1 and stage 2 take half the time, the parse_many could run at twice the speed. It is unlikely to do so. Still, we see benefits of about 40% due to threading.

To achieve this interleaving, we load the data in batches (blocks) of some size. In the current code (master), we create a new thread for each batch. Thread creation is expensive so our approach only works over sizeable batches. This PR improves things and makes parse_many faster when using small batches.

  This fixes our parse_stream benchmark which is just busted.
  This replaces the one-thread per batch routine by a worker object that reuses the same thread. In benchmarks, this allows us to get the same maximal speed, but with smaller processing blocks. It does not help much with larger blocks because the cost of the thread create gets amortized efficiently.
This PR makes parse_many beneficial over small datasets. It also makes us less dependent on the thread creation time.

Unfortunately, it is going to be difficult to say anything definitive in general. The cost of creating a thread varies widely depending on the OS. On some systems, it might be cheap, in others very expensive. It should be expected that the new code will depend less drastically on the performances of the underlying system, since we create juste one thread.

Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-12 16:51:18 -04:00
Daniel Lemire 1b6258ec8c Added std::minify 2020-06-12 16:37:41 -04:00
Daniel Lemire be707dbb6f Added a remark 2020-06-12 16:07:34 -04:00
John Keiser 664b03bb13 Short circuit find escapes if there is a backslash 2020-06-12 10:10:35 -07:00
John Keiser 7c6723d912 Print progress bar even if there is only one file 2020-06-12 10:01:19 -07:00
John Keiser 1febf2ec83 Merge pull request #919 from simdjson/jkeiser/move-current-loc
[4/4] Stop persisting current_loc (+2% parse throughput)
2020-06-12 09:55:04 -07:00
John Keiser fe69928764 Merge pull request #918 from simdjson/jkeiser/remove-iterator-variables
[3/4] Remove unneeded structural_iterator variables
2020-06-12 09:52:35 -07:00
John Keiser bbd61eb13f Let tape writing be put in a register 2020-06-12 09:18:20 -07:00
John Keiser e15e1e253d peek_char -> peek_next_char 2020-06-12 09:10:16 -07:00
Daniel Lemire 45e2178ada Duh. 2020-06-11 17:20:28 +00:00
Daniel Lemire a6e4933d93 Exposing the string minifier. 2020-06-11 13:07:18 -04:00
Daniel Lemire 98599e0972 Remove the circleci badge since it may appear to fail due to perfdiff 2020-06-11 11:37:53 -04:00
John Keiser b4837f2e2f Merge pull request #915 from simdjson/jkeiser/stage2-common
[2/4] Use same state machine for stage 2 streaming and non-streaming
2020-06-10 08:37:08 -07:00
John Keiser ea08e7d192 Remove unused extra copy of find_next_document_index 2020-06-09 17:52:13 -07:00
John Keiser d178e089a6 Stop caching current structural, keep current index around instead of
next
2020-06-08 15:21:54 -07:00
John Keiser 5f00b37e21 Stop caching the buffer index 2020-06-08 15:21:54 -07:00
John Keiser 8a8792d47f Remove most uses of current_char() 2020-06-08 15:21:54 -07:00
John Keiser 59d9bc9e48 Store the pointer to the next structural instead of base
structural_indexes and an index
2020-06-08 15:21:54 -07:00
John Keiser 8793dd3ceb Don't store len locally 2020-06-08 15:21:54 -07:00
John Keiser 48062380fa Move parser to structural_iterator 2020-06-08 15:21:54 -07:00
John Keiser 3636aa5522 Extend structural_parser from structural_iterator 2020-06-08 15:21:54 -07:00
John Keiser a1aea4588f Move document stream state to implementation 2020-06-08 15:21:54 -07:00
John Keiser 1d4fffb799 Fix fallback implementation 2020-06-08 15:21:52 -07:00
John Keiser 6f90f5dc5f Remove templating from finish() method 2020-06-08 15:20:56 -07:00
John Keiser 9dd6972d26 Remove impossible checks, add EMPTY check to normal parser 2020-06-08 15:20:56 -07:00
John Keiser d731a7d52c Privatize structural_parser 2020-06-08 15:20:56 -07:00
John Keiser 059468b74e Eliminate streaming_structural_parser subclass with templates 2020-06-08 15:20:56 -07:00
John Keiser 5e69fb782a Call a function to parse structurals 2020-06-08 15:20:56 -07:00
John Keiser a5beffda78 Remove streaming_structural_parser.h 2020-06-08 15:20:56 -07:00
John Keiser 7de7ce5fdc Move document stream state to implementation 2020-06-08 15:20:56 -07:00
John Keiser 383e8c7f68 Merge pull request #913 from simdjson/jkeiser/internal-streaming
[1/4] Simplify parse_many() and fix bugs
2020-06-08 15:19:27 -07:00
John Keiser 0dbda65e44 Fix fallback implementation 2020-06-08 14:52:23 -07:00
John Keiser fe01da077e Make threaded version work again 2020-06-07 16:21:00 -07:00
John Keiser d43a4e9df9 Remove SUCCESS_AND_HAS_MORE (internal only value) 2020-06-07 16:20:55 -07:00
John Keiser 3e226795f0 Run all passing json against parse_many. Empty documents pass, too. 2020-06-07 16:20:51 -07:00
John Keiser c4a0fe1606 Add tests for parse_many() errors 2020-06-07 16:20:46 -07:00
John Keiser ef63a84a3e Move document stream state to implementation 2020-06-07 16:20:44 -07:00
John Keiser 8c16ba372e Acknowledge that we always have a remainder 2020-06-06 16:46:38 -07:00
John Keiser 9be4a17687 Separate definition from declaration, arrange top down 2020-06-06 16:46:38 -07:00
Furkan 89332e1696 Temporary fix to #914 (#917) 2020-06-05 21:01:41 -04:00
John Keiser 8a56129def Merge pull request #916 from simdjson/jkeiser/issue906stage2
Move unclosed array check to stage 2
2020-06-05 14:23:44 -07:00
John Keiser ed0c815735 Move unclosed array check to stage 2 2020-06-05 12:39:13 -07:00
Daniel Lemire 7a69da16e4 Fixing issue 906 (#912)
* Fixing issue 906

* Safe patching.

* Now with explanations.

* Bumping up memory allocation.

* Putting the patch back.

* fallback fixes.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-05 15:37:09 -04:00
Daniel Lemire 351717414d updating docker with instructions... (#901)
* Better dockerfile with instructions.

* Typo.
2020-06-04 20:06:29 -04:00
Daniel Lemire 52f44de257 This introduces a tiny simplification in number parsing. (#910)
* This introduces a tiny simplification in number parsing.

* Removing unnecessary function.

Co-authored-by: Daniel Lemire <lemire@gmai.com>
2020-06-04 17:13:02 -04:00
John Keiser ae6dddfff4 Merge pull request #903 from simdjson/jkeiser/dom-parser-implementation
Move parser state to implementation-specific class
2020-06-04 13:09:57 -07:00
Daniel Lemire 539f555e23 We no longer use make 2020-06-02 16:45:30 -04:00
John Keiser b75fa26dc1 Move containing_scope and ret_address to .cpp 2020-06-01 12:15:55 -07:00
John Keiser 3d22a2d845 One weird trick: set a bogus error value in the parser impl
This makes us faster under both gcc and clang somehow.
2020-06-01 12:15:55 -07:00
John Keiser 1aab4752e2 Store all parser state in the implementation 2020-06-01 12:15:54 -07:00
John Keiser 86f8a4a9d2 Don't set parser.valid or parser.error
This regresses performance and is ONLY here because the next
two commits are here; this lets us see the impact of removing
parser.error separately from the impact of the next commit.
2020-06-01 12:14:09 -07:00
John Keiser db2cb061cb Remove on_error function
Solely here to make the next patch smaller and more isolatable
2020-06-01 12:14:09 -07:00
John Keiser 6a71b24495 Reuse stored buf and len from parser 2020-06-01 12:14:09 -07:00
John Keiser 84712a8bbc Store buf and len in parser implementation 2020-06-01 12:14:09 -07:00
John Keiser b86fb95306 Rename doc_parser -> parser 2020-06-01 12:14:09 -07:00
John Keiser a3a9bde83e Move DOM parsing into concrete interface implementation 2020-06-01 12:14:09 -07:00
Daniel Lemire 2fe2dd170b The "competition tests" are being made portable (#907)
* More portable competition

* This will enable SIMDJSON_COMPETITION everywhere by default.

* Minor fixes
2020-05-31 20:34:06 -04:00
yoannlr f772bf4fbc Port tools to cxxopts (#904)
* Port tools to cxxopts

* Fix minify tool architecture argument

* Fix wrong return code in json2json

* Change return codes

* Better handling of the errors.

* Updating to latest version.

* cxxopts outside of SIMDJSON_COMPETITION

Co-authored-by: Daniel Lemire <lemire@gmail.com>
2020-05-29 15:39:23 -04:00
myd7349 ac0c3093f4 Fix build error on UWP (#896)
For x64-uwp, size_t is 64-bit, DWORD is 32-bit.
When /WX compiler option is specified, error occurs.
2020-05-25 09:49:24 -04:00
Daniel Lemire 12150baa5e Using just ASCII. (#899)
* Using just ASCII.

* Let us prune checkperf.

* Moving the description of lookup2 to the HACKING.md file.
2020-05-21 21:59:06 -04:00
John Keiser 219b02c1e5 Merge pull request #900 from simdjson/jkeiser/delay-start-element
Don't write the start element at all until the end
2020-05-21 14:57:54 -07:00
John Keiser 4551e60f8b Don't write start object/array until the end 2020-05-21 14:28:47 -07:00
John Keiser ea842e78af Merge pull request #895 from simdjson/jkeiser/logger
Add logging to stage 2
2020-05-21 14:11:01 -07:00
John Keiser 5651fbedc4 Add logging to stage 2 2020-05-21 09:47:19 -07:00
John Keiser 2f87dfd4a9 Merge pull request #897 from myd7349/fix-ninja-build
Fix Ninja build
2020-05-21 09:44:39 -07:00
myd7349 332dd76867 Fix Ninja build
When using Ninja as generator, it failed to build:
$ cmake .. -G Ninja
$ ninja
> ninja: error: '/jsonexamples/generated/miss-templates/*.txt', needed by 'jsonexamples/generated/utf-8.json', missing and no known rule to make it
2020-05-21 12:29:39 +08:00
Daniel Lemire d2c9ea8a9a Detect bash instead of relying on MSVC detection. (#894) 2020-05-20 12:13:14 -04:00
Daniel Lemire 40d57da83c fixes issue 891 (#893) 2020-05-20 11:54:53 -04:00
John Keiser 561813eb2a Merge pull request #883 from simdjson/jkeiser/move-include-files
Make include files more fine-grained
2020-05-19 15:48:06 -07:00
John Keiser e6c9dfbd91 Make include files more fine-grained 2020-05-19 14:42:04 -07:00
John Keiser 603b6596af Merge pull request #882 from simdjson/jkeiser/move-cpp-files
Split stage2 into files-per-class
2020-05-19 14:34:01 -07:00
John Keiser 64abc3e86c Include top-level .h files outside #if statements 2020-05-19 13:33:14 -07:00
John Keiser fac42fa3e8 Generate relative filenames properly in amalgamation 2020-05-19 13:32:38 -07:00
John Keiser 7ad4020829 Make main compilation chunks into .cpp files 2020-05-19 13:32:35 -07:00
John Keiser 72ab0d11ff Move stage 1 and 2 files to their own directories 2020-05-19 13:30:34 -07:00
John Keiser 4ea866f050 Move stage2 classes into their own files 2020-05-19 13:30:34 -07:00
John Keiser a476531524 Share ref_address everywhere it's used 2020-05-19 13:30:34 -07:00
John Keiser e7e6ac5bb3 Merge pull request #887 from simdjson/jkeiser/checkperf-less
Don't repeat circleci checkperf tests on drone
2020-05-19 13:21:39 -07:00
Daniel Lemire f346362b00 The jsonstats utility becomes ever more powerful. (#890) 2020-05-19 10:31:27 -04:00
John Keiser d9ba455695 Don't repeat circleci checkperf tests on drone 2020-05-15 04:03:06 -07:00
Daniel Lemire 8927a0561f Obvious fix. (#885) 2020-05-14 20:39:44 -04:00
Daniel Lemire e03c5e9f23 We should guard the include (#881) 2020-05-13 20:02:46 -04:00
Daniel Lemire 1f79200db8 This fixes how we count integers in jsonstats. (#878)
* This fixes how we count integers in jsonstats.
2020-05-13 09:31:32 -04:00
Daniel Lemire c009e4a57d Fuzzing should not require loading lots of complicated dependencies (#879)
* I don't think we need google benchmark as part of the fuzzer

* I don't think we should load the "competition" for fuzzing.
2020-05-13 08:30:09 -04:00
John Keiser c615d52cf4 Merge pull request #876 from simdjson/jkeiser/doc-writing-stage-2
Move tape writes into stage 2 code
2020-05-12 10:05:40 -07:00
John Keiser dbb3316511 Move current_string_buf_loc to stage 2 2020-05-11 06:11:32 -07:00
John Keiser cd6f204c77 Move write_tape() to stage 2 code 2020-05-11 06:09:48 -07:00
John Keiser 269131ed21 Move on_number_* to stage 2 code 2020-05-11 06:04:54 -07:00
John Keiser 65d784e88e Move on_start/end_string to stage 2 code 2020-05-11 05:49:40 -07:00
John Keiser 35afb6cae0 Move on_error, on_success to stage 2 code 2020-05-11 05:46:18 -07:00
John Keiser 27bce09be8 Consolidate start_scope/end_scope 2020-05-11 05:40:02 -07:00
John Keiser 4f25b6ac0c Move on_end_* to stage 2 code 2020-05-11 05:34:49 -07:00
John Keiser 3d5ed1a7e3 Move on_start_* to stage 2 code 2020-05-11 05:30:35 -07:00
John Keiser a03115a4a6 Move end_scope to stage 2 code 2020-05-11 05:24:12 -07:00
John Keiser 7219d28a31 Call end_scope directly from stage 2 code 2020-05-11 05:20:04 -07:00
John Keiser 0875bce68f Don't pass depth to on_end_* 2020-05-11 05:15:39 -07:00
John Keiser 54fe302907 Don't pass depth to end_scope 2020-05-11 05:06:41 -07:00
John Keiser edaa8f811f Move on_start_* depth management to stage 2 code 2020-05-11 05:03:25 -07:00
John Keiser 2c8fd109de Move increment_count to stage 2 2020-05-11 04:58:50 -07:00
John Keiser 07fe7ad1a2 Use the same increment_count() everywhere 2020-05-11 04:48:15 -07:00
John Keiser 16d88cc095 Don't pass depth to increment_count 2020-05-11 04:15:02 -07:00
Daniel Lemire 2a6e6b3dbd Cleaning string_view (#872)
* Cleaning string_view

* Corrected typo

* Alignment.
2020-05-10 16:05:52 -04:00
Daniel Lemire 0c19848230 Fixes issue 871 (#873) 2020-05-10 15:56:49 -04:00
Daniel Lemire 3c3a4db54e Compile under Visual Studio for ARM64 (#861)
* Modifications so that we can compile under Visual Studio for ARM64
* Let us throw appveyor at this beast.
2020-05-06 23:08:10 -04:00
Daniel Lemire 0e6bd2224f Fixes issue 863 (#864)
* Fixes issue 863
2020-05-06 22:38:49 -04:00
John Keiser a64d2f4673 Merge pull request #857 from simdjson/jkeiser/benchfeatures
Make benchfeatures work again
2020-05-05 12:34:49 -07:00
John Keiser 1e8a54af0b Fix benchfeatures.rb on Ruby 1.9 2020-05-05 11:52:07 -07:00
John Keiser aa53d8708e Generate test data when tests are built 2020-05-05 11:29:41 -07:00
John Keiser 8c600ca553 Make benchfeatures work again 2020-05-05 09:39:29 -07:00
John Keiser 25fe6d7dde Merge pull request #859 from simdjson/jkeiser/vsquickstart
Eliminate Intellisense errors and warnings in new VS project
2020-05-05 08:25:13 -07:00
John Keiser afb369950c Disable Intellisense-only warnings in simdjson.h/cpp 2020-05-04 11:47:04 -07:00
John Keiser d7f133c24c Reamalgamate 2020-05-04 11:36:00 -07:00
John Keiser 5312fd30e5 Fix CRT_SECURE warnings in clang 2020-05-04 11:36:00 -07:00
John Keiser 23dd0bdaa1 Remove /nologo MSVC flag 2020-05-04 11:35:57 -07:00
John Keiser 1d06624d38 Unset /D_CRT_SECURE_NO_WARNINGS
- Also localize DISABLE_DEPRECATED_WARNING so that we catch other
  deprecations
2020-05-04 11:35:05 -07:00
Pavel P d40069a018 Disable deprecation warnings for VS builds
fopen/getenv are standard c++ that are not deprecated.
2020-05-04 11:34:00 -07:00
John Keiser 73e27bdd48 Merge pull request #852 from furkanusta/cmake-flags
CMake: Separate public and private compilation flags
2020-05-03 08:55:52 -07:00
Furkan Usta 064eb0b24f CMake: Make simdjson-internal-flags subsume simdjson-flags 2020-05-03 02:48:29 +03:00
Furkan Usta af968c5b44 Merge branch 'master' of github.com:simdjson/simdjson into cmake-flags 2020-05-03 02:12:23 +03:00
John Keiser f93dbe51e2 Merge pull request #855 from simdjson/dlemire/minorreformatwithclangformat
A gift to John.
2020-05-02 13:55:43 -07:00
Daniel Lemire 1c34707925 A gift to John. 2020-05-02 15:01:22 -04:00
John Keiser deaca58504 Merge pull request #851 from furkanusta/cmake-checkperf
Propagate some CMake variables to checkperf
2020-05-02 09:22:48 -07:00
John Keiser 1d519aa9cb Merge pull request #854 from furkanusta/fix808
Add path information back to amalgamate.sh (Fixes #808)
2020-05-02 09:21:53 -07:00
John Keiser 191faeae70 Merge pull request #853 from furkanusta/windows-parsingchecks
Enable numberparsingcheck and stringparsingcheck on MSVC
2020-05-02 09:19:47 -07:00
John Keiser 1153aaf55b Merge pull request #850 from furkanusta/cmake-taketwo
Only install singleheader/simdjson.h as part of the public API
2020-05-02 09:06:06 -07:00
Furkan Usta 292cb5a5af Add path information back to amalgamate.sh (Fixes #808) 2020-05-02 18:00:42 +03:00
Furkan Usta 1e9488d4a6 Remove Microsoft comment regarding dirent in parsingchecks 2020-05-02 16:01:30 +03:00
Furkan Usta ff1d77ead9 Add NOMINMAX to parsingchecks 2020-05-02 15:33:53 +03:00
Furkan Usta 977e1a94b2 Use dirent_portable.h only in MSVC 2020-05-02 15:16:50 +03:00
Furkan Usta 60ee5fc844 Enable numberparsingcheck and stringparsingcheck on MSVC 2020-05-02 15:12:30 +03:00
Furkan Usta 940c8fcb5e Fix: simdjson-private-flags to simdjson-internal-flags 2020-05-02 05:23:53 +03:00
Furkan Usta 71e0148eb4 CMake: Link simdjson-fuzzer to simdjson-source as before
simdjson target adds extra definitions which MSVC doesn't like
2020-05-02 05:21:57 +03:00
Furkan Usta 293c104cc4 CMake: Separate public and private compilation flags
simdjson-internal-flags for macros and warnings
simdjson-flags for pthread, sanitizer, and libcpp
2020-05-02 04:08:47 +03:00
Furkan Usta 5a3035bb72 Propagate some CMake variables to checkperf
Although it passes user-defined options, if the project is build in Debug mode or with Clang (since
CXX defaults to gcc on Linux) results can flactuate
2020-05-02 02:40:13 +03:00
Furkan Usta e04cbd71d0 Only install singleheader/simdjson.h as part of the public API 2020-05-02 01:44:11 +03:00
Daniel Lemire fa4ce6a8bc There is confusion between gigabytes and gigibytes. Let us standardize throughout. (#838)
* There is confusion between gigabytes and gigibytes.

* Trying to be consistent.
2020-05-01 12:16:18 -04:00
Daniel Lemire 9863f62321 Trying to avoid unused warnings in isa detection. (#846) 2020-05-01 11:43:31 -04:00
John Keiser 7cd1f7dbd5 Merge pull request #840 from simdjson/jkeiser/conanimages
Use gcc9 and clang9 where possible
2020-05-01 08:40:57 -07:00
Daniel Lemire 8c45a18524 Hiding cpuid_*_bit as well as related enum. (#843) 2020-04-30 21:08:08 -04:00
Daniel Lemire 8cb383ed45 Update AUTHORS 2020-04-30 19:49:20 -04:00
Daniel Lemire 7d1305b169 Update AUTHORS 2020-04-30 19:49:01 -04:00
Daniel Lemire 24ab1f32e4 adding Furkan Taşkale 2020-04-30 19:48:36 -04:00
Daniel Lemire 073ad0dada This function is unused. (#842) 2020-04-30 19:45:45 -04:00
John Keiser f43b6a0675 Use gcc9 and clang9 where possible 2020-04-30 15:35:38 -07:00
Daniel Lemire fc1ddcd2f8 Faster case-insensitive comparisons. (#837)
* Faster case-insensitive comparisons.
2020-04-30 15:52:28 -04:00
John Keiser e7f774f964 Merge pull request #836 from furkanusta/fix830
Make library consumable after CMake installation (Fixes #830)
2020-04-30 11:34:08 -07:00
John Keiser cb49af1ea5 Merge pull request #835 from simdjson/jkeiser/enable-google-benchmarks
Default SIMDJSON_GOOGLE_BENCHMARKS to ON.
2020-04-30 11:22:38 -07:00
Furkan Usta 73d7d704c1 CMake: Remove export_private_library
Since we are exporting all the targets as part of the main simdjson target we do not need private
exports anymore
2020-04-30 02:06:19 +03:00
John Keiser 8b89232f12 Merge pull request #834 from simdjson/jkeiser/normal-checkperf-windows
Compare against master on Windows
2020-04-29 15:28:06 -07:00
John Keiser c3dec1a5ea Default SIMDJSON_GOOGLE_BENCHMARKS to ON. 2020-04-29 15:21:43 -07:00
Furkan Usta 44b06d70e8 CMake: Link Threads the old-style
If linked against Threads::Threads target while building static libraries, cmake cannot find the
threads library while trying to use the installed target afterwards
2020-04-29 23:47:36 +03:00
Furkan Usta eee07e6cfd Use the same export name for all targets 2020-04-29 23:47:27 +03:00
Daniel Lemire 5051c27c3d Update HACKING.md 2020-04-29 15:57:37 -04:00
John Keiser 76f3506ac5 Compare against master on Windows 2020-04-29 12:55:53 -07:00
Daniel Lemire 5d7a84fad7 Update HACKING.md 2020-04-29 14:05:50 -04:00
Daniel Lemire dec161ed26 Update CONTRIBUTORS 2020-04-29 13:57:07 -04:00
Nong Li 0f9dbf84b7 Fix incorrect check for case insensitive key lookup (#824) 2020-04-29 13:55:28 -04:00
Daniel Lemire f0d5337818 Adding independent benchmarks using Google Benchmark (#826)
* Adding independent benchmarks using Google Benchmark
2020-04-29 13:53:54 -04:00
Daniel Lemire 4cd9de5c37 This will change the default of the parse benchmark so that it work over hot buffers (#827)
* This will change the default of the parse benchmark so that it work over hot buffers
by default, thus omitting memory allocation as part of the benchmark.

* Everyone should be using '-H' from now on.
2020-04-29 13:43:27 -04:00
John Keiser 76b0bfa7f5 Merge pull request #816 from simdjson/jkeiser/gitignore
Remove unneeded .gitignores
2020-04-28 15:19:57 -07:00
John Keiser 32d6b0eed4 Merge pull request #825 from simdjson/jkeiser/documents-sec
Don't include benchmark overhead in documents/s
2020-04-28 15:19:18 -07:00
John Keiser 127d962271 Remove unneeded .gitignores
Now that cmake is the only build system, we don't generate
binaries in the source tree anymore. Amalgamate also doesn't
leave anything around.
2020-04-28 13:21:30 -07:00
John Keiser 92d7af0881 Don't include benchmark overhead in documents/s 2020-04-28 13:15:01 -07:00
Daniel Lemire af12066f77 Update README.md 2020-04-28 11:38:34 -04:00
Daniel Lemire 2a1f8fa8f1 Provides support for clang under Windows. (#817) 2020-04-27 22:09:27 -04:00
Daniel Lemire 04e47bde84 Update basics.md 2020-04-27 16:16:40 -04:00
John Keiser 49da7e74cd usage.md -> basics.md (#823) 2020-04-27 16:03:19 -04:00
John Keiser 6ac47734c0 Only build fuzzers when fuzzing. (#822) 2020-04-27 16:02:19 -04:00
John Keiser 0e6ea76e88 Make checkperf work on Windows (#799)
* Make command line arguments work for Windows

* Run checkperf on Windows
2020-04-27 14:20:05 -04:00
PavelP 0514588175 Improves clang-cl build with Visual Studio (#809) 2020-04-27 08:59:32 -04:00
John Keiser 59d1212039 Merge pull request #803 from simdjson/jkeiser/windows-ci-parallel
Run Windows tests and builds in parallel
2020-04-26 12:07:54 -07:00
John Keiser d61cca6720 Run Windows tests and builds in parallel 2020-04-26 10:12:15 -07:00
John Keiser 8c74e88f16 Merge pull request #740 from pps83/master-amalgamate
Make amalgamated functionally equivalent to src/simdjson.cpp
2020-04-26 08:17:49 -07:00
PavelP 46cf512032 Add cmake /build dir to gitignore (#800)
+ ignore build* top level dirs
2020-04-26 10:11:16 -04:00
Pavel P 24a185d26b Amalgamate src/simdjson.cpp as-is
amalgamation.sh shouldn't change contents of src/simdjson.cpp by forcing dmalloc.h that didn't exist in non-amalgamated version and shouldn't change order of includes by placing simdjson.h at the top

fixes #739
2020-04-26 08:27:19 +06:00
Daniel Lemire b99a7344c9 missing spaces. 2020-04-25 22:26:18 -04:00
Daniel Lemire c6a4fb1e13 Update README.md 2020-04-25 22:21:12 -04:00
Daniel Lemire e2718fe845 Update HACKING.md 2020-04-25 11:30:54 -04:00
Daniel Lemire 76314280cb Update basics.md 2020-04-25 11:24:41 -04:00
Daniel Lemire d6716218bd details. 2020-04-24 20:12:02 -04:00
Daniel Lemire 9371a92122 Update CONTRIBUTING.md 2020-04-24 19:52:16 -04:00
Daniel Lemire e8b030ad17 Fixes issue https://github.com/simdjson/simdjson/issues/774 (#794) 2020-04-24 11:36:00 -04:00
Daniel Lemire e0180b4849 Update issue templates 2020-04-24 09:57:10 -04:00
Daniel Lemire 3901bbb401 Update README.md 2020-04-24 09:33:29 -04:00
Daniel Lemire 76bebfd798 Update issue templates 2020-04-24 09:32:26 -04:00
Daniel Lemire 3013166d8d Update issue templates 2020-04-24 09:26:01 -04:00
Daniel Lemire 8596e702ac Delete custom.md 2020-04-24 09:20:31 -04:00
Daniel Lemire 414bf4a296 Update issue templates 2020-04-24 09:19:37 -04:00
Daniel Lemire 0daa01edef Update issue templates 2020-04-24 09:18:22 -04:00
Daniel Lemire 98abd96075 Update CONTRIBUTING.md 2020-04-24 09:09:39 -04:00
Daniel Lemire 8e3fc826e2 adding doxygen badge 2020-04-24 07:19:16 -04:00
Daniel Lemire c750095241 Trying to improve a bit. (#791)
* Trying to improve a bit.

* Correcting typo
2020-04-23 22:36:08 -04:00
John Keiser 2a0c0c0ad2 Merge pull request #788 from simdjson/jkeiser/remove-makefile
Remove Makefile
2020-04-23 19:33:38 -07:00
John Keiser 92f3bb89c3 Remove Makefile and update CI not to use it 2020-04-23 19:33:20 -07:00
Daniel Lemire ac0e6c5e6e Update basics.md 2020-04-23 22:01:20 -04:00
Daniel Lemire f397b6fedf Another example. (#790)
* Another example.

* Adding a reference to error chaining.
2020-04-23 21:48:41 -04:00
John Keiser 1d069e5077 Split simdjson-flags and cmakecache into include files 2020-04-23 17:06:35 -07:00
John Keiser c5684a6278 Merge pull request #786 from simdjson/jkeiser/sdl-warnings
Enable /sdl warnings on Windows
2020-04-23 17:04:32 -07:00
Daniel Lemire f3ac0be0e6 Merge branch 'master' of github.com:simdjson/simdjson 2020-04-23 18:39:56 -04:00
Daniel Lemire 18c9468af5 Fixed typo 2020-04-23 18:39:32 -04:00
John Keiser 32bc0da362 Regenerate singleheader 2020-04-23 15:37:08 -07:00
Daniel Lemire c9a3800ce7 Let us drop libc++ (#787) 2020-04-23 18:33:38 -04:00
ostri d4239aaa8f default initialisaiton (#779)
* padded_string.* default initialisation
parsedjson_iterator - copy constructor; depth_index not necessary
2020-04-23 18:32:11 -04:00
Daniel Lemire 4f72d5cfac This adds another example (#785) 2020-04-23 18:29:28 -04:00
John Keiser 66acab4130 Enable /sdl warnings on Windows 2020-04-23 15:12:21 -07:00
John Keiser 9e9e3373e0 Merge pull request #784 from simdjson/jkeiser/reamalgamate
Remalgamate when source/header files change
2020-04-23 13:57:53 -07:00
Daniel Lemire 502fee1b45 Fixes the way conditions are checked on variables so it is correct. (#780) 2020-04-23 16:01:40 -04:00
Daniel Lemire 4d0c7d706d Warn 32-bit users about their doom. (#783) 2020-04-23 16:01:19 -04:00
John Keiser 6cd418b60a Automatically re-amalgamate when source/headers change 2020-04-23 12:17:39 -07:00
John Keiser a3b39dfd1a Test against headers amalgamated from current source 2020-04-23 12:17:37 -07:00
Daniel Lemire 74da47e286 Building from the single-header files alone everywhere, including under Visual Studio (#778)
* The idea here is to build from the single-header files alone.

* Testing the headers from the repo.
2020-04-23 15:07:26 -04:00
John Keiser 587ba9bec0 Merge pull request #728 from simdjson/jkeiser/cmake-fuzz-noexceptions
Compile fuzzers without exceptions, run as part of tests
2020-04-23 10:13:33 -07:00
Daniel Lemire 382392e03b This should enable -Weffc++ (#777)
* Enabling -Weffc++
2020-04-23 13:03:04 -04:00
John Keiser 409948a0f9 Update amalgamated headers 2020-04-23 09:19:26 -07:00
Daniel Lemire 34919ca394 Update README.md 2020-04-23 12:04:46 -04:00
Daniel Lemire 0d1c574cb1 A few more changes... (#775)
* More nitpicking.
2020-04-23 11:36:52 -04:00
Daniel Lemire c564815931 Merge branch 'master' of github.com:simdjson/simdjson 2020-04-23 11:30:09 -04:00
Daniel Lemire f6fb667ac1 Being more explicit. 2020-04-23 11:25:20 -04:00
Daniel Lemire 832bbe734d Extra details. 2020-04-23 11:20:12 -04:00
Daniel Lemire 7a2fda891c Merge branch 'master' into jkeiser/cmake-fuzz-noexceptions 2020-04-23 10:29:14 -04:00
Daniel Lemire e50c239a2e Added Matjaž Ostroveršnik. 2020-04-23 10:18:56 -04:00
Daniel Lemire cc7c8d92da We do not use the CC compiler. 2020-04-23 10:14:19 -04:00
ostri 87acab0846 elimination of most of g++ -Weffc++ warnings (#764)
Co-authored-by: Matjaž Ostroveršnik <ostri@localhost.localdomain>
Co-authored-by: Daniel Lemire <lemire@gmail.com>
2020-04-23 10:06:44 -04:00
John Keiser f43459d476 Merge pull request #759 from simdjson/jkeiser/wconversion
Compile with conversion warnings enabled
2020-04-22 20:53:20 -07:00
Daniel Lemire e030f02776 Merge branch 'master' into jkeiser/wconversion 2020-04-22 22:03:34 -04:00
John Keiser 10f2d01e7f Run most build flavors on VS2019 (#761) 2020-04-22 21:59:45 -04:00
Daniel Lemire 80dbf9a32a We should not enable checkperf on anything but Linux. (#762) 2020-04-22 21:54:03 -04:00
Daniel Lemire 185274e70f Let us see if we can test with libc++. (#732)
* Let us see if we can test with libc++.
* Fixed spacing.
2020-04-22 21:24:42 -04:00
Daniel Lemire f0ac55ec0c testing on freebsd (#768)
* Adding cirrus tests
* Adding cirrus badge.
2020-04-22 21:22:09 -04:00
Daniel Lemire 44544635dd More debugging effort. 2020-04-22 17:55:28 -04:00
Daniel Lemire 3c594b1037 Debugging... 2020-04-22 17:30:19 -04:00
Daniel Lemire ea7100e8c4 More tweaking. 2020-04-22 17:22:46 -04:00
John Keiser a198abc485 Use int as index to reduce cast operations
Decreases the number of instructions per block by almost 1
2020-04-22 14:21:33 -07:00
John Keiser d4a37f6ef5 Enable conversion warnings on Linux and Windows 2020-04-22 14:21:30 -07:00
John Keiser a5c9c31231 Merge pull request #755 from simdjson/jkeiser/windows-deprecation-warning
Reenable deprecation warnings on Windows
2020-04-22 14:18:28 -07:00
Daniel Lemire e3ec78a832 Trying again. 2020-04-22 17:18:08 -04:00
John Keiser a116e68a47 Merge pull request #729 from simdjson/jkeiser/cmake-amalgamate
Add amalgamation support to cmake
2020-04-22 14:16:10 -07:00
Daniel Lemire e7084de166 Let us try this way... 2020-04-22 17:14:57 -04:00
Daniel Lemire 398eda6365 Merge branch 'master' of github.com:simdjson/simdjson 2020-04-22 17:06:10 -04:00
Daniel Lemire 349abf5ee6 Hopeful attempt at using cirrus (for FreeBSD) 2020-04-22 17:05:50 -04:00
John Keiser 4ce40b1975 Reenable _CRT_SECURE_NO_WARNINGS 2020-04-22 10:46:59 -07:00
Daniel Lemire 536fe28f8f Being explicit regarding the initialization of two member variables. (#765) 2020-04-22 12:46:55 -04:00
John Keiser 3e9e14f4d6 Reenable deprecation warnings on Windows 2020-04-22 08:53:19 -07:00
John Keiser c8140068ad Merge pull request #760 from simdjson/jkeiser/remove-unneeded-wd-flags
Remove unneeded /WD flags on Windows
2020-04-22 08:21:03 -07:00
John Keiser db314bc381 Make fuzzing work without exceptions 2020-04-21 17:33:47 -07:00
John Keiser 499a26b152 Remove /D_CRT_SECURE_NO_WARNINGS on Windows 2020-04-21 16:56:13 -07:00
John Keiser a9cdb5be50 Remove unneeded /WD flags on Windows 2020-04-21 16:32:34 -07:00
Daniel Lemire 5f04208dbd This removes the problematic use of the intrinsic _addcarry_u64 for Visual Studio (#758)
in the ARM 64-bit kernel. This intrinsic does not appear in the documentation
https://docs.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics?view=vs-2019
and should probably not be used. Note that we expect the compiler to produce
efficient code out of our implementation.
2020-04-21 17:13:22 -04:00
PavelP ffaa292006 Master vs2019 x86 compile fixes (#743)
* Added bitexact implementations of _BitScanForward64 and _BitScanReverse64 for VS2019 32-bit builds

* Added bitexact implementations of _umul128 for VS2019 x86, arm, arm64 builds

* Implement mul_overflow for VS2019 arm64 builds

 + implement mul_overflow using __umulh (msvc/clang results: https://godbolt.org/z/smRwA7)

* Added Win32 for VS2019 to .appveyor.yml

* Update amalgamated headers (fix x86 builds with VS2019)
2020-04-21 14:42:53 -04:00
John Keiser d3e44b1108 Add amalgamation support to cmake 2020-04-20 19:50:51 -07:00
John Keiser fbf274a42b Merge pull request #727 from simdjson/jkeiser/cmake-checkperf
Add checkperf to cmake
2020-04-20 19:45:45 -07:00
Daniel Lemire d94cd65dfd We used to have a requirements section which went away. I think it is required. (#749) 2020-04-20 19:03:26 -04:00
Daniel Lemire 3c1b403c4e Fixing typo. 2020-04-20 18:19:38 -04:00
John Keiser 75564453b3 Don't checkperf sanitize, don't do sanitize-gcc7 2020-04-20 11:23:19 -07:00
John Keiser 3091e2dc0e Add fallback, westmere and unthreaded checkperf 2020-04-20 11:18:40 -07:00
John Keiser fc50a36cc5 Make checkperf build master with the same options 2020-04-20 11:14:46 -07:00
John Keiser 9bf9fba2ec Add checkperf to cmake 2020-04-20 11:14:46 -07:00
John Keiser 121615da70 Merge pull request #753 from simdjson/jkeiser/fix-cmake-error
Fix cmake error when SIMDJSON_COMPETITION=OFF
2020-04-20 11:11:57 -07:00
John Keiser 53d28a713c Fix cmake error when SIMDJSON_COMPETITION=OFF 2020-04-20 10:49:40 -07:00
John Keiser cf37704193 Merge pull request #737 from simdjson/jkeiser/sanitize-gcc-9
Run sanitize using gcc 9
2020-04-20 10:20:57 -07:00
Daniel Lemire 38289fe381 Tweaking a sentence (#747) 2020-04-20 11:46:02 -04:00
John Keiser f9337a1111 Update amalgamated headers 2020-04-19 11:38:02 -07:00
John Keiser 6d059b479f Add sanitize-gcc9 test 2020-04-19 10:55:50 -07:00
John Keiser b6e23b2d3e Compile and run tests in parallel (-j) 2020-04-19 10:55:23 -07:00
John Keiser e5e6a46c37 Consolidate multi-implementation tests
Uses SIMDJSON_FORCE_IMPLEMENTATION to switch the implementation at test
time.
2020-04-19 09:59:49 -07:00
John Keiser 22b9a53bef Add SIMDJSON_FORCE_IMPLEMENTATION 2020-04-18 18:21:56 -07:00
John Keiser 3be81e3206 Merge pull request #720 from simdjson/jkeiser/prune-circle-tests
Run fewer redundant steps and configs in CircleCI
2020-04-17 13:02:54 -07:00
John Keiser ff09b6c824 Run fewer redundant steps and configs in CI 2020-04-17 12:23:05 -07:00
John Keiser a8e892ba90 Merge pull request #718 from simdjson/jkeiser/cmake-werror
Treat warnings as errors
2020-04-16 17:48:34 -07:00
John Keiser 289cc3e7a0 Treat warnings as errors during compilation 2020-04-15 19:59:38 -07:00
John Keiser 7480b87e07 Merge pull request #693 from simdjson/jkeiser/cmake-quickstartcpp
Add C++11 tests to cmake
2020-04-15 19:53:14 -07:00
Daniel Lemire befa6423be This massively improves the performance of tight loops relying on a type() call. (#721)
* This massively improves the performance of tight loops relying on a type() call.

* Adding a few more benchmarks
2020-04-15 20:45:40 -04:00
John Keiser fd418f568c Fix c++11 warnings on clang
- namespace x::y is C++17
- static_assert requires message in C++11
2020-04-15 17:27:48 -07:00
John Keiser 09cf18a646 Add C++11 tests to cmake
- Add simdjson-flags target so callers don't have flags forced on them
2020-04-15 17:26:25 -07:00
Daniel Lemire 326c175dcb Massive performance boost for get<double>. (#719)
* Massive performance boost for get<double>.
2020-04-15 20:09:45 -04:00
Daniel Lemire 6d7c77ddc1 Let us try to check with the exceptions disabled. (#707)
* Tweaking code so that we can run all tests with exceptions off.
* Removing SIMDJSON_DISABLE_EXCEPTIONS
2020-04-15 16:45:36 -04:00
Daniel Lemire efd706528b Minor tweaks to the CMake. 2020-04-15 10:19:05 -04:00
Daniel Lemire b523c43927 Can we provide a size() function to arrays and objects? (eager approach) [TO BE MERGED] (#690)
* This is an implementation of "size()" for arrays and objects.
* Adding benchmark
* Adding a size() remark in the documentation.
* Extending size() to result types.
2020-04-15 10:15:48 -04:00
Paul Dreik 75545ff70d ref qualify parser methods to avoid use of dangling objects (#703)
To avoid using data belonging to a temporary, the parse functions are ref qualified to get a compile error if used on an rvalue. See https://github.com/simdjson/simdjson/issues/696

Compilation tests are also added, to make sure bad usage fails to compile.

Reviewed by jkeiser.
2020-04-15 09:57:52 +02:00
Daniel Lemire 3c6ef83046 Trying to correct the documentation so that it actually describes how the code behaves. (Attempt two) (#712)
* Trying to correct the documentation so that it actually describes how the code behaves.

* tweaking the wording.

* Improving.

* Removing confusing sentence.

* Fixing formatting.

* Now with working example, tested.

* Added a smaller piece of code
2020-04-14 22:31:21 -04:00
John Keiser b9ac0a79f1 Merge pull request #715 from simdjson/jkeiser/thorough-type-tests
Test more variants of cast, get, etc.
2020-04-14 16:08:36 -07:00
Daniel Lemire 8539896f3d It is inconvenient to be unable to print a padded_string. (#713)
* It is inconvenient to be unable to print a padded_string.

* Allows us to print the padded_string even when it is embedded in result object when exceptions are enabled.
2020-04-14 19:07:32 -04:00
John Keiser a3b508ceff Test get<>(), exception vs. no exception, explicit vs. implicit cast 2020-04-14 13:18:42 -07:00
Daniel Lemire 334a486737 Tweaking the doxygen. (#700)
* Tweaking the doxygen.

* Fixing typo.
2020-04-14 11:31:46 -04:00
Daniel Lemire d7370cc916 Let us document the relationship between a parser instance and the parsed document. (#699) 2020-04-14 08:30:06 -04:00
Paul Dreik 92c34f7f38 do not use deprecated apis in the fuzzers (#705)
* move from deprecated interface in fuzz dump raw tape

* update fuzz_dump to the non deprecated replacement

* replace use of deprecated api

* hopefully fix windows build
2020-04-14 07:45:30 +02:00
Paul Dreik 93328c8d6d improve the fuzzer documentation, refresh links (#697) 2020-04-12 17:49:40 -04:00
Paul Dreik 5710ec13d4 switch to clang 9 in the github action (#695)
The upstream convenience script from llvm does not support installing clang 8 anymore.
2020-04-12 20:02:45 +02:00
Paul Dreik fa637fcecb Enable the minifier fuzzer (fixes #530) (#694)
This enables the minify fuzzer, which has been disabled because it did not pass the oss-fuzz instrumentation test. Now it does, after changes in simdjson (https://github.com/lemire/simdjson/issues/186).

 * get minify running (api change)
 * disable benchmarks when compiling fuzzers
 * catch exceptions from the minify fuzzer
 * enable repeated corpus creation without recursive inclusion of zip
 * remove leftover comment
2020-04-12 18:47:24 +02:00
Daniel Lemire 4af7d6f108 Disabling threads on apple's hardware when optimizer is turned off (#692)
* Disabling threads on apple's hardware.

* Turns out that you can have your bread, your butter and you cake too!
2020-04-10 18:41:05 -04:00
John Keiser 0fd159dadb Merge pull request #689 from simdjson/jkeiser/cmake-test
Add most CI tests to cmake
2020-04-09 16:12:32 -07:00
John Keiser 1ff22c78b3 Add quickstart to cmake 2020-04-09 14:56:54 -07:00
John Keiser ceb1def55c Add quicktests, slowtests to cmake
- Also add testjson2json.sh
- Move test scripts to tests directory to consolidate concerns
2020-04-09 14:21:45 -07:00
John Keiser 893a1d8306 Merge pull request #685 from simdjson/jkeiser/cmake-competition
Add parsing competitions to cmake
2020-04-09 11:25:12 -07:00
John Keiser 3c91690e55 Don't run parsing competitions on Windows
(They currently use linux-specific stuff.)
2020-04-09 08:52:29 -07:00
John Keiser 6835dd73bc Only apply compile flags to simdjson 2020-04-09 08:52:29 -07:00
John Keiser 7317fe1440 Don't reinitialize submodules
Add ability to turn competitive benchmarks off (no need for submodules)
2020-04-09 08:52:29 -07:00
John Keiser 7b58fea911 Add benchmark competitions to cmake 2020-04-09 08:52:29 -07:00
John Keiser c1ff74c9a6 Add issue150 test 2020-04-09 08:52:29 -07:00
John Keiser 6dabfa176a Add competition libraries 2020-04-09 08:52:29 -07:00
John Keiser 0714f5fc67 Create dependencies/CMakeLists.txt 2020-04-09 08:52:29 -07:00
John Keiser 3b1b1bfd48 Merge pull request #684 from simdjson/jkeiser/cmake
Simplify cmake, add more of our tests into it
2020-04-08 16:02:47 -07:00
John Keiser 218c867f46 Disable failing VS2017 tests in cmake 2020-04-08 14:58:28 -07:00
Daniel Lemire 2bc12f9730 Removing useless line. 2020-04-08 17:55:25 -04:00
Daniel Lemire 5e564a8e0c Improving the doxygen. (#687) 2020-04-08 17:53:04 -04:00
John Keiser beaa6a9a7a Create simdjson-windows-headers interface library 2020-04-08 14:52:56 -07:00
John Keiser a9c8224f40 Add numberparsingcheck and stringparsingcheck tests 2020-04-08 14:52:56 -07:00
John Keiser 3dcc188d93 Add more tests to cmake 2020-04-08 14:52:56 -07:00
John Keiser 10b7556a37 Specify cmake tests, benchmarks and tools idiomatically 2020-04-08 14:52:56 -07:00
John Keiser 54b7291c34 Reference simdjson by name, don't specify include files individually 2020-04-08 14:52:55 -07:00
John Keiser 1e30b6e334 Compile under C++ 11 2020-04-08 14:00:13 -07:00
John Keiser 406240bae3 Support C++ 14 2020-04-08 14:00:13 -07:00
Daniel Lemire 74d9b41b7d Minor fixes to our documentation regarding thread safety. (#683)
* Minor fixes to our documentation regarding thread safety.

* A bit more pessimistic.
2020-04-08 16:41:08 -04:00
John Keiser ff0b0c54b7 Merge pull request #682 from simdjson/jkeiser/simplify-cars
Simplify cars example
2020-04-08 10:24:01 -07:00
John Keiser 6eec2d6b4f Simplify cars example 2020-04-05 09:15:20 -07:00
Daniel Lemire 5731c5437a Sanity test. (#675) 2020-04-04 16:39:37 -04:00
Daniel Lemire 04f14ec026 This adds a test for std::ignore (#674) 2020-04-04 11:53:03 -04:00
Dirk Eddelbuettel 12ed6336b1 remove three trailing semicolons that -pedantic dislikes (#673) 2020-04-02 21:06:25 -04:00
Daniel Lemire 3cb79e6977 Trying again. (#671) 2020-04-02 19:24:43 -04:00
John Keiser c5e21a2469 Merge pull request #669 from simdjson/jkeiser/type-switch
Add element.type() for type switching
2020-04-02 15:01:51 -07:00
John Keiser 13aee51011 Add element.type() for type switching 2020-04-02 14:07:19 -07:00
Daniel Lemire 53fca1b5e6 This is a first attempt at fixing issue 660. (#668)
* This is a first attempt at fixing issue 660. The hard part is not the fix by itself, the hard part is to make sure we never get caught with our pants down like that again.

I expect the CI tests will fail. Further commits will solve the issues.

* Setting the rpath properly.

* For the sanitize tests, we want verify the installation.
2020-04-02 15:18:58 -04:00
Daniel Lemire 7dad9fca0f In the future, we will bump the so version with even minor releases. (#664) 2020-04-01 16:08:28 -04:00
John Keiser b249d7c76c Merge pull request #666 from Mengesh/patch-1
Update basics.md
2020-04-01 10:54:01 -07:00
Marko Radišić 4060f64232 Update basics.md
Fix links to singleheader .h and .cpp
2020-04-01 17:13:53 +02:00
M. Zhou 5b2f7d3374 CMake: Bump SOVERSION to "1" due to the breaking changes in API/ABI. (fixes #661) (#662) 2020-04-01 09:53:45 -04:00
Daniel Lemire 3116e29d16 Release candidate (#655)
* Release candidate
2020-03-31 17:47:25 -04:00
Daniel Lemire d2406f2a22 More precise prerequisites 2020-03-31 17:41:12 -04:00
Daniel Lemire a648318900 Let us link to the API on simdjson.org (#654)
* Let us link to the API on simdjson.org

* Updating link to rust port.
2020-03-31 16:44:28 -04:00
Daniel Lemire 849f54e4f8 grammar 2020-03-31 10:29:20 -04:00
Daniel Lemire 61009fea3f Fixed botched edit. 2020-03-31 10:20:31 -04:00
Daniel Lemire 21dce6cca9 Displaying the numbers of documents parsed per second (#652)
* Some users are interested, as a metric, in the number of documents parsed per second.
Obviously, this means reusing the same parser again and again.

* Adding a sentence

* This update the parsingcompetition benchmark so that it displays the number of documents parsed per second.
2020-03-30 17:51:03 -04:00
John Keiser 56bc8a778d Merge pull request #647 from simdjson/jkeiser/doxygen-update
Deprecate more, show less private things in doxygen
2020-03-30 14:43:17 -07:00
John Keiser d93af1161d Remove set_capacity, replace with allocate
Makes allocation point more predictable
2020-03-30 13:49:54 -07:00
John Keiser 434776db1a Deprecate more things 2020-03-30 13:48:43 -07:00
John Keiser 6167e9cefc Update doxygen to not show deprecated/private things 2020-03-30 13:47:27 -07:00
John Keiser dc918d764e Merge pull request #646 from simdjson/jkeiser/quickstart-example
Compile all .md examples in CI
2020-03-30 13:44:43 -07:00
John Keiser 9906887151 Add error handling info to RELEASES.md 2020-03-30 13:44:03 -07:00
John Keiser b5a1017afa Update JsonStream.md -> parse_many to new API 2020-03-30 13:44:03 -07:00
John Keiser 7badc230a4 Add RELEASES.md 2020-03-30 13:44:03 -07:00
Daniel Lemire ab78482ee7 The testing files should not be all over the place. The "small" directory should be only for small files (duh). (#650) 2020-03-30 16:24:03 -04:00
Daniel Lemire 6369cf4dd9 Better documentation for the -H flag. (#651) 2020-03-30 15:44:04 -04:00
John Keiser 7656bd50ee Generate API docs at /api/docs 2020-03-29 17:01:12 -07:00
John Keiser 2115596ed3 Compile performance.md examples in tests 2020-03-29 16:28:34 -07:00
John Keiser 0e3453f7c2 Compile examples from implementation-selection.md 2020-03-29 16:28:34 -07:00
John Keiser 7ed65e42d7 Add actual examples from basics.md to readme_examples 2020-03-29 16:28:29 -07:00
John Keiser 835b640ebd Run quickstart example in CI 2020-03-29 15:45:12 -07:00
John Keiser 6ee3318531 Update amalgamated headers 2020-03-29 15:07:35 -07:00
John Keiser ae24fe3850 Merge pull request #644 from simdjson/jkeiser/indexer
Make object[] key lookup
2020-03-28 16:46:53 -07:00
John Keiser d4f4608dab Merge pull request #643 from simdjson/jkeiser/get
Replace as_X and is_X with get<T> and is<T>
2020-03-28 16:46:05 -07:00
John Keiser ea8a5020e2 Remove array indexer, make object indexer key lookup 2020-03-28 15:56:43 -07:00
John Keiser 622d9c9480 Replace as_X and is_X with get<T> and is<T> 2020-03-28 15:29:53 -07:00
John Keiser bb0c9547be Merge pull request #642 from simdjson/jkeiser/dom
Create "dom" namespace to put element and friends in
2020-03-28 14:44:51 -07:00
John Keiser 62da98aef6 Rename dom::stream to dom::document_stream 2020-03-28 13:42:24 -07:00
John Keiser 03746b966b Move document/element/etc. under dom 2020-03-28 13:42:21 -07:00
John Keiser de001da35b Merge pull request #640 from simdjson/jkeiser/simdjson-result
Use simdjson_result for all _result classes
2020-03-28 13:36:54 -07:00
Daniel Lemire 0da460ca13 Improving readability a bit, but introducing page breaks. 2020-03-28 16:18:57 -04:00
Daniel Lemire 450e19858b Minor fix to distinctuseridcompetition 2020-03-28 15:56:10 -04:00
John Keiser 836e1fc330 Use simdjson_result for all _result classes 2020-03-28 12:03:05 -07:00
John Keiser e836c28008 Deprecate parser error code methods
- Also make competitions compile without warnings
2020-03-28 10:13:20 -07:00
John Keiser fff4f921e4 Merge pull request #637 from simdjson/jkeiser/string-view
Use string_view instead of const char * where possible
2020-03-28 08:45:40 -07:00
John Keiser 9f265711a8 Merge pull request #629 from simdjson/jkeiser/parse-element
Return document::element from parser.parse()
2020-03-28 08:45:14 -07:00
Daniel Lemire 32afcd2e48 Better documentation for issue 70 (#638) 2020-03-27 19:44:01 -04:00
John Keiser 748df8d109 Use string_view instead of string/char* where possible 2020-03-27 13:11:41 -07:00
John Keiser 5ad405006c Return document::element from parse, load, parse_many, load_many 2020-03-27 12:24:41 -07:00
John Keiser 47859f3560 Merge pull request #628 from simdjson/jkeiser/remove-simdjson-move-result
Remove simdjson_move_result and remove deprecation warnings
2020-03-27 11:58:44 -07:00
Daniel Lemire 56d1b9a226 Update README.md 2020-03-27 14:12:30 -04:00
Daniel Lemire 1b6a31b277 Updating the performance numbers. (#634)
* Updating the performance numbers.

* Updating with growing file sizes.
2020-03-27 14:11:02 -04:00
John Keiser 90a7503181 Rename pj -> doc, fix a few other idioms 2020-03-27 09:22:46 -07:00
John Keiser e3efbcddc1 Cast padded_string to string_view instead of string 2020-03-27 09:13:11 -07:00
John Keiser c14b2fb36c Remove const char* variants for at_key()
- Remove const char * variants for at_key(), string_view covers them
- Add at_key_case_insensitive variants on *_result
- Add at(), at_key(), at_key_case_insensitive() tests
2020-03-27 09:09:08 -07:00
John Keiser f0f111b387 Make ParsedJson::Iterator backcompat test 2020-03-27 09:07:39 -07:00
Daniel Lemire c95e45d283 Typo 2020-03-26 21:08:54 -04:00
Daniel Lemire 52b8d50178 Going less crazy. 2020-03-26 21:04:24 -04:00
Daniel Lemire f581627d10 Let us get circle ci to be more aggressive 2020-03-26 20:10:34 -04:00
Daniel Lemire 6a8ec95a46 Various fixes. 2020-03-26 20:08:54 -04:00
Daniel Lemire b6c6680add Ported jsoncheck. 2020-03-26 19:56:04 -04:00
Daniel Lemire 5fb149f833 Converted inter_tests... 2020-03-26 19:52:17 -04:00
Daniel Lemire abb0bf9247 Fixed basictests 2020-03-26 19:40:29 -04:00
Daniel Lemire 8f3ddd3a73 Updating allparserscheckfile 2020-03-26 17:15:33 -04:00
Daniel Lemire 8f34e6714a Removing dead code. 2020-03-26 17:01:51 -04:00
John Keiser 56841bcede Fix conversion error on Windows 2020-03-26 12:48:07 -07:00
John Keiser 006cc2ed60 Remove simdjson_move_result 2020-03-26 12:48:03 -07:00
Daniel Lemire c79cf8d6bf Correcting indentation. 2020-03-26 12:01:35 -07:00
Daniel Lemire cf704cf81b Introducing strict tests (-Werror) only for GCC 8 and GCC 9. 2020-03-26 12:01:35 -07:00
Daniel Lemire 265d474ec8 Can we test with GCC 9 2020-03-26 12:01:35 -07:00
John Keiser 2e420169c3 Remove document::parse and document::load 2020-03-26 10:13:09 -07:00
John Keiser 5aec2671ea Remove JsonStream. Use parse_many() instead. 2020-03-26 09:25:07 -07:00
Daniel Lemire ab0e22a316 Trying to migrate distinctuseridcompetition to new API. (#624)
* Trying to migrate distinctuseridcompetition to new API.

* Ok. Good performance + got rid of old API.
2020-03-26 12:06:28 -04:00
John Keiser 06587824be Deprecate ParsedJson::Iterator 2020-03-25 18:26:51 -07:00
John Keiser a0bce440a6 Remove document_iterator, document::iterator, ParsedJsonIterator
Keep ParsedJson::Iterator only, without template, in same form as
it was in 0.2
2020-03-25 18:26:51 -07:00
John Keiser 26b15251e2 Split docs into multiple files 2020-03-25 18:25:14 -07:00
Daniel Lemire 1cf4fe405d Fixing issue 602 (#621) 2020-03-25 21:06:20 -04:00
Daniel Lemire 6b8f5d3354 Fixing issue 601 (#618)
* Fixing issue 601
2020-03-25 20:44:55 -04:00
John Keiser d5af359365 Fix compile error in master (#619) 2020-03-25 20:11:23 -04:00
Daniel Lemire 8769e42a56 Fixes issue 600 (#614)
* Fixes issue 600
2020-03-25 18:01:23 -04:00
John Keiser 7cde65aa6e This deprecates json_parse() and build_parsed_json(). 2020-03-25 14:19:24 -07:00
John Keiser e1b1500e3b Make _padded available without using namespace simdjson 2020-03-25 09:37:18 -07:00
John Keiser 2943a1c27f Reorganize README and add documentation 2020-03-25 09:36:48 -07:00
John Keiser b28cafc1d1 Remove backslash unescaping from JSON pointer impl
Also speed up non-escaped key lookup
2020-03-25 08:56:40 -07:00
Daniel Lemire 65f999b7b7 Fixes issue 598. (#608)
* Fixes issue 598.

* Dropping old code.
2020-03-25 11:22:16 -04:00
John Keiser 06d6636b97 Update jsonpointer tool to use new API 2020-03-24 16:35:39 -07:00
John Keiser eb5a1ea113 Merge pull request #596 from simdjson/jkeiser/json-pointer
Support JSON pointer in DOM navigation model
2020-03-24 09:46:39 -07:00
Daniel Lemire d84e70b6e5 migrating minifier competition to new API (#597)
* Migrating minifiercompetition to new API.
2020-03-24 10:13:55 -04:00
Daniel Lemire 7ff034504d Migrating parsingcompetition to new API. (#593)
* Migrating parsingcompetition to new API.

* Removing ParsedJson
2020-03-24 10:06:44 -04:00
Daniel Lemire dedf0c6a8d Merge branch 'master' into jkeiser/json-pointer 2020-03-24 09:51:59 -04:00
Daniel Lemire 5514ae3879 Adding another user. 2020-03-24 09:41:32 -04:00
Daniel Lemire 5af0dfb031 Putting back the fuzzer badge and extending the documentation. (#587) 2020-03-24 10:50:27 +01:00
John Keiser 0bcda5e384 Support JSON pointer in DOM navigation model 2020-03-23 15:05:20 -07:00
Daniel Lemire d1eef242c6 Trying to give better guidance regarding large files. (#594) 2020-03-23 18:04:22 -04:00
John Keiser ceee00b276 Use c++ instead of g++ in quick start for clang 2020-03-23 14:59:13 -07:00
John Keiser 6c2ab064cb Correct quick start
Not sure how this update didn't make it in, but it's necessary.
2020-03-23 14:59:13 -07:00
Daniel Lemire 772a5dc3d5 Updated script so that we generate a small and a large file. 2020-03-23 14:06:10 -04:00
Daniel Lemire 3e39a998ce Merge branch 'master' of github.com:lemire/simdjson
Conflicts:
	include/simdjson/jsonstream.h
2020-03-22 12:40:34 -04:00
Daniel Lemire 2867dc50fa Minor typo. 2020-03-22 12:39:01 -04:00
Bruce Mitchener c3c43769ae Fix typos. 2020-03-22 09:14:14 -07:00
John Keiser 36ceaa4452 Keep loaded_bytes in parser to reduce allocation
Also centralized memory ownership to make it easy to keep data around
2020-03-21 18:12:16 -07:00
John Keiser c34b1a1b2a Organize basic tests to make easier to turn on/off 2020-03-21 18:12:16 -07:00
John Keiser e4df0ca368 Add parse, parse_many, load, load_many tests 2020-03-21 18:12:16 -07:00
Daniel Lemire 8a91cecf41 testing only with ok documents. 2020-03-21 18:12:16 -07:00
Daniel Lemire 04e8710cf5 Testing issue 570 2020-03-21 18:12:16 -07:00
John Keiser e8b3f9eaad Support document::parse("[1,2,3]"_padded) 2020-03-21 11:15:20 -07:00
Daniel Lemire 23d6ec6cff Testing clang + libc++ (#579)
* Testing clang + libc++

* I love how docker makes permissions super complicated.
2020-03-21 11:23:11 -04:00
Daniel Lemire 0a6edae2dd Removing oss-fuzz logo since it is not clear what it means. 2020-03-20 22:08:48 -04:00
Daniel Lemire 0e45663ce8 Quickstart and intro (#575)
* Add quickstart

* Fix amalgamation

Co-authored-by: John Keiser <john@johnkeiser.com>
2020-03-20 22:07:52 -04:00
Daniel Lemire 80a9f4defd Updating appveyor 2020-03-20 16:59:47 -04:00
Daniel Lemire 8f1e4018c0 Updating drone link 2020-03-20 16:57:38 -04:00
Daniel Lemire afe36d0b36 Updating circleci link 2020-03-20 16:54:37 -04:00
Daniel Lemire 5d1e3efce8 faster minifier (#568)
* Fallback should use our scalar code.
* parse should have a nicer error message.
* Making it so that "minify" can use different architectures.
* Let us change the minifier competition so that it tests all implementations.
* Documenting the untaken optimization opportunity.

Co-authored-by: John Keiser <john@johnkeiser.com>
2020-03-20 16:14:47 -04:00
Daniel Lemire 293ec7aec5 Minor update. 2020-03-20 11:02:18 -04:00
Daniel Lemire 6cefeb338b std::tie does not work on some compilers (#567)
* std::tie workaround.

* Cleaner solution
2020-03-19 16:56:45 -04:00
John Keiser f1744f5495 Break out string/structural scanning from tokenizer 2020-03-18 10:40:06 -07:00
Daniel Lemire 5750a173ff We should not need to specify the architecture under ARM. (#564) 2020-03-18 08:24:21 -04:00
Daniel Lemire e3a4fd9f93 Minor tweaks on fallback. 2020-03-17 14:59:47 -07:00
John Keiser 5a071c1907 Remove TARGET_FALLBACK 2020-03-17 14:59:47 -07:00
John Keiser 7cf3a7511b Add fallback implementation to CI
- Also add SIMDJSON_IMPLEMENTATION_HASWELL/WESTMERE/ARM64/FALLBACK=1/0 to
enable/disable various implemnentations
2020-03-17 14:59:47 -07:00
John Keiser af203aaf86 Add fallback parser for pre-SSE4.2 machines 2020-03-17 14:59:47 -07:00
John Keiser 8e2c06cb0e Compile with -fno-exceptions 2020-03-17 13:54:37 -07:00
John Keiser 1a5d8f1957 Add tests for SIMDJSON_EXCEPTIONS=0, add tie() support 2020-03-17 13:54:37 -07:00
John Keiser 03c828c7ad Add SIMDJSON_EXCEPTIONS=ON to turn on exception interface 2020-03-17 13:54:37 -07:00
Daniel Lemire 758dc511fb Better comment. 2020-03-17 14:22:40 -04:00
Daniel Lemire 0164723a8e Adding comments. 2020-03-16 20:17:48 -04:00
Daniel Lemire 032936a7b5 Update README.md 2020-03-16 08:14:25 -04:00
Daniel Lemire da3e064fc7 Added a comment. 2020-03-15 22:35:21 -04:00
Daniel Lemire 317fc6ba0e accurate number parsing (#558) 2020-03-15 22:30:21 -04:00
John Keiser 1aaad223c0 Simplify atom parsing 2020-03-13 19:00:08 -07:00
John Keiser 81c86d7090 Structural iterator 2020-03-13 19:00:08 -07:00
Daniel Lemire d9a9fd387d Adding a stress test. 2020-03-13 18:59:15 -07:00
John Keiser 0c190b165c Benchmark minify 2020-03-13 18:59:15 -07:00
John Keiser acc7bd79b0 Support cout << json, cout << minify(json) 2020-03-13 18:59:15 -07:00
John Keiser e4e89fe27a Fix parse benchmarker (#554)
* Fix parse benchmarker

* Make CI fail when parse doesn't work
2020-03-13 16:19:21 -04:00
Daniel Lemire 24551db0c8 Fixing typo 2020-03-13 14:51:34 -04:00
Daniel Lemire 12e6611ba4 Fix for printf. 2020-03-13 14:44:21 -04:00
Daniel Lemire fb15886a1c Simple fix for name erasure. 2020-03-13 14:41:19 -04:00
Daniel Lemire 06c1dc3a29 Adding NDEBUG to release (#557)
* Adding NDEBUG to release

* Asserts are deleted with NDEBUG. We want hard asserts.
2020-03-13 14:37:02 -04:00
Daniel Lemire 89d9de2353 Adding a check to see whether document::stream copy constructor and assignment actually compile (#556)
* Currently, document::stream contains an attribute that is a reference:

```
      document::parser &parser;
```

Yet we try to have it default on the move operator:

```
  stream &operator=(document::stream &&other) = default;
  stream &operator=(const document::stream &) = delete; // Disallow copying
```

```
  stream(document::stream &&other) = default;
  stream(const document::stream &) = delete; // Disallow copying
```

I am not sure what the move is supposed to do with the reference.

I cannot find where we test the copy constructor and assignment. This has been concerned that it is either dead code or buggy code.

* Remove non-working, unnecessary move constructors

* We still want to disallow copies.

Co-authored-by: John Keiser <john@johnkeiser.com>
2020-03-13 12:53:42 -04:00
Daniel Lemire 12c85d3e23 If we are going to have a google benchmark flag, we better make sure … (#551)
* If we are going to have a google benchmark flag, we better make sure that we test it out minimal (it should build).

* Fix bench_dom_api

Co-authored-by: John Keiser <john@johnkeiser.com>
2020-03-12 17:48:30 -04:00
John Keiser a5afec1f94 Make #defines into simdjson::constants 2020-03-11 19:16:29 -07:00
John Keiser ac0899c043 Add error tests, doc_ref_result[] chaining 2020-03-11 17:19:41 -07:00
John Keiser 40c6213d7e Add parser.load() and load_many() to load files 2020-03-11 17:19:41 -07:00
John Keiser d140bc23f5 Automatically allocate memory as needed in parse 2020-03-11 16:14:54 -07:00
John Keiser 00f0859e1f Add ability to run multiple files 2020-03-11 16:05:05 -07:00
Daniel Lemire a0b2fab6fa Documenting the fact that we are doing ok with tiny documents. (#549) 2020-03-11 16:21:35 -04:00
Daniel Lemire f669aafcf2 Correcting typo 2020-03-09 17:55:26 -04:00
John Keiser 66a2807210 Rename invalid_json to simdjson_error 2020-03-06 16:12:51 -08:00
John Keiser c3009eb324 Run make amalgamate in Drone CI for x64-amalgamated-build 2020-03-06 15:41:51 -08:00
John Keiser 3bdfe167de Support cout << error 2020-03-06 15:41:51 -08:00
John Keiser 31e8a12e88 Make error_message(error_code) return C string
- Also move all error message logic to include inline
2020-03-06 15:41:51 -08:00
John Keiser ebbfdcd35a Amalgamate before running amalgamated tests 2020-03-05 12:04:45 -08:00
John Keiser 9a7c8fb5be Use parse_many in examples/tests/docs 2020-03-05 12:04:45 -08:00
John Keiser cfef4ff2ad Create parser.parse_many() API 2020-03-05 12:04:45 -08:00
John Keiser b2220d6157 Fix amalgamation 2020-03-05 11:13:25 -08:00
John Keiser 5ff941ae3d Include <simdjson> directly, move document_parser_callbacks to top level 2020-03-04 14:26:54 -08:00
John Keiser 1c922d3b73 Fix JsonStream reference to parser on_error 2020-03-04 14:26:54 -08:00
John Keiser b23dd28a06 Declare functions inline to surface "undefined" errors earlier 2020-03-04 14:26:54 -08:00
John Keiser a55f41a24a Move JsonStream inline implementation to inline .h 2020-03-04 14:26:54 -08:00
John Keiser 5525c6f729 Stop using jsoncharutils.h in JsonStream 2020-03-04 14:26:54 -08:00
John Keiser eb147d9868 Mark jsonformatutils.h/isadetection.h internal
- Move jsonformatutils.h to internal/jsonformatutils.h (it is used by
document::print_json)
- Move isadetection.h to src/ (it is only used internally)
2020-03-04 14:26:54 -08:00
John Keiser f58a5d534e Move parser inline implementation to .cpp 2020-03-04 14:26:54 -08:00
John Keiser b3ea8c406e Add simdjson.cpp for unified use (#515) 2020-03-04 10:12:27 -08:00
John Keiser 99667f7c55 Create top level simdjson.h (#515)
- Allows everyone to #include the same way, singleheader or not.
2020-03-04 10:12:27 -08:00
John Keiser 0b21203141 Document navigation API 2020-03-02 14:49:03 -08:00
Daniel Lemire 9a9ca974c2 Adding Clang Build Analyzer under "real-world usage" 2020-03-02 17:03:11 -05:00
Daniel Lemire 140e4dde3d Update README.md 2020-02-27 18:00:43 -05:00
Daniel Lemire 68670301e3 Adding instructions regarding how to check for an unsupported CPU (#508)
* Adding instructions.

* Slighty more documentation.
2020-02-25 11:09:51 -05:00
Daniel Lemire a98d841983 Minor tweaks. (#507) 2020-02-24 17:13:10 -05:00
Daniel Lemire 332b764cc4 Minor nitpick (don't start sentence i 2020-02-24 16:44:03 -05:00
John Keiser 560f0742cc Update amalgamated headers 2020-02-22 10:42:13 -08:00
John Keiser 910f272467 Add parser implementation interface and selection API (#501)
* Make architecture implementations virtual functions

- Easier to add new architectures (add implementation to implementation.cpp)
- Easier to add new algorithms / functions to architecture selection
(add to implementation.h, implement)
- Automatically select best implementation in static initialization
- Allow user to explicitly select implementation with a string (i.e.
parameter)
- Allow user to inspect current implementation name/description
- Allow user to list available implementations
- Eliminate architecture enum and architecture-based templating
- Add noexcept in non-inline functions

* Move implementation static methods to their own classes

* Detect best supported implementation on first use

* available_implementationsI() -> available_implementations
2020-02-21 16:34:27 -05:00
John Keiser b6423a3426 Remove googletest entirely as benchmark dependency (#504) 2020-02-21 12:52:38 -05:00
Tim Gates 4d2736ffa9 Fix simple typo: submiting -> submitting
Closes #498
2020-02-18 13:46:06 -08:00
John Keiser 4dc2adf7f8 Update README, add README examples 2020-02-18 08:37:07 -08:00
John Keiser da34f9a253 Add Google Benchmark for calling conventions
- disable it on ubuntu 18.04 tests, which fail for [really can't figure
out why]
2020-02-18 08:37:07 -08:00
John Keiser 1f76737510 Make valstat-ish parse APIs 2020-02-18 08:37:07 -08:00
John Keiser bc8bc7d1a8 Lowercase Architecture and ErrorValues (#487)
ErrorValues -> error_code, Architecture -> architecture
2020-02-14 15:21:28 -08:00
Daniel Lemire 083569fca8 This code is terrible and should not be there. (#496) 2020-02-13 07:38:11 -05:00
Frank Wessels a8903d9765 Update reference for simdjson-go to minio (#492) 2020-02-11 12:58:04 -05:00
John Keiser 8e7d1a5f09 Separate document state from ParsedJson
This creates a "document" class with only user-facing document state (no parser internals).

- document: user-facing document state
- document::iterator: iterator (equivalent of ParsedJsonIterator)
- document::parser: parser state plus a "docked" document we parse into (equivalent of ParsedJson)

Usage:

```c++
auto doc = simdjson::document::parse(buf, len); // less efficient but simplest
```

```c++
simdjson::document::parser parser; // reusable parser
parser.allocate_capacity(len);
simdjson::document* doc = parser.parse(buf, len); // pointer to doc inside parser
doc = parser.parse(buf2, len); // reuses all buffers and overwrites doc; more efficient
```
2020-02-07 10:02:36 -08:00
Daniel Lemire c879b56f41 Fixing logical error. 2020-02-07 10:44:17 -05:00
Daniel Lemire 4518f1fba1 Some minor nitpicking. 2020-02-07 10:41:45 -05:00
Daniel Lemire 5c59b3a775 Fixing memory leaks. (Minor issue.) 2020-02-07 10:29:15 -05:00
Yufei Huang 299dfcdd3c Update README.md (#486) 2020-02-07 07:56:16 -05:00
John Keiser 76c706644a Move stage 2 tape writing to ParsedJson (#477)
This is a first step to allowing alternate tape formats.
2020-02-04 14:28:42 -08:00
John Keiser 0c8f2b9d85 Make "make amalgamate" more automatic (#480)
- automatically include local includes in the right places
2020-02-03 12:51:24 -05:00
Daniel Lemire c924aaede9 Fix issue472: make JsonStream a template. (#473)
* Fix issue472: make JsonStream a template.

* Adding missing include.

* Tweaking headers and some minor formatting.

* Removing file from aggregation.

* Moving jsoncharutils

* Adding new header.

* Trying another header.

* Let us try to route around Visual Studio's nonesense.
2020-01-30 17:16:41 -05:00
Daniel Lemire 28710f8ad5 fix for Issue 467 (#469)
* Fix for issue467

* Updating single-header

* Let us make it so that JsonStream is constructed from a padded_string which will avoid dangerous overruns.

* Fixing parse_stream

* Updating documentation.
2020-01-29 19:00:18 -05:00
Daniel Lemire e695a19d11 Trying to fix issue 465 (#466)
* Trying to fix issue 465

* Actually testing

* Refreshing amal.

* Removing spurious ;
2020-01-27 11:25:23 -05:00
John Keiser 6978a0b8d4 Benchmark escapes (#464)
* Add escapes as a feature we benchmark

* Don't print effectiveness metric unless verbose is on
2020-01-27 09:58:14 -05:00
Daniel Lemire 6784530b8b Update CONTRIBUTING.md 2020-01-27 09:37:55 -05:00
Daniel Lemire 03d5fc33ca Update CONTRIBUTING.md 2020-01-27 09:34:29 -05:00
Daniel Lemire ba14232628 Fixing mem leak. (#461) 2020-01-27 09:31:12 -05:00
Daniel Lemire 1cdf5581f3 Adding a contributing document. 2020-01-25 12:32:15 -05:00
Daniel Lemire 3488c49d0a Basically, haswell processor should be able to count on lzcnt. (#458) 2020-01-22 16:52:55 -05:00
John Keiser adaef43bc6 Find all escaped characters with simpler algorithm (#450) 2020-01-22 14:11:14 -05:00
Daniel Lemire ce8fe1bdf6 This isolates a fix found in the large PR https://github.com/lemire/simdjson/pull/445 (#457) 2020-01-22 12:58:59 -05:00
Daniel Lemire fa04595d90 Correcting typo. 2020-01-22 11:08:53 -05:00
Daniel Lemire aea79912ec Adding a "get_corpus" benchmark. (#456)
* Adding a "get_corpus" benchmark.

* Improving portability.
2020-01-20 17:27:25 -05:00
Daniel Lemire 80b4dd2e8a Removing all stdout, stderr from main library. (#455)
* Removing all stdout,stderr from main library.
2020-01-20 16:03:15 -05:00
Daniel Lemire 48530b89ea adding link to R bindings 2020-01-20 14:55:31 -05:00
Daniel Lemire a4025788ae Commenting out one attribute when SIMDJSON_THREADS_ENABLED is off. (#453) 2020-01-20 11:18:29 -05:00
dbj c6f2f60b03 clarifications -- documentation update (#448) 2020-01-20 10:39:47 -05:00
Daniel Lemire 33060738b6 Making the project tag in simdjson more explicit and disabling LTO (#452)
* Making the project tag in simdjson more explicit

* Let us disable deliberately LTO.
2020-01-20 10:18:58 -05:00
Daniel Lemire ab6d4871d8 Adding haswell amal. tests (#447)
* Adding an extra test.

* Disabling the AVX-accelerated minifier.

* Updating amalgamation.
2020-01-15 19:49:11 -05:00
Daniel Lemire f87e64f988 Add option to make buffers hot and remove recent benchmarking changes (#443)
* This revert the code back to how it was prior to the silly "run two stages" routine and instead
adds an option to benchmark the code over hot buffers. It turns out that it can be expensive,
when the files are large, to allocate the pages.
2020-01-15 19:48:00 -05:00
Daniel Lemire 27861f6358 SIMDJSON_PADDING is now an absolute constant. This is temporary since
padding should go away once  https://github.com/lemire/simdjson/issues/174
is resolved.
2020-01-15 15:49:50 -05:00
Daniel Lemire f611b65bc0 This updates the minifier. (#446) 2020-01-15 13:45:32 -05:00
Daniel Lemire 2dc61fbdc4 Update README.md 2020-01-15 11:57:48 -05:00
Daniel Lemire 22be05400d Update README.md 2020-01-15 10:18:03 -05:00
Daniel Lemire a9f501fe7d Update README.md 2020-01-14 11:50:14 -05:00
Daniel Lemire e9077370ec Update README.md 2020-01-14 10:42:04 -05:00
Daniel Lemire a804351a76 I think that i and idx should be size_t (64-bit). (#438) 2020-01-13 17:42:52 -05:00
Daniel Lemire f97b655f02 Instead of emulating the whole parsing as stage 1 + stage 2, let us benchmark the real thing. (#441)
* Instead of emulating the whole parsing as stage 1 + stage 2, let us
benchmark the real thing.

* Adding explicit constructor.

* Adding warning to the benchmark user.

* Making re-running optional.
2020-01-11 10:14:22 -05:00
Daniel Lemire 1498b78342 Minor simplifications. 2020-01-10 14:07:57 -05:00
dbj 85e84fc1fa improved string padded (#440)
* dirent portable latest version

* improved

std::string argument passed by const reference
ctor added with std::string_view  argument
`allocate_padded_buffer()`  moved here with **optional** check on `length < 1`

* allocate_padded_buffer moved to padded_string.h
2020-01-10 10:15:48 -05:00
Daniel Lemire 833e5d8bf1 Update README.md 2020-01-09 17:01:41 -05:00
UKABUER 773883c486 Fix #420 (#421) 2020-01-09 09:56:43 -05:00
Daniel Lemire 6e5e0278c2 Exposing bug #420 2020-01-09 09:55:54 -05:00
Daniel Lemire 951c4bedf8 Simpler jsonstream (#436)
* One simplification.

* Removing untested functions.
2020-01-07 19:10:02 -05:00
dbj 9842e1f9d0 dirent portable latest version (#435) 2020-01-07 18:41:57 -05:00
Daniel Lemire 4c0c1c9830 Updating a comment. 2020-01-06 22:01:23 -05:00
Daniel Lemire 6706d6053e Upgrading gcc to gcc 8 2020-01-06 18:28:29 -05:00
Daniel Lemire 0a874a5063 Some tuning 2020-01-06 11:41:07 -05:00
dbj 2caa6e3370 C++ language version detection (#418)
* added visual_studio folder where visual_studio cmake generated, local artefacts are

* C++ version detection
2020-01-06 11:38:09 -05:00
Daniel Lemire a9e990251d removing left over debug 2020-01-04 12:50:04 -05:00
Daniel Lemire 7bde23590a Debugging jsonstream (#432)
Fixes #424 (and provide tests for it), as well as #401
2020-01-03 22:22:47 -05:00
Daniel Lemire 5042dd52ce This is implementing @jkeiser optimization idea. (#431) 2020-01-03 09:21:36 -05:00
John Keiser 3b9e6bff3c Print stage 2 information in feature benchmarker 2020-01-02 17:23:21 -07:00
Daniel Lemire a2d05b21ff Merge branch 'master' of github.com:lemire/simdjson 2020-01-02 15:27:00 -05:00
Daniel Lemire f4f5f670a2 Better documentation of the padding. 2020-01-02 15:25:03 -05:00
John Keiser 165e23773f Refactor stage 2 into structural_parser class 2020-01-02 13:12:22 -07:00
Daniel Lemire 8dbb598057 Adding another test 2020-01-02 14:22:43 -05:00
Daniel Lemire ba9dc12164 Adding tests motivated by https://github.com/lemire/simdjson/pull/430 2020-01-02 14:20:51 -05:00
Paul Dreik 399d08c86c use unique_ptr in class parsedjson (#417)
* refactor parsedjson to use unique_ptr instead of owning raw pointer
* fix a potential undefined behavior
* output only first cpu in /proc/cpuinfo
2019-12-31 14:31:45 -05:00
Daniel Lemire 6f799435b6 Removing commented out stuff. 2019-12-30 22:21:04 -05:00
Daniel Lemire 3d14154a29 Merge branch 'master' of github.com:lemire/simdjson 2019-12-30 15:08:55 -05:00
Daniel Lemire 7e331957c4 Adding ruby script. 2019-12-30 15:07:42 -05:00
Paul Dreik 4da06830f1 add new fuzzers for print_json and dump_raw_tape (#416)
after looking at the coverage report available at
https://storage.googleapis.com/oss-fuzz-coverage/simdjson/reports/20191222/linux/src/simdjson/report.html
2019-12-27 13:42:44 -05:00
Paul Dreik 27293cc1c1 don't add integers to string literals (#410)
* string literal + integer means unintended and incorrect pointer arithmetic

fixes a clang warning. it could not be triggered, because it can only be
triggered if the string given to getopt is not covered among the
cases in the switch.

* handle review comment
2019-12-24 20:19:22 +01:00
Daniel Lemire 2caac2b218 Adding two test files from https://github.com/lemire/simdjson/pull/404/files (#415) 2019-12-24 14:04:11 -05:00
Nexus Web Development 0dc80ccf21 Update README.md : Simple typo (#412)
Exemple to example.
2019-12-23 11:35:42 -05:00
Nexus Web Development f2b48ede4c Update JsonStream.md : simple typo (#413)
Exemple to example.
2019-12-23 11:35:09 -05:00
Stenal P Jolly 6cefdc2f5c Fix README typo (#407) 2019-12-23 11:08:55 -05:00
Paul Dreik 29e78413fe output failed fuzz cases (#409)
this is to get more debug output from cases like this:
https://github.com/lemire/simdjson/commit/c9cd8e62111a7b25c240bea08a175de5ec6f8def/checks?check_suite_id=351526761
https://pipelines.actions.githubusercontent.com/gFSIASDqcDhMdHkcuMJEdsUcdOsOrVBr8d56BjAkszcoMkibGp/_apis/pipelines/1/runs/274/signedlogcontent/3?urlExpires=2019-12-21T18%3A50%3A08.3387613Z&urlSigningMethod=HMACV1&urlSignature=DVF3u4jEs81xJP6Hmi8LLQlLOztJke8MsP62J0MXruQ%3D

which ends with:
2019-12-20T23:08:42.6830393Z ../src/generic/numberparsing.h:243:31: runtime error: 1e+311 is outside the range of representable values of type 'double'
2019-12-20T23:08:42.6836790Z SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../src/generic/numberparsing.h:243:31 in
2019-12-20T23:08:42.6837715Z MS: 2 InsertRepeatedBytes-CrossOver-; base unit: 47cf79b80bc84f2ec8b39c1c73daa6c1222ef624
2019-12-20T23:08:42.6866416Z artifact_prefix='./'; Test unit written to ./crash-3fa2f47d980d4bf5812af18fb0d0a0ce9b5cc65a
2019-12-20T23:08:42.6999448Z ##[error]Process completed with exit code 1.
2019-12-20T23:08:42.7030863Z Cleaning up orphan processes


The problem is that github actions does not store artifacts on failure.
2019-12-21 20:32:32 +01:00
Paul Dreik 8192e63a4b update apt repos before installing (#408)
This should hopefully resolve the recent errors in the github action
for running the fuzzers.
2019-12-21 19:23:51 +01:00
Daniel Lemire b2ebdb0d07 I think we can align the numbers better (so it is prettier). (#399)
* I think we can align the numbers better (so it is prettier).

* Remove space before %, align third line better

Co-authored-by: John Keiser <john@johnkeiser.com>
2019-12-20 19:58:49 -05:00
dbj 9c3828fefe STRINGIFY implemented (#402)
* STRINGIFY implemented

* SIMDJSON_THREADS_ENABLED def/undef
2019-12-20 07:57:00 -05:00
John Keiser 60916318f7 Show miss rate, make it more accurate 2019-12-18 14:38:25 -08:00
John Keiser d7c83397e4 lookup+cont-check algorithm 2019-12-18 14:37:21 -08:00
Daniel Lemire 1d621bba37 Being more explicit about EMPTY errors. 2019-12-18 14:39:48 +00:00
John Keiser e2f349e7bd Measure impact of utf-8 blocks and structurals per block directly 2019-12-17 11:41:13 -08:00
Daniel Lemire 102262c7ab Fixing issue386 (#396)
* Creating arch-specific bitmanipulation.h files.
* Improving system and compiler portability.
* We want to allow trailing_zeroes on zero inputs.
2019-12-16 19:09:18 -05:00
Daniel Lemire f02babe427 Adding analysis by @sebpop from https://github.com/lemire/simdjson/pull/391#issuecomment-565551462 2019-12-13 13:39:15 -05:00
Daniel Lemire fc6133b58f Fixes issue 388 (#394) 2019-12-11 08:13:29 -05:00
Daniel Lemire 2bd65fa444 Adding @mswilson to the list of contributors 2019-12-10 13:14:12 -05:00
mswilson d33208c7db Correct detection of NEON support (#392)
... as the test as it is currently implemented will always evaluate to true.

Fixes #389
2019-12-10 13:12:17 -05:00
Daniel Lemire c9cd8e6211 PMULL is slow on ARM64, let us not rely on it? (#391) 2019-12-09 17:15:34 -05:00
Daniel Lemire 74a96878bc Correcting the spelling of macOS. 2019-12-05 15:09:53 -05:00
Daniel Lemire 7e28708e1d Making it clearer that this is a 64-bit library 2019-12-05 09:18:49 -05:00
Daniel Lemire 1211c01ca1 Resolves issue 186 (#383)
* Resolves issue 186
https://github.com/lemire/simdjson/issues/186
2019-12-02 12:23:45 -05:00
Daniel Lemire f32b97733b Updating the json minifier benchmark to match that of the new API. 2019-12-02 10:46:03 -05:00
Jeremie Piotte 4e1c90f76f Fix memory allocation of the max_depth in JsonStream. 2019-11-28 13:55:31 -05:00
Daniel Lemire e63f258470 missed one 2019-11-26 14:51:40 -05:00
Daniel Lemire ede9f9117f Minor cleaning 2019-11-26 14:51:01 -05:00
Daniel Lemire 7c560fa137 Cleaning documentation. 2019-11-26 14:13:17 -05:00
piotte13 178a0842fe Refreshing the single header files. 2019-11-26 11:08:32 -05:00
piotte13 f345490cae Updating .gitignore for most popular IDEs 2019-11-26 10:59:18 -05:00
Jeremie Piotte db141e82c9 Specifying that RFC7464 is not supported 2019-11-26 10:33:33 -05:00
Jeremie Piotte f163155929 JsonStream documentation (#381)
* adding Multiline JSON competition chart to doc
* Completing the comments for JsonStream
* Adding a page for JsonStream's documentation.
2019-11-25 18:11:55 -05:00
John Keiser 9b6377fd80 Precalculate the ASCII path 2019-11-25 11:49:44 -08:00
John Keiser 7356b4532f Perform UTF-8 detection via flag lookup algorithm
- adds the alternative zwegner, range and lookup utf8 algorithms as well, for
ability to do "shootouts"
2019-11-25 11:49:44 -08:00
John Keiser 7d7bec856d Remove lookup_lower_4_bits
It's only a coincidence that it works in current uses: it doesn't do
what the name says. Particularly, if the high bit is 1 it will yield
0 even if the lower 4 bits would yield something else.
2019-11-25 11:49:44 -08:00
Paul Dreik c5504ef50b run the oss fuzz initial seed corpus in CI (#378)
This makes sure the seed corpus keeps being healthy.
2019-11-23 22:49:41 +01:00
Daniel Lemire 3658ff650d Delete Notes.md 2019-11-23 14:15:25 -05:00
Paul Dreik 6d14afd80e Make threads optional in the cmake build (#376)
Only the simdjson library should optionally depend on threads,
the executables that link to simdjson will get the dependency
indirectly.

* add option for controlling threads (default is on)
* add CI testing with threading on/off for msvc, gcc and clang
* fix an unrelated copy paste comment error in the cirlce ci build conf
2019-11-22 21:51:46 +01:00
Jeremie Piotte 6e5178efc4 Update CONTRIBUTORS 2019-11-21 16:49:07 -05:00
Jeremie Piotte 29fc51522a Introducing concurrency mode in JsonStream. (#373)
* JsonStream threaded prototype

* JsonStream Threaded version working. Still supporting non-threaded version.

* Fix where invalid files would enter infinite loop.

* SingleHeader update

* I will remove -pthread in cmake for now.

* Attempt at resolving the -pthread issue
2019-11-21 11:22:06 -05:00
Daniel Lemire 6cd8fb7982 Adding a getline benchmark (#344) 2019-11-20 20:33:16 -05:00
John Keiser ce824f8653 Decrease stage 1 step size to 64 bytes on Westmere/ARM
- Templatize scan_step() with STAGE1_STEP_SIZE
- Fix simd8::store()
- add NUM_CHUNKS to simd8
2019-11-18 21:58:07 -08:00
John Keiser 708f4a094d Move inline functions out of class definition for templating 2019-11-18 21:58:07 -08:00
Paul Dreik 2704b73399 Add fuzzer badge and improve fuzzer documentation (#367)
* Update Fuzzing.md

* add oss-fuzz badge
2019-11-13 16:57:20 +01:00
Paul Dreik 783ccd6c21 Add CI Fuzz job
This runs fuzzing for a short while, then executes the corpus through valgrind.
The extended corpus is uploaded to persistent storage on bintray.
2019-11-12 16:46:23 +01:00
Paul Dreik 3fd1c3b64a run short fuzzing and valgrind in github action 2019-11-11 22:17:32 +01:00
Daniel Lemire 58d249ca16 Introducing move assignments. (#363) 2019-11-09 10:34:32 -05:00
Jeremie Piotte bdc2b07339 Streams of JSON documents + Large files (>4GB) (#350) (#364)
* rough prototype working.  Needs more test and fine tuning.

* prototype working on large files.

* prototype working on large files.

* Adding benchmarks

* jsonstream API adjustment

* type

* minor fixes and cleaning.

* minor fixes and cleaning.

* removing warnings

* removing some copies

* runtime dispatch error fix

* makefile linking src/jsonstream.cpp

* fixing arm stage 1 headers

* fixing stage 2 headers

* fixing stage 1 arm header

* making jsonstream portable

* cleaning imports

* including <algorithms> for windows compiler

* cleaning benchmark imports

* adding jsonstream to amalgamation

* merged main into branch

* bug fix where JsonStream would bug on rare cases.

* Addind a JsonStream Demo to Amalgamation

* Fix for https://github.com/lemire/simdjson/issues/345

* Follow up test and fix for https://github.com/lemire/simdjson/issues/345 (#347)

* Final (?) fix for https://github.com/lemire/simdjson/issues/345

* Verbose basictest

* Being more forgiving of powers of ten.

* Let us zero the tail end.

* add basic fuzzers (#348)

* add basic fuzzing using libFuzzer

* let cmake respect cflags, otherwise the fuzzer flags go unnoticed

also, integrates badly with oss-fuzz

* add new fuzzer for minification, simplify the old one

* add fuzzer for the dump example

* clang format

* adding Paul Dreik

* rough prototype working.  Needs more test and fine tuning.

* prototype working on large files.

* prototype working on large files.

* Adding benchmarks

* jsonstream API adjustment

* type

* minor fixes and cleaning.

* Fixing issue 351 (#352)

* Fixing issues 351 and 353

* minor fixes and cleaning.

* removing warnings

* removing some copies

* Fix ARM compile errors on g++ 7.4 (#354)

* Fix ARM compilation errors

* Update singleheader

* runtime dispatch error fix

* makefile linking src/jsonstream.cpp

* fixing arm stage 1 headers

* fixing stage 2 headers

* fixing stage 1 arm header

* fix integer overflow in subnormal_power10 (#355)

detected by oss-fuzz

https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18714

* Adding new test file, following https://github.com/lemire/simdjson/pull/355

* making jsonstream portable

* cleaning imports

* including <algorithms> for windows compiler

* cleaning benchmark imports

* adding jsonstream to amalgamation

* merged main into branch

* bug fix where JsonStream would bug on rare cases.

* Addind a JsonStream Demo to Amalgamation

* merging main

* rough prototype working.  Needs more test and fine tuning.

* prototype working on large files.

* prototype working on large files.

* Adding benchmarks

* jsonstream API adjustment

* minor fixes and cleaning.

* minor fixes and cleaning.

* removing warnings

* removing some copies

* runtime dispatch error fix

* makefile linking src/jsonstream.cpp

* fixing arm stage 1 headers

* fixing stage 2 headers

* fixing stage 1 arm header

* making jsonstream portable

* cleaning imports

* including <algorithms> for windows compiler

* cleaning benchmark imports

* adding jsonstream to amalgamation

* bug fix where JsonStream would bug on rare cases.

* Addind a JsonStream Demo to Amalgamation

* rough prototype working.  Needs more test and fine tuning.

* minor fixes and cleaning.

* adding jsonstream to amalgamation

* merged main into branch

* Addind a JsonStream Demo to Amalgamation

* merging main

* merging main

* make file fix
2019-11-08 17:39:45 -05:00
Daniel Lemire 6888ca709d Update README.md 2019-11-08 16:39:09 -05:00
Paul Dreik 8ae818e17c add ossfuzz support (#362)
* initial oss-fuzz friendly build

parts taken from libfmt, which I wrote and have the copyright to

* fix build error

* add script for building a corpus zip

see https://google.github.io/oss-fuzz/getting-started/new-project-guide/#seed-corpus

* fix zip command

* drop setting the C++ standard

* disable the minify fuzzer, does not pass oss-fuzz check-build test

* fix integer overflow in subnormal_power10

detected by oss-fuzz

https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18714

* invoke the build like oss fuzz does

* document what the scripts are for and how to use them

* add a page about fuzzing
2019-11-08 10:32:43 -05:00
Daniel Lemire c4f1baad31 Making get_corpus safer (#360) 2019-11-06 12:22:42 -05:00
Daniel Lemire 3439ce19c9 Adding a flag which allows us to disable AVX detection. This exposes a bug. (#356) 2019-11-06 10:39:26 -05:00
John Keiser b7c18df540 Merge pull request #346 from lemire/jkeiser/simd_u8
Genericize SIMD arch code with `simd8<T>`
2019-11-05 19:49:14 -08:00
John Keiser 74799134b1 Add cpuinfo to checkperf 2019-11-05 13:44:04 -08:00
John Keiser 3828e1e538 Fix performance issues:
1. Don't recast "int" result of movemask to uint32_t
2. Call max_epu8 with the mask first and the bytes second.
2019-11-05 13:44:04 -08:00
John Keiser d89046d515 Use simd8 helpers for find_bs_bits_and_quote_bits 2019-11-05 13:44:04 -08:00
John Keiser 4bc128f07e Move compute_quote_mask to generic bitmask library 2019-11-05 13:44:04 -08:00
John Keiser e383b7a6ab Use generic simd operators for find_whitespace_and_operators 2019-11-05 13:37:56 -08:00
John Keiser c89d6bf68b Genericize utf-8 check 2019-11-05 13:37:32 -08:00
Daniel Lemire 52640518d3 Adding new test file, following https://github.com/lemire/simdjson/pull/355 2019-11-05 08:58:41 -05:00
Paul Dreik cf493254b7 fix integer overflow in subnormal_power10 (#355)
detected by oss-fuzz

https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18714
2019-11-04 16:54:03 -05:00
John Keiser c97eb41dc6 Fix ARM compile errors on g++ 7.4 (#354)
* Fix ARM compilation errors

* Update singleheader
2019-11-04 10:36:34 -05:00
Daniel Lemire b1224a77db Fixing issue 351 (#352)
* Fixing issues 351 and 353
2019-11-01 16:05:28 -04:00
Daniel Lemire 17b777f751 adding Paul Dreik 2019-10-28 14:47:45 -04:00
Paul Dreik 9442c9e1f4 add basic fuzzers (#348)
* add basic fuzzing using libFuzzer

* let cmake respect cflags, otherwise the fuzzer flags go unnoticed

also, integrates badly with oss-fuzz

* add new fuzzer for minification, simplify the old one

* add fuzzer for the dump example

* clang format
2019-10-28 14:46:57 -04:00
Daniel Lemire 15740500af Let us zero the tail end. 2019-10-24 18:49:30 -04:00
Daniel Lemire 3484dda45e Being more forgiving of powers of ten. 2019-10-24 18:27:24 -04:00
Daniel Lemire 1ece6c0e2f Verbose basictest 2019-10-24 16:40:40 -04:00
Daniel Lemire 59cad23aeb Merge branch 'master' of github.com:lemire/simdjson 2019-10-24 16:34:10 -04:00
Daniel Lemire da1c35d04b Final (?) fix for https://github.com/lemire/simdjson/issues/345 2019-10-24 16:33:37 -04:00
Daniel Lemire c469aed047 Follow up test and fix for https://github.com/lemire/simdjson/issues/345 (#347) 2019-10-24 16:06:29 -04:00
Daniel Lemire a065805b0f Fix for https://github.com/lemire/simdjson/issues/345 2019-10-24 15:34:30 -04:00
Jérémie Galarneau f41a18b57d Fix typo in REAME.md: technical -> technique (#338) 2019-10-19 11:07:02 -04:00
Daniel Lemire 1257432df3 Adding list. 2019-10-18 17:32:54 -04:00
Daniel Lemire d0c0e31220 Increasing the ULP bound. 2019-10-18 17:30:29 -04:00
Daniel Lemire 9b7832c39a Adding another test (powers of two). 2019-10-16 17:47:52 -04:00
Daniel Lemire e3e29b720d Adding a check that powers of ten can be parsed (sanity check). 2019-10-16 16:27:50 -04:00
John Keiser 64872bddf4 Eliminate stage1_find_marks_flatten.h 2019-10-14 12:33:46 -07:00
John Keiser 81f2249575 Move stage1 into a class to pass fewer parameters 2019-10-14 12:33:46 -07:00
John Keiser 9bbd6bd874 Move headers to implementation area
- jsoncharutils.h, numberparsing.h, simdprune_tables.h
2019-10-14 11:51:41 -07:00
Daniel Lemire 13e477ebfe Update CONTRIBUTORS 2019-10-10 14:29:35 -04:00
Daniel Lemire c284b54fc2 adding link to Go port 2019-10-09 16:28:01 -04:00
John Keiser 69caa477fb Use struct for UTF-8 checks, remove templating
- Removes templating from simd_input, utf8_checker, and parse_string
- Make drone gcc run a lot faster
- Make drone clang run a little faster (NOTE:
https://hub.docker.com/r/silkeh/clang helps even more, but I wasn't sure
whether we wanted to trust that)
- Make drone arm run in parallel to get results quicker
2019-10-08 17:58:45 -07:00
Daniel Lemire 81f9aac13f Fixing minor perf. regression. 2019-10-07 16:31:44 -04:00
Juho Lauri b2eff3c90c case insensitive move_to_key (#324)
* case insensitive move_to_key
* portable strcmpi
2019-10-07 16:08:17 -04:00
Daniel Lemire aa45fe7359 Adding check for subnormal parsing. (#328) 2019-10-07 11:01:34 -04:00
Daniel Lemire 253af0766c adding Juho 2019-10-02 14:26:42 -04:00
Juho Lauri cf9dbe583d improved const correctness (#321) 2019-10-02 14:25:28 -04:00
John Keiser de8df0a05f Combined performance patch (5% overall, 15% stage 1) (#317)
* Allow -f

* Support parse -s (force sse)

* Simplify flatten_bits

- Add directly to base instead of storing variable
- Don't modify base_ptr after beginning of function
- Eliminate base variable and increment base_ptr instead

* De-unroll the flatten_bits loops

* Decrease dependencies in stage 1

- Do all finalize_structurals work before computing the quote mask; mask
  out the quote mask later
- Join find_whitespace_and_structurals and finalize_structurals into
  single find_structurals call, to reduce variable leakage
- Rework pseudo_pred algorithm to refer to "primitive" for clarity and some
  dependency reduction
- Rename quote_mask to in_string to describe what we're trying to
  achieve ("mask" could mean many things)
- Break up find_quote_mask_and_bits into find_quote_mask and
  invalid_string_bytes to reduce data leakage (i.e. don't expose quote bits
  or odd_ends at all to find_structural_bits)
- Genericize overflow methods "follows" and "follows_odd_sequence" for
  descriptiveness and possible lifting into a generic simd parsing library

* Mark branches as likely/unlikely

* Reorder and unroll+interleave stage 1 loop

* Nest the cnt > 16 branch inside cnt > 8
2019-10-01 12:01:08 -04:00
Daniel Lemire 53b6deaeae Safer handling of error codes, fixes https://github.com/lemire/simdjson/issues/318 (#319) 2019-09-29 12:12:15 -04:00
Opemipo 462858efa3 Fix Typo (#311)
escapted -> escaped
2019-09-12 10:16:33 -04:00
John Keiser f7e893667d Use simd_input generic methods for utf8 checking (#301)
* Use generic each/reduce in simdutf8check

* Remove macros from generic simd_input uses

* Use array instead of members to store simd registers

* Default local checkperf to clone from .
2019-09-02 12:46:05 -04:00
Daniel Lemire 5765c81f66 Fixing number parsing of large ints 2019-09-02 12:40:39 -04:00
Daniel Lemire 92334a8e28 Better tests. 2019-09-02 12:32:44 -04:00
Daniel Lemire c4218c8e40 Accept large unsigned integers (#295) (#306)
* handle uint64 value in JSON
* Add integer_tests
* Add get_unsigned_integer() on  ParsedJson::BasicIterator
* Write 'u' to tape when the value seems unsigned
* Add to handle 'u' element
* Brush up integer_tests.cpp
* Append tests/integer_tests in .gitignore
* Add comments to is_integer and is_unsigned_integer
2019-09-02 11:56:26 -04:00
saka1 c1f27fb848 Accept large unsigned integers (#295)
* handle uint64 value in JSON
* Add integer_tests
* Add get_unsigned_integer() on  ParsedJson::BasicIterator
* Write 'u' to tape when the value seems unsigned
* Add to handle 'u' element
* Brush up integer_tests.cpp
* Append tests/integer_tests in .gitignore
* Add comments to is_integer and is_unsigned_integer
2019-09-02 10:50:24 -04:00
Valeriy Van 6d0fd5bb93 Stylistic fix in README.md (#305) 2019-09-01 12:35:50 -04:00
Daniel Lemire bd15d3ae24 Pointing appveyor badge to master 2019-08-30 17:26:18 -04:00
John Keiser 7f249cd179 Use non-interleaved map() to make structurals clearer (#304) 2019-08-29 21:38:41 -04:00
John Keiser aef3f4be99 Merge pull request #296 from lemire/wide_mask
Genericize bitmask building to make algorithms clearer
2019-08-28 08:53:21 -07:00
John Keiser bf8083888d Validate perf against master, not v0.2.1 2019-08-26 13:35:18 -07:00
John Keiser f4fa5b7340 Add MAP_CHUNKS2, make parameter name related to input 2019-08-26 09:46:49 -07:00
John Keiser 169568ca47 Use map() to interleave instructions for parallelism 2019-08-26 09:46:49 -07:00
John Keiser 9cc4ddfc88 Use map().to_bitmask() instead of build_bitmask() 2019-08-26 09:46:49 -07:00
John Keiser 441963c84c Add AMD64 build_bitmask 2019-08-26 09:46:49 -07:00
John Keiser cf4ae61ac6 Modify checkperf to print out perfdiff command
to make it easier to run it yourself without having to recompile the
world
2019-08-26 09:46:49 -07:00
John Keiser da0f1cacea Remove static modifiers 2019-08-26 09:46:48 -07:00
John Keiser 5e5592178d Update amalgamated cpp 2019-08-26 09:46:48 -07:00
John Keiser b01222518d Genericize bitmask building to make algorithms clearer 2019-08-26 09:46:48 -07:00
Daniel Lemire 2060cf8a70 Updating reference to paper 2019-08-26 10:24:31 -04:00
saka1 a4bd87119b Remove duplicate lines in .gitignore (#300) 2019-08-25 09:34:13 -04:00
Daniel Lemire 9f26355fe0 This should lower false positives. (#299) 2019-08-25 09:33:00 -04:00
Daniel Lemire f667d4965d This is a bug fix: our prev function was buggy. (#291) 2019-08-23 18:59:43 -04:00
John Keiser 585f84a734 Move architecture-specific headers to src/ (#287)
* Use namespaces instead of templates for stage1 impls

* Move stage1 implementation into the src/ directory

* Move architecture-specific code to src/
2019-08-21 07:59:49 -04:00
Daniel Lemire a1bff85263 Documenting the limits of move_to_key with respect to Unicode Equivalence. 2019-08-20 17:10:30 -04:00
Daniel Lemire fb920bba62 ZippyJSON 2019-08-20 14:11:53 -04:00
saka1 58697f6f3b Remove unnecessary x permissions from JSON files (#290) 2019-08-18 20:48:48 -04:00
saka1 18c5b8d68a Update .gitignore for cmake files (#289) 2019-08-18 17:24:38 -04:00
John Keiser 08cf140811 Merge pull request #285 from lemire/methods
Use methods instead of functions for simd_input
2019-08-16 17:45:42 -07:00
John Keiser 94673bcdf2 Use methods for utf8 checker 2019-08-16 14:15:37 -07:00
John Keiser aa15917c9d Use methods instead of functions for simd_input 2019-08-16 14:07:30 -07:00
John Keiser 85fb37b6ea Lower the bar for performance check 2019-08-16 12:34:28 -07:00
John Keiser ae3ae9a474 Merge pull request #280 from lemire/circle-reuse
Parallelize Circle CI and speed up gcc runs
2019-08-16 09:55:39 -07:00
Vitaly Baranov e9be643db5 Fix condition in ParsedJson::allocate_capacity(). (#283) 2019-08-16 08:38:59 -04:00
John Keiser b49eefbee6 Give test jobs better names 2019-08-15 19:41:30 -07:00
John Keiser 640283fec6 Reuse jobs in Circle CI 2019-08-15 19:41:30 -07:00
John Keiser c8d50a6060 Make perf validation more stable, check no-AVX as well (#275)
* Compare against v0.2.1, fail only if perf is less 6 times in a row

* Run AVX and no-AVX perf tests in Circle CI

* Set % difference threshold to 0.2%
2019-08-15 20:43:21 -04:00
Vitaly Baranov 6a2728e730 No allocation in the iterator's constructor (#276)
* Get rid of dynamic allocation in ParsedJson::Iterator.

* Implement copy assignment operator for ParsedJson::Iterator.

* ParsedJson::Iterator is now a template class.
2019-08-15 19:42:15 -04:00
John Keiser 1740d93420 Merge pull request #277 from jkeiser/separate_include
Move simd_input and utf8 check functions into their own headers
2019-08-14 13:17:15 -07:00
John Keiser 0042d9b406 Move UTF8 checking functions into their own file 2019-08-14 10:34:11 -07:00
John Keiser 3bfa6097d5 .gitignore that ignores all the things 2019-08-14 10:33:30 -07:00
John Keiser 237b8865f5 Correct header #define 2019-08-13 17:44:26 -07:00
John Keiser 8f01cece3a Move simd_input and associated functions to their own header 2019-08-13 17:44:06 -07:00
Daniel Lemire 2ca574d9e6 Removing windows.h (#273) 2019-08-12 19:40:21 -04:00
Daniel Lemire 7f27e1e0e1 Merge branch 'master' of github.com:lemire/simdjson 2019-08-12 16:05:31 -04:00
John Keiser 875e2f9d0d check for performance degradation in CI (#270)
* Add -n and -w arguments

* Add Dockerfile that compares perf against master

* Add checkperf to .drone.yml

* Clone from github instead of .git since CI doesn't have .git
2019-08-12 16:03:56 -04:00
Daniel Lemire 5c538dd9d6 correcting weird formatting. 2019-08-12 15:56:15 -04:00
Daniel Lemire 3fb82502f7 This gets rid of the silly ALLOW_SAME_PAGE_BUFFER_OVERRUN (#268) 2019-08-09 17:36:32 -04:00
Daniel Lemire 7be2998cae Adding more sanitize tests as part of the CI. (#267)
* Adding more sanitize tests as part of the CI.
2019-08-09 17:26:05 -04:00
Vitaly Baranov 9dfab9d9a4 Disable UBSan error in trailing_zeroes(). (#266)
https://github.com/lemire/simdjson/issues/265
2019-08-09 14:37:22 -04:00
Daniel Lemire b63ae1f190 Update README.md 2019-08-09 12:10:09 -04:00
Daniel Lemire 4fc796b387 Documenting thread safety. 2019-08-08 16:23:31 -04:00
Daniel Lemire c3ff41dd84 Adding @vitlibar as a contributor. 2019-08-08 16:17:43 -04:00
Vitaly Baranov 0b927f059c Make dynamic dispatch free of TSan warnings (#256) 2019-08-08 16:16:35 -04:00
John Keiser f3c3afd4cd Use direct call to templated flatten_bits instead of if (#262)
* Use direct call to templated flatten_bits instead of if

* Put really_inline back on find_structural_bits_64
2019-08-08 15:09:17 -04:00
John Keiser 1e26859bb7 Bring .git into docker (#259) 2019-08-06 09:39:33 -04:00
John Keiser b1beacd1f3 Make headers show up in Header Files in VS2019 (#257) 2019-08-05 16:36:52 -04:00
John Keiser d9a0e2b8f4 Fix Intellisense errors opening .h files on VS2019 (#253) 2019-08-04 19:57:55 -04:00
John Keiser 4c7e95aac3 Make Docker start much faster (#255) 2019-08-04 18:43:39 -04:00
John Keiser c3310c6e8f Add sane defaults for .sh and such (#254) 2019-08-04 18:11:48 -04:00
ioioioio 2a24567370 Replace macros by include files (#236) (#248)
* stage1 compiles without macros

* cleaning

* amalgation is weird but works

* macros are removed from stringparsing

* amalgation fixed

* Huge macros are removed.

* clang-format
2019-08-04 15:58:35 -04:00
Daniel Lemire bd9628df93 Producing a new release 2019-08-04 15:43:47 -04:00
Daniel Lemire 99a153d9e8 Hiding the pointer away... (#252)
* Hiding the runtime dispatch pointer in a source file so it is not an exported symbol
* Disabling hard failure on style check.
* Fixes https://github.com/lemire/simdjson/issues/250
2019-08-04 15:41:00 -04:00
Daniel Lemire 04da71c3a1 dynamic and static tests 2019-08-04 10:09:16 -04:00
Daniel Lemire 0c86dd9d8d Testing static and dynamic libraries. 2019-08-04 09:56:34 -04:00
Daniel Lemire 349068dcda Update README.md 2019-08-03 12:27:37 -04:00
Daniel Lemire 144b10b35d simdjson vs. JSON for Modern C++ (#247)
* New competitor.

* Fixing makefile.
2019-08-02 19:48:34 -04:00
Daniel Lemire 44722bddcf Update README.md 2019-08-02 12:01:32 -04:00
Daniel Lemire 2a240e3fe2 Fixing style violation. 2019-08-01 16:38:51 -04:00
730 changed files with 115908 additions and 85793 deletions
+38 -14
View File
@@ -1,22 +1,46 @@
version: '{build}'
branches:
only:
- master
image:
- Visual Studio 2017
clone_folder: c:\projects\simdjson
platform:
- x64
branches: { only: [ master ] }
configuration: Release
image: Visual Studio 2019
platform: x64
environment:
matrix:
- AVXFLAG: "OFF"
- AVXFLAG: "ON"
- job_name: VS2019
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
- 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"
- job_name: VS2015
image: Visual Studio 2015
CMAKE_ARGS: -A %Platform% -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_ENABLE_THREADS=OFF
CTEST_ARGS: -E checkperf
build_script:
- mkdir build
- cd build
- ps: cmake -DSIMDJSON_DISABLE_AVX="$env:AVXFLAG" -DCMAKE_GENERATOR_PLATFORM=x64 ..
- cmake --build .
- ctest --verbose
- cmake --version
- cmake %CMAKE_ARGS% --parallel ..
- cmake -LH ..
- cmake --build . --config %Configuration% --verbose --parallel
for:
-
matrix:
except:
- job_name: VS2019ARM
test_script:
- ctest --output-on-failure -C %Configuration% --verbose %CTEST_ARGS% --parallel
clone_folder: c:\projects\simdjson
matrix:
fast_finish: true
+258 -151
View File
@@ -1,162 +1,269 @@
version: 2
version: 2.1
# We constantly run out of memory so please do not use parallelism (-j, -j4).
# Reusable image / compiler definitions
executors:
gcc8:
docker:
- image: conanio/gcc8
environment:
CXX: g++-8
CC: gcc-8
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc9:
docker:
- image: conanio/gcc9
environment:
CXX: g++-9
CC: gcc-9
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
gcc10:
docker:
- image: conanio/gcc10
environment:
CXX: g++-10
CC: gcc-10
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang10:
docker:
- image: conanio/clang10
environment:
CXX: clang++-10
CC: clang-10
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang9:
docker:
- image: conanio/clang9
environment:
CXX: clang++-9
CC: clang-9
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
clang6:
docker:
- image: conanio/clang60
environment:
CXX: clang++-6.0
CC: clang-6.0
BUILD_FLAGS:
CTEST_FLAGS: --output-on-failure
# Reusable test commands (and initializer for clang 6)
commands:
install_cmake:
steps:
- run: apt-get update -qq
- run: apt-get install -y cmake
cmake_prep:
steps:
- checkout
- run: mkdir -p build
cmake_build_cache:
steps:
- cmake_prep
- run: cmake $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination -B build .
cmake_build:
steps:
- cmake_build_cache
- run: cmake --build build
cmake_test:
steps:
- cmake_build
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -L acceptance &&
ctest $CTEST_FLAGS -LE acceptance -E checkperf
cmake_test_all:
steps:
- cmake_build
- run: |
cd build &&
tools/json2json -h &&
ctest $CTEST_FLAGS -DSIMDJSON_IMPLEMENTATION="haswell;westmere;fallback" -L acceptance -LE per_implementation &&
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -E checkperf &&
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
cmake_perftest:
steps:
- cmake_build_cache
- run: |
cmake --build build --target checkperf &&
cd build &&
ctest --output-on-failure -R checkperf
# we not only want cmake to build and run tests, but we want also a successful installation from which we can build, link and run programs
cmake_install_test: # this version builds, install, test and then verify from the installation
steps:
- run: cd build && make install
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++17 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
cmake_installed_test_cxx20: # assuming that it was installed, this tries to build using C++20
steps:
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++20 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
jobs:
"gcc":
docker:
- image: ubuntu:18.04
environment:
CXX: g++-7
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
g++-7
git
# static
justlib-gcc10:
description: Build just the library, install it and do a basic test
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_JUST_LIBRARY=ON }
steps: [ cmake_build, cmake_install_test, cmake_installed_test_cxx20 ]
gcc10-perftest:
description: Build and run performance tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_perftest ]
gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake static build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
clang6:
description: Build and run tests on clang 6 and AVX 2 with a cmake static build
executor: clang6
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test ]
clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
# libcpp
libcpp-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake static build and libc++
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON -DSIMDJSON_BUILD_STATIC=ON }
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
# sanitize
sanitize-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
sanitize-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
- run:
name: Building (gcc)
command: make
# dynamic
dynamic-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake dynamic build
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
steps: [ cmake_test, cmake_install_test ]
dynamic-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake dynamic build
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
steps: [ cmake_test, cmake_install_test ]
- run:
name: Running tests (gcc)
command: make quiettest amalgamate
# unthreaded
unthreaded-gcc10:
description: Build and run tests on GCC 10 and AVX 2 *without* threads
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
steps: [ cmake_test, cmake_install_test ]
unthreaded-clang10:
description: Build and run tests on Clang 10 and AVX 2 *without* threads
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
steps: [ cmake_test, cmake_install_test ]
- run:
name: Building (gcc, cmake)
command: |
mkdir build
cd build
cmake ..
make
# noexcept
noexcept-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with exceptions off
executor: gcc10
environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF }
steps: [ cmake_test, cmake_install_test ]
noexcept-clang10:
description: Build and run tests on Clang 10 and AVX 2 with exceptions off
executor: clang10
environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF }
steps: [ cmake_test, cmake_install_test ]
- run:
name: Running tests (gcc, cmake)
command: |
cd build
make test
#
# Misc.
#
"gccnoavx":
docker:
- image: ubuntu:18.04
environment:
CXX: g++-7
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
g++-7
git
- run:
name: Building (gcc)
command: ARCHFLAGS="-march=nehalem" make
- run:
name: Running tests (gcc)
command: ARCHFLAGS="-march=nehalem" make quiettest amalgamate
- run:
name: Building (gcc, cmake)
command: |
mkdir build
cd build
cmake -DSIMDJSON_DISABLE_AVX=on ..
make
- run:
name: Running tests (gcc, cmake)
command: |
cd build
make test
"clang":
docker:
- image: ubuntu:18.04
environment:
CXX: clang++-6.0
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
clang-6.0
git
- run:
name: Building (clang)
command: make
- run:
name: Running tests (clang)
command: make quiettest amalgamate
- run:
name: Building (clang, cmake)
command: |
mkdir build
cd build
cmake ..
make
- run:
name: Running tests (clang, cmake)
command: |
cd build
make test
"clangnoavx":
docker:
- image: ubuntu:18.04
environment:
CXX: clang++-6.0
steps:
- checkout
- run: apt-get update -qq
- run: >
apt-get install -y
build-essential
cmake
clang-6.0
git
- run:
name: Building (clang)
command: ARCHFLAGS="-march=nehalem" make
- run:
name: Running tests (clang)
command: ARCHFLAGS="-march=nehalem" make quiettest amalgamate
- run:
name: Building (clang, cmake)
command: |
mkdir build
cd build
cmake -DSIMDJSON_DISABLE_AVX=on ..
make
- run:
name: Running tests (clang, cmake)
command: |
cd build
make test
# make (test and checkperf)
arch-haswell-gcc10:
description: Build, run tests and check performance on GCC 10 with -march=haswell
executor: gcc10
environment: { CXXFLAGS: -march=haswell }
steps: [ cmake_test ]
arch-nehalem-gcc10:
description: Build, run tests and check performance on GCC 10 with -march=nehalem
executor: gcc10
environment: { CXXFLAGS: -march=nehalem }
steps: [ cmake_test ]
sanitize-haswell-gcc10:
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
executor: gcc10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, BUILD_FLAGS: "", CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
sanitize-haswell-clang10:
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
executor: clang10
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -E checkperf }
steps: [ cmake_test ]
workflows:
version: 2
version: 2.1
build_and_test:
jobs:
- "clang"
- "gcc"
- "clangnoavx"
- "gccnoavx"
# full multi-implementation tests
#- gcc7 tested on GitHub actions
- gcc10 # do not delete this as it tests our performance
- clang6
#- clang10 # this gets tested a lot below
# libc++
- libcpp-clang10
# full single-implementation tests
- sanitize-gcc10
- sanitize-clang10
- dynamic-gcc10
- dynamic-clang10
- unthreaded-gcc10
- unthreaded-clang10
# no exceptions
- noexcept-gcc10
- noexcept-clang10
# quicker make single-implementation tests
- arch-haswell-gcc10
- arch-nehalem-gcc10
# sanitized single-implementation tests
- sanitize-haswell-gcc10
- sanitize-haswell-clang10
# testing "just the library"
- justlib-gcc10
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows
+21
View File
@@ -0,0 +1,21 @@
task:
timeout_in: 120m
freebsd_instance:
matrix:
- image_family: freebsd-13-0-snap
env:
ASSUME_ALWAYS_YES: YES
setup_script:
- pkg update -f
- pkg install bash
- pkg install cmake
- pkg install git
build_script:
- mkdir build
- cd build
- cmake -DSIMDJSON_BASH=OFF -DSIMDJSON_GIT=OFF ..
- make
test_script:
- cd build
- ctest --output-on-failure -E checkperf
+15
View File
@@ -0,0 +1,15 @@
*
!.git
!Makefile
!amalgamate.sh
!benchmark
!dependencies
!include
!jsonchecker
!jsonexamples
!scripts
!singleheader
!src
!style
!tests
!tools
+396 -115
View File
@@ -1,40 +1,409 @@
kind: pipeline
name: x64
platform:
os: linux
arch: amd64
name: i386-gcc # we do not support 32-bit systems, but we run tests
platform: { os: linux, arch: amd64 }
steps:
- name: test
image: gcc:8
- name: Build and Test
image: i386/ubuntu
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- make
- make quiettest
- make amalgamate
- apt-get update -qq
- apt-get install -y g++ cmake gcc
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64
platform:
os: linux
arch: arm64
name: i386-clang # we do not support 32-bit systems, but we run tests
platform: { os: linux, arch: amd64 }
steps:
- name: test
image: gcc:8
- name: Build and Test
image: i386/ubuntu
environment:
CC: clang-6.0
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- make
- make quiettest
- make amalgamate
- apt-get update -qq
- apt-get install -y clang++-6.0 cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: gcc9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: gcc:9
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: clang6
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang60
user: root
environment:
CC: clang-6.0
CXX: clang++-6.0
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: dynamic-gcc9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: gcc:9
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: dynamic-clang9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang9
user: root
environment:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: sanitize-gcc9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: gcc:9
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: sanitize-clang9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang9
user: root
environment:
CC: clang-9
CXX: clang++-9
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=haswell;westmere;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: arm64-gcc8
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: gcc:8
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: arm64-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-dynamic-gcc8
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: gcc:8
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-dynamic-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-sanitize-gcc8
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: gcc:8
environment:
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CC: gcc
CXX: g++
commands:
- apt-get update -qq
- apt-get install -y cmake libstdc++6
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: arm64-sanitize-clang6
platform: { os: linux, arch: arm64 }
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang-6.0
CXX: clang++-6.0
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
BUILD_FLAGS: -- -j
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y clang cmake git
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
---
kind: pipeline
name: ninja-clang9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang9
user: root
environment:
CC: clang-9
CXX: clang++-9
BUILD_FLAGS: -- -j 4
CMAKE_FLAGS: -GNinja -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CXXFLAGS: -stdlib=libc++
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: libcpp-clang9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: conanio/clang9
user: root
environment:
CC: clang-9
CXX: clang++-9
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
CXXFLAGS: -stdlib=libc++
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: noexceptions-gcc9
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: gcc:9
environment:
CC: gcc
CXX: g++
BUILD_FLAGS: -- -j
CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF
CTEST_FLAGS: -j4 --output-on-failure -E checkperf
commands:
- apt-get update -qq
- apt-get install -y cmake
- mkdir build
- cd build
- cmake $CMAKE_FLAGS ..
- cmake --build . $BUILD_FLAGS
- ctest $CTEST_FLAGS
---
kind: pipeline
name: arm64-fuzz
platform: { os: linux, arch: arm64 }
steps:
- name: Build and run fuzzers shortly
image: ubuntu:20.04
environment:
CC: clang
CXX: clang++
DEBIAN_FRONTEND: noninteractive
ASAN_OPTIONS: detect_leaks=0
commands:
- apt-get update -qq
- apt-get install -q -y clang cmake git wget zip ninja-build
- wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
- tar xf corpus.tar && rm corpus.tar
- fuzz/build_like_ossfuzz.sh
- mkdir -p common_out
- for fuzzer in build/fuzz/fuzz_* ; do echo $fuzzer;$fuzzer common_out out/* -max_total_time=40; done
---
kind: pipeline
name: stylecheck
platform:
os: linux
arch: amd64
platform: { os: linux, arch: amd64 }
steps:
- name: Build and Test
image: ubuntu:18.04
@@ -42,91 +411,3 @@ steps:
- apt-get update -y
- apt-get install -y python clang-format
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
---
kind: pipeline
name: amd64_clang_cmake
platform:
os: linux
arch: amd64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: amd64_gcc_cmake
platform:
os: linux
arch: amd64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: arm64_clang_cmake
platform:
os: linux
arch: arm64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
---
kind: pipeline
name: arm64_gcc_cmake
platform:
os: linux
arch: arm64
steps:
- name: Build and Test
image: ubuntu:18.04
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest
+113 -15
View File
@@ -1,19 +1,117 @@
# Set the default behavior, in case people don't have core.autocrlf set.
# Uncomment next line to adjust line endings
* text=auto
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.cpp text
*.h text
*.java text
*.xml text
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.svg binary
*.json binary
# we don't want json files to be modified for this project
*.json binary
# Common settings that generally should always be used with your language specific settings
#
# From generator:
# https://www.davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/
#
# Documents
*.bibtex text diff=bibtex
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text
*.tex text diff=tex
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
*.sql text
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as an asset (binary) by default.
*.svg text
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
*.eps binary
# Scripts
*.bash text eol=lf
*.sh text eol=lf
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
# Serialisation
#*.json text
*.toml text
*.xml text
*.yaml text
*.yml text
# Archives
*.7z binary
*.gz binary
*.tar binary
*.zip binary
#
# Exclude files from exporting
#
.gitattributes export-ignore
.gitignore export-ignore
# Sources
*.c text eol=lf diff=c
*.cc text eol=lf diff=cpp
*.cxx text eol=lf diff=cpp
*.cpp text eol=lf diff=cpp
*.c++ text eol=lf diff=cpp
*.hpp text eol=lf diff=cpp
*.h text eol=lf diff=c
*.h++ text eol=lf diff=cpp
*.hh text eol=lf diff=cpp
# Compiled Object files
*.slo binary
*.lo binary
*.o binary
*.obj binary
# Precompiled Headers
*.gch binary
*.pch binary
# Compiled Dynamic libraries
*.so binary
*.dylib binary
*.dll binary
# Compiled Static libraries
*.lai binary
*.la binary
*.a binary
*.lib binary
# Executables
*.exe binary
*.out binary
*.app binary
+41
View File
@@ -0,0 +1,41 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug (unverified)
assignees: ''
---
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
**Describe the bug**
A clear and concise description of what the bug is.
Note that a compiler warning is not a bug.
**To Reproduce**
Steps to reproduce the behaviour: provide a code sample if possible.
If we cannot reproduce the issue, then we cannot address it.
Note that a stack trace from your own program is not enough.
**Configuration (please complete the following information if relevant):**
- OS: [e.g. Ubuntu 16.04.6 LTS]
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
- Version [e.g. 22]
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug. In particular, we do not support legacy 32-bit systems.
**Indicate whether you are willing or able to provide a bug fix as a pull request**
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+37
View File
@@ -0,0 +1,37 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
Please provide a clear rationale for the feature. Be advised that simdjson is a community-based project: you should consider providing help.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
** Are you willing to contribute code or documentation toward this new feature? **
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
@@ -0,0 +1,32 @@
---
name: Standard issue template
about: Issue
title: ''
labels: ''
assignees: ''
---
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
Is your issue:
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request.
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
3. A feature request? Please provide a clear rationale for the feature. Be advised that simdjson is a community-based project: you should consider providing help.
4. A documentation issue? Can you suggest an improvement?
If you plan to contribute to simdjson, please read our
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
+27
View File
@@ -0,0 +1,27 @@
name: Alpine Linux
'on':
- push
- pull_request
jobs:
ubuntu-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: start docker
run: |
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
echo 'docker exec alpine "$@";' > ./alpine.sh
chmod +x ./alpine.sh
- name: install packages
run: |
./alpine.sh apk update
./alpine.sh apk add build-base cmake g++ linux-headers git bash
- name: cmake
run: |
./alpine.sh cmake -B build_for_alpine
- name: build
run: |
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest"
+152
View File
@@ -0,0 +1,152 @@
name: Fuzz and run valgrind
on:
push:
branches:
- master
pull_request:
schedule:
- cron: 23 */8 * * *
jobs:
build:
runs-on: ubuntu-latest
env:
# fuzzers that use the default implementation
defaultimplfuzzers: atpointer dump dump_raw_tape element minify parser print_json
# fuzzers that loop over the implementations themselves
implfuzzers: implementations minifyimpl utf8
implementations: haswell westmere fallback
UBSAN_OPTIONS: halt_on_error=1
MAXLEN: -max_len=4000
steps:
- name: Install packages necessary for building
run: |
sudo apt update
sudo apt-get install --quiet ninja-build valgrind zip unzip
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 10
- uses: actions/checkout@v1
- name: Create and prepare the initial seed corpus
run: |
fuzz/build_corpus.sh
mv corpus.zip seed_corpus.zip
mkdir seedcorpus
unzip -q -d seedcorpus seed_corpus.zip
- name: Download the corpus from the last run
run: |
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
tar xf corpus.tar
rm corpus.tar
- name: List clang versions
run: |
ls /usr/bin/clang*
which clang++
clang++ --version
- name: Build all the variants
run: fuzz/build_fuzzer_variants.sh
- name: Explore fast (release build, default implementation)
run: |
set -eux
for fuzzer in $defaultimplfuzzers $implfuzzers; do
mkdir -p out/$fuzzer # in case this is a new fuzzer, or corpus.tar is broken
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-fast/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=30 $MAXLEN
done
- name: Fuzz default impl. fuzzers with sanitizer+asserts (good at detecting errors)
run: |
set -eux
for fuzzer in $defaultimplfuzzers; do
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
for implementation in $implementations; do
export SIMDJSON_FORCE_IMPLEMENTATION=$implementation
build-sanitizers/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
done
echo now have $(ls out/$fuzzer |wc -l) files in corpus
done
- name: Fuzz differential impl. fuzzers with sanitizer+asserts (good at detecting errors)
run: |
set -eux
for fuzzer in $implfuzzers; do
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-sanitizers/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
echo now have $(ls out/$fuzzer |wc -l) files in corpus
done
- name: Minimize the corpus with the fast fuzzer on the default implementation
run: |
set -eux
for fuzzer in $defaultimplfuzzers $implfuzzers; do
mkdir -p out/cmin/$fuzzer
# get input from everyone else (corpus cross pollination)
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
build-fast/fuzz/fuzz_$fuzzer -merge=1 $MAXLEN out/cmin/$fuzzer out/$fuzzer $others seedcorpus
rm -rf out/$fuzzer
mv out/cmin/$fuzzer out/$fuzzer
done
- name: Package the corpus into an artifact
run: |
for fuzzer in $defaultimplfuzzers $implfuzzers; do
tar rf corpus.tar out/$fuzzer
done
- name: Save the corpus as a github artifact
uses: actions/upload-artifact@v2
with:
name: corpus
path: corpus.tar
# This takes a subset of the minimized corpus and run it through valgrind. It is slow,
# therefore take a "random" subset. The random selection is accomplished by sorting on filenames,
# which are hashes of the content.
- name: Run some of the minimized corpus through valgrind (replay build, default implementation)
run: |
for fuzzer in $defaultimplfuzzers $implfuzzers; do
find out/$fuzzer -type f |sort|head -n200|xargs -n40 valgrind build-replay/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer.txt
done
- name: Compress the valgrind output
run: tar cf valgrind.tar valgrind-*.txt
- name: Save valgrind output as a github artifact
uses: actions/upload-artifact@v2
if: always()
with:
name: valgrindresults
path: valgrind.tar
if-no-files-found: ignore
- name: Upload the corpus and results to bintray if we are on master
if: ${{ github.event_name == 'schedule' }}
run: |
echo uploading each artifact twice, otherwise it will not be published
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
- name: Archive any crashes as an artifact
uses: actions/upload-artifact@v2
if: always()
with:
name: crashes
path: |
crash-*
leak-*
timeout-*
if-no-files-found: ignore
+53
View File
@@ -0,0 +1,53 @@
name: MinGW32-CI
on: [push, pull_request]
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
# It is important to build static libraries because cmake is not smart enough under Windows/mingw to take care of the path. So
# with a dynamic library, you could get failures due to the fact that the EXE can't find its DLL.
jobs:
ci:
name: windows-gcc
runs-on: windows-2016
env:
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DSIMDJSON_BUILD_STATIC=ON .. ' if using the command line
CC: gcc
CXX: g++
steps: # To reproduce what is below, start a powershell with administrative rights, using scoop *is* a good idea
- uses: actions/checkout@v2
- uses: actions/cache@v2 # we cache the scoop setup with 32-bit GCC
id: cache
with:
path: |
C:\ProgramData\scoop
key: scoop32 # static key: should be good forever
- name: Setup Windows # This should almost never run if the cache works.
if: steps.cache.outputs.cache-hit != 'true'
shell: powershell
run: |
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install sudo --global
sudo scoop install git --global
sudo scoop install ninja --global
sudo scoop install cmake --global
sudo scoop install gcc --arch 32bit --global
$env:path
Write-Host 'Everything has been installed, you are good!'
- name: Build and Test 32-bit x86
shell: powershell
run: |
$ENV:PATH="C:\ProgramData\scoop\shims;C:\ProgramData\scoop\apps\gcc\current\bin;C:\ProgramData\scoop\apps\ninja\current;$ENV:PATH"
g++ --version
cmake --version
ninja --version
git --version
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
+59
View File
@@ -0,0 +1,59 @@
name: MinGW64-CI
on: [push, pull_request]
# Important: scoop will either install 32-bit GCC or 64-bit GCC, not both.
# It is important to build static libraries because cmake is not smart enough under Windows/mingw to take care of the path. So
# with a dynamic library, you could get failures due to the fact that the EXE can't find its DLL.
jobs:
ci:
name: windows-gcc
runs-on: windows-2016
env:
CMAKE_GENERATOR: Ninja # This is critical, try ' cmake -GNinja-DSIMDJSON_BUILD_STATIC=ON .. ' if using the command line
CC: gcc
CXX: g++
steps: # To reproduce what is below, start a powershell with administrative rights, using scoop *is* a good idea
- uses: actions/checkout@v2
- uses: actions/cache@v2 # we cache the scoop setup with 64-bit GCC
id: cache
with:
path: |
C:\ProgramData\scoop
key: scoop64 # static key: should be good forever
- name: Setup Windows # This should almost never run if the cache works.
if: steps.cache.outputs.cache-hit != 'true'
shell: powershell
run: |
Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')
scoop install sudo --global
sudo scoop install git --global
sudo scoop install ninja --global
sudo scoop install cmake --global
sudo scoop install gcc --arch 64bit --global
$env:path
Write-Host 'Everything has been installed, you are good!'
- name: Build and Test 64-bit x64
shell: powershell
run: |
$ENV:PATH="C:\ProgramData\scoop\shims;C:\ProgramData\scoop\apps\gcc\current\bin;C:\ProgramData\scoop\apps\ninja\current;$ENV:PATH"
g++ --version
cmake --version
ninja --version
git --version
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
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
+44
View File
@@ -0,0 +1,44 @@
name: MSYS2-CI
on: [push, pull_request]
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-gcc
type: Release
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc
type: Release
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Debug
- msystem: "MINGW32"
install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc
type: Debug
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v2
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{ matrix.msystem }}
install: ${{ matrix.install }}
- name: Build and Test
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DSIMDJSON_BUILD_STATIC=ON -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
cmake --build . --verbose
ctest -j4 --output-on-failure -E checkperf
+21
View File
@@ -0,0 +1,21 @@
name: Performance check on Ubuntu 18.04 CI (GCC 7)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.0
with:
cmake-version: '3.9.x'
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf ubuntu18-checkperf.yml
+22
View File
@@ -0,0 +1,22 @@
name: Ubuntu 18.04 CI (GCC 7)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.0
with:
cmake-version: '3.9.x'
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -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
+20
View File
@@ -0,0 +1,20 @@
name: Performance check on Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.0
with:
cmake-version: '3.9.x'
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . --target checkperf &&
ctest --output-on-failure -R checkperf
+22
View File
@@ -0,0 +1,22 @@
name: Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.0
with:
cmake-version: '3.9.x'
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_BUILD_STATIC=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest -j --output-on-failure -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
+26
View File
@@ -0,0 +1,26 @@
name: VS16-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- 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"
+25
View File
@@ -0,0 +1,25 @@
name: VS16-CLANG-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -T ClangCL -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+26
View File
@@ -0,0 +1,26 @@
name: VS16-Ninja-CI
on: [push, pull_request]
jobs:
ci:
name: windows-vs16
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: 'Run CMake with VS16'
uses: lukka/run-cmake@v2
with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
cmakeBuildType: Release
buildWithCMake: true
cmakeGenerator: VS16Win64
cmakeAppendedArgs: -G Ninja -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_BUILD_STATIC=ON
buildWithCMakeArgs: --config Release
- name: 'Run CTest'
run: ctest -C Release -E checkperf --output-on-failure
working-directory: "${{ github.workspace }}/../../_temp/windows"
+98 -1
View File
@@ -1 +1,98 @@
build/
# eclipse project files
.cproject
.project
.settings
# emacs temp files
*~
# vim temp files
.*.swp
# XCode
^build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
*.DS_Store
# IDE specific folder for JetBrains IDEs
.idea/
cmake-build-debug/
cmake-build-release/
# Visual Studio Code artifacts
.vscode/*
.history/
# Visual Studio artifacts
/VS/
# C/C++ build outputs
.build/
bins
gens
libs
objs
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# CMake files that may be specific to our installation
# Build outputs
/build*/
/visual_studio/
# Fuzzer outputs generated by instructions in fuzz/Fuzzing.md
/corpus.zip
/ossfuzz-out/
/out/
# Generated docs
/doc/api
+15
View File
@@ -25,3 +25,18 @@
[submodule "dependencies/jsoncpp"]
path = dependencies/jsoncpp
url = https://github.com/open-source-parsers/jsoncpp.git
[submodule "dependencies/json"]
path = dependencies/json
url = https://github.com/nlohmann/json.git
[submodule "dependencies/benchmark"]
path = dependencies/benchmark
url = https://github.com/google/benchmark.git
[submodule "dependencies/cxxopts"]
path = dependencies/cxxopts
url = https://github.com/jarro2783/cxxopts
[submodule "dependencies/boost.json"]
path = dependencies/boost.json
url = https://github.com/CPPAlliance/json.git
[submodule "dependencies/yyjson"]
path = dependencies/yyjson
url = https://github.com/ibireme/yyjson.git
+2 -1
View File
@@ -1,3 +1,4 @@
# List of authors for copyright purposes
# List of authors for copyright purposes, in no particular order
Daniel Lemire
Geoff Langdale
John Keiser
+63 -39
View File
@@ -1,52 +1,76 @@
cmake_minimum_required(VERSION 3.9) # CMP0069 NEW
include(CheckIPOSupported)
check_ipo_supported(RESULT ltoresult)
if(ltoresult)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
# usage: cmake -DSIMDJSON_DISABLE_AVX=on ..
option(SIMDJSON_DISABLE_AVX "Forcefully disable AVX even if hardware supports it" OFF)
project(simdjson
DESCRIPTION "Parsing gigabytes of JSON per second"
LANGUAGES CXX C
)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MACOSX_RPATH OFF)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
endif()
project(simdjson)
set(SIMDJSON_LIB_NAME simdjson)
set(PROJECT_VERSION_MAJOR 0)
set(PROJECT_VERSION_MINOR 2)
set(PROJECT_VERSION_PATCH 0)
set(SIMDJSON_LIB_VERSION "0.2.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "0" CACHE STRING "simdjson library soversion")
set(PROJECT_VERSION_MINOR 6)
set(PROJECT_VERSION_PATCH 1)
set(SIMDJSON_SEMANTIC_VERSION "0.6.1" CACHE STRING "simdjson semantic version")
set(SIMDJSON_LIB_VERSION "4.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "4" CACHE STRING "simdjson library soversion")
set(SIMDJSON_GITHUB_REPOSITORY https://github.com/simdjson/simdjson)
if(NOT MSVC)
option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disables the production of a dynamic library
else()
option(SIMDJSON_BUILD_STATIC "Build a static library" ON) # turning it on disables the production of a dynamic library
endif()
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
include(GNUInstallDirs)
include(cmake/simdjson-flags.cmake)
include(cmake/simdjson-user-cmakecache.cmake)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
find_package(CTargets)
find_package(Options)
install(DIRECTORY include/${SIMDJSON_LIB_NAME} DESTINATION include)
set (TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/jsonchecker/")
set (BENCHMARK_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/jsonexamples/")
add_definitions(-DSIMDJSON_TEST_DATA_DIR="${TEST_DATA_DIR}")
add_definitions(-DSIMDJSON_BENCHMARK_DATA_DIR="${TEST_DATA_DIR}")
enable_testing()
if(SIMDJSON_JUST_LIBRARY)
message( STATUS "Building just the library, omitting all tests, tools and benchmarks." )
else(SIMDJSON_JUST_LIBRARY)
# Setup tests
enable_testing()
add_subdirectory(jsonchecker)
add_subdirectory(jsonexamples)
add_library(test-data INTERFACE)
target_link_libraries(test-data INTERFACE jsonchecker-data jsonchecker-minefield-data jsonexamples-data)
endif(SIMDJSON_JUST_LIBRARY)
# Create the top level simdjson library (must be done at this level to use both src/ and include/
# directories) and tools
#
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(tools)
add_subdirectory(tests)
add_subdirectory(benchmark)
add_subdirectory(windows)
if(NOT(SIMDJSON_JUST_LIBRARY))
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
add_subdirectory(singleheader)
endif()
install(FILES singleheader/simdjson.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
#
# Compile tools / tests / benchmarks
#
if(NOT(SIMDJSON_JUST_LIBRARY))
add_subdirectory(tests)
add_subdirectory(examples)
add_subdirectory(benchmark)
add_subdirectory(fuzz)
endif()
#
# Source files should be just ASCII
#
find_program(FIND find)
find_program(FILE file)
find_program(GREP grep)
if((FIND) AND (FILE) AND (GREP))
add_test(
NAME "just_ascii"
COMMAND sh -c "${FIND} include src windows tools singleheader tests examples benchmark -path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' -type f -exec ${FILE} '{}' \; |${GREP} -v ASCII || exit 0 && exit 1"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
#
# CPack
#
set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Parsing gigabytes of JSON per second")
+99
View File
@@ -0,0 +1,99 @@
Contributing
============
The simdjson library is an open project written in C++. Contributions are invited. Contributors
agree to the project's license.
We have an extensive list of issues, and contributions toward any of these issues is invited.
Contributions can take the form of code samples, better documentation or design ideas.
In particular, the following contributions are invited:
- The library is focused on performance. Well-documented performance optimization are invited.
- Fixes to known or newly discovered bugs are always welcome. Typically, a bug fix should come with
a test demonstrating that the bug has been fixed.
- The simdjson library is advanced software and maintainability and flexibility are always a
concern. Specific contributions to improve maintainability and flexibility are invited.
We discourage the following types of contributions:
- Code refactoring. We all have our preferences as to how code should be written, but unnecessary
refactoring can waste time and introduce new bugs. If you believe that refactoring is needed, you
first must explain how it helps in concrete terms. Does it improve the performance?
- Applications of new language features for their own sake. Using advanced C++ language constructs
is actually a negative as it may reduce portability (to old compilers, old standard libraries and
systems) and reduce accessibility (to programmers that have not kept up), so it must be offsetted
by clear gains like performance or maintainability. When in doubt, avoid advanced C++ features
(beyond C++11).
- Style formatting. In general, please abstain from reformatting code just to make it look prettier.
Though code formatting is important, it can also be a waste of time if several contributors try to
tweak the code base toward their own preference. Please do not introduce unneeded white-space
changes.
In short, most code changes should either bring new features or better performance. We want to avoid unmotivated code changes.
Specific rules
----------
We have few hard rules, but we have some:
- Printing to standard output or standard error (`stderr`, `stdout`, `std::cerr`, `std::cout`) in the core library is forbidden. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Compiled code should not write to stdout or stderr".
- Calls to `abort()` are forbidden in the core library. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Under no circumstances should your compiled code ever call abort or exit".
- All source code files (.h, .cpp) must be ASCII.
- All C macros introduced in public headers need to be prefixed with either `SIMDJSON_` or `simdjson_`.
Tools, tests and benchmarks are not held to these same strict rules.
General Guidelines
----------
Contributors are encouraged to :
- Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code.
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency.
- Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on.
- Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer if we do not understand.
- Provide tests for any new feature. We will not merge a new feature without tests.
Pull Requests
--------------
Pull requests are always invited. However, we ask that you follow these guidelines:
- It is wiser to discuss your ideas first as part of an issue before you start coding. If you omit this step and code first, be prepare to have your code receive scrutiny and be dropped.
- Users should provide a rationale for their changes. Does it improve performance? Does it add a feature? Does it improve maintainability? Does fix a bug? This must be explicitly stated as part of the pull request. Do not propose changes based on taste or intuition. We do not delegate programming to tools: that some tool suggested a code change is not reason enough to change the code.
1. When your code improves performance, please document the gains with a benchmark using hard numbers.
2. If your code fixes a bug, please be either fix a failing test, or propose a new test.
3. Other types of changes must be clearly motivated. We openly discourage changes with no identifiable benefits.
- 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.
If the benefits of your proposed code remain unclear, we may choose to discard your code: that is not an insult, we frequently discard our own code. We may also consider various alternatives and choose another path. Again, that is not an insult or a sign that you have wasted your time.
Style
-----
Our formatting style is inspired by the LLVM style.
The simdjson library is written using the snake case: when a variable or a function is a phrase, each space is replaced by an underscore character, and the first letter of each word written in lowercase. Compile-time constants are written entirely in uppercase with the same underscore convention.
Code of Conduct
---------------
Though we do not have a formal code of conduct, we will not tolerate bullying, bigotry or
intimidation. Everyone is welcome to contribute. If you have concerns, you can raise them privately with the core team members (e.g., D. Lemire, J. Keiser).
We welcome contributions from women and less represented groups. If you need help, please reach out.
Consider the following points when engaging with the project:
- We discourage arguments from authority: ideas are discusssed on their own merits and not based on who stated it.
- Be mindful that what you may view as an aggression is maybe merely a difference of opinion or a misunderstanding.
- Be mindful that a collection of small aggressions, even if mild in isolation, can become harmful.
Getting Started Hacking
-----------------------
An overview of simdjson's directory structure, with pointers to architecture and design
considerations and other helpful notes, can be found at [HACKING.md](HACKING.md).
+12
View File
@@ -23,5 +23,17 @@ Chang Liu
Sunny Gleason
John Keiser
Zach Bjornson
Vitaly Baranov
Juho Lauri
Michael Eisel
Io Daza Dillon
Paul Dreik
Jeremie Piotte
Matthew Wilson
Dušan Jovanović
Matjaž Ostroveršnik
Nong Li
Furkan Taşkale
Brendan Knapp
# if you have contributed to the project and your name does not
# appear in this list, please let us know!
+88 -10
View File
@@ -1,10 +1,88 @@
# docker build -t simdjson . && docker run --privileged -t simdjson
FROM gcc:8.3
COPY . /usr/src/
WORKDIR /usr/src/
RUN make clean
RUN make amalgamate
RUN make
RUN make test
RUN make parsingcompetition
CMD ["bash", "scripts/selectparser.sh"]
###
#
# Though simdjson requires only commonly available compilers and tools, it can
# be convenient to build it and test it inside a docker container: it makes it
# possible to test and benchmark simdjson under even relatively out-of-date
# Linux servers. It should also work under macOS and Windows, though not
# at native speeds, maybe.
#
# Assuming that you have a working docker server, this file
# allows you to build, test and benchmark simdjson.
#
# We build the library and associated files in the dockerbuild subdirectory.
# It may be necessary to delete it before creating the image:
#
# rm -r -f dockerbuild
#
# The need to delete the directory has nothing to do with docker per se: it is
# simply cleaner in CMake to start from a fresh directory. This is important: if you
# reuse the same directory with different configurations, you may get broken builds.
#
#
# Then you can build the image as follows:
#
# docker build -t simdjson --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) .
#
# Please note that the image does not contain a copy of the code. However, the image will contain the
# the compiler and the build system. This means that if you change the source code, after you have built
# the image, you won't need to rebuild the image. In fact, unless you want to try a different compiler, you
# do not need to ever rebuild the image, even if you do a lot of work on the source code.
#
# We specify the users to avoid having files owned by a privileged user (root) in our directory. Some
# people like to run their machine as the "root" user. We do not think it is cool.
#
# Then you need to build the project:
#
# docker run -v $(pwd):/project:Z simdjson
#
# Should you change a source file, you may need to call this command again. Because the output
# files are persistent between calls to this command (they reside in the dockerbuild directory),
# this command can be fast.
#
# 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"
#
# The run the complete tests requires you to have built all of simdjson.
#
# Building all of simdjson takes a long time. Instead, you can build just one target:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "[ -d dockerbuild ] || mkdir dockerbuild && cd dockerbuild && cmake .. && cmake --build . --target parse"
#
# Note that it is safe to remove dockerbuild before call the previous command, as the repository gets rebuild. It is also possible, by changing the command, to use a different directory name.
#
# You can run performance tests:
#
# docker run -it --privileged -v $(pwd):/project:Z simdjson sh -c "cd dockerbuild && for i in ../jsonexamples/*.json; do echo \$i; ./benchmark/parse \$i; done"
#
# The "--privileged" is recommended so you can get performance counters under Linux.
#
# You can also grab a fresh copy of simdjson and rebuild it, to make comparisons:
#
# docker run -it -v $(pwd):/project:Z simdjson sh -c "git clone https://github.com/simdjson/simdjson.git && cd simdjson && mkdir build && cd build && cmake .. && cmake --build . --target parse "
#
# Then you can run comparisons:
#
# docker run -it --privileged -v $(pwd):/project:Z simdjson sh -c "for i in jsonexamples/*.json; do echo \$i; dockerbuild/benchmark/parse \$i| grep GB| head -n 1; simdjson/build/benchmark/parse \$i | grep GB |head -n 1; done"
#
####
FROM ubuntu:20.10
################
# We would prefer to use the conan io images but they do not support 64-bit ARM? The small gcc images appear to
# be broken on ARM.
# Furthermore, we would not expect users to frequently rebuild the container, so using ubuntu is probably fine.
###############
ARG USER_ID
ARG GROUP_ID
RUN apt-get update -qq
RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata
RUN apt-get install -y cmake g++ git
RUN mkdir project
RUN addgroup --gid $GROUP_ID user; exit 0
RUN adduser --disabled-password --gecos '' --uid $USER_ID --gid $GROUP_ID user; exit 0
USER user
RUN gcc --version
WORKDIR /project
CMD ["sh","-c","[ -d dockerbuild ] || mkdir dockerbuild && cd dockerbuild && cmake .. && cmake --build . "]
+2578
View File
File diff suppressed because it is too large Load Diff
+292
View File
@@ -0,0 +1,292 @@
Hacking simdjson
================
Here is wisdom about how to build, test and run simdjson from within the repository. This is mostly useful for people who plan to contribute simdjson, or maybe study the design.
If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md) guide.
Design notes
------------------------------
The parser works in two stages:
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
The role of stage 1 is to identify pseudo-structural characters as quickly as possible. A character is pseudo-structural if and only if:
1. Not enclosed in quotes, AND
2. Is a non-whitespace character, AND
3. Its preceding character is either:
(a) a structural character, OR
(b) whitespace OR
(c) the final quote in a string.
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
Stage 1 also does unicode validation.
Stage 2 handles all of the rest: number parsings, recognizing atoms like true, false, null, and so forth.
Directory Structure and Source
------------------------------
simdjson's source structure, from the top level, looks like this:
* **CMakeLists.txt:** The main build system.
* **include:** User-facing declarations and inline definitions (most user-facing functions are inlined).
* simdjson.h: A "main include" that includes files from include/simdjson/. This is equivalent to
the distributed simdjson.h.
* simdjson/*.h: Declarations for public simdjson classes and functions.
* simdjson/*-inl.h: Definitions for public simdjson classes and functions.
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
implementations).
* simdjson.cpp: A "main source" that includes all implementation files from src/. This is
equivalent to the distributed simdjson.cpp.
* arm64/|fallback/|haswell/|westmere/: Architecture-specific implementations. All functions are
Each architecture defines its own namespace, e.g. simdjson::haswell.
* generic/: Generic implementations of the simdjson parser. These files may be included and
compiled multiple times, from whichever architectures use them. They assume they are already
enclosed in a namespace, e.g.:
```c++
namespace simdjson {
namespace haswell {
#include "generic/stage1/json_structural_indexer.h"
}
}
```
Other important files and directories:
* **.drone.yml:** Definitions for Drone CI.
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
* **.circleci:** Definitions for Circle CI.
* **.github/workflows:** Definitions for GitHub Actions (CI).
* **singleheader:** Contains generated `simdjson.h` and `simdjson.cpp` that we release. The files `singleheader/simdjson.h` and `singleheader/simdjson.cpp` should never be edited by hand.
* **singleheader/amalgamate.sh:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (bash script).
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnnecessary system calls. We recommend checking the performance as follows:
```bash
mkdir build
cd build
cmake ..
cmake --build . --config Release
benchmark/parse ../jsonexamples/twitter.json
```
The last line becomes `./benchmark/Release/parse.exe ../jsonexample/twitter.json` under Windows. You may also use Google Benchmark:
```bash
mkdir build
cd build
cmake ..
cmake --build . --target bench_parse_call --config Release
./benchmark/bench_parse_call
```
The last line becomes `./benchmark/Release/bench_parse_call.exe` under Windows. Under Windows, you can also build with the clang compiler by adding `-T ClangCL` to the call to `cmake ..`: `cmake .. - TClangCL`.
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
automatically, and is run in CI.
* **jsonchecker:** A set of JSON files used to check different functionality of the parser.
* **pass*.json:** Files that should pass validation.
* **fail*.json:** Files that should fail validation.
* **jsonchecker/minefield/y_*.json:** Files that should pass validation.
* **jsonchecker/minefield/n_*.json:** Files that should fail validation.
* **jsonexamples:** A wide spread of useful, real-world JSON files with different characteristics
and sizes.
* **test:** The tests are here. basictests.cpp and errortests.cpp are the primary ones.
* **tools:** Source for executables that can be distributed with simdjson. Some examples:
* `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
* `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
* `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
*`jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
> **Don't modify the files in singleheader/ directly; these are automatically generated.**
While simdjson distributes just two files from the singleheader/ directory, we *maintain* the code in
multiple files under include/ and src/. The files include/simdjson.h and src/simdjson.cpp are the "spine" for
these, and you can include them as if they were the corresponding singleheader/ files.
Runtime Dispatching
--------------------
A key feature of simdjson is the ability to compile different processing kernels, optimized for specific instruction sets, and to select
the most appropriate kernel at runtime. This ensures that users get the very best performance while still enabling simdjson to run everywhere.
This technique is frequently called runtime dispatching. The simdjson achieves runtime dispatching entirely in C++: we do not assume
that the user is building the code using CMake, for example.
To make runtime dispatching work, it is critical that the code be compiled for the lowest supported processor. In particular, you should
not use flags such as -mavx2, /arch:AVX2 and so forth while compiling simdjson. When you do so, you allow the compiler to use advanced
instructions. In turn, these advanced instructions present in the code may cause a runtime failure if the runtime processor does not
support them. Even a simple loop, compiled with these flags, might generate binary code that only run on advanced processors.
So we compile simdjson for a generic processor. Our users should do the same if they want simdjson's runtime dispatch to work. It is important
to understand that if runtime dispatching does not work, then simdjson will cause crashes on older processors. Of course, if a user chooses
to compile their code for a specific instruction set (e.g., AVX2), they are responsible for the failures if they later run their code
on a processor that does not support AVX2. Yet, if we were to entice these users to do so, we would share the blame: thus we carefully instruct
users to compile their code in a generic way without doing anything to enable advanced instructions.
We only use runtime dispatching on x64 (AMD/Intel) platforms, at the moment. On ARM processors, we would need a standard way to query, at runtime,
the processor for its supported features. We do not know how to do so on ARM systems in general. Thankfully it is not yet a concern: 64-bit ARM
processors are fairly uniform as far as the instruction sets they support.
In all cases, simdjson uses advanced instructions by relying on "intrinsic functions": we do not write assembly code. The intrinsic functions
are special functions that the compiler might recognize and translate into fast code. To make runtime dispatching work, we rely on the fact that
the header providing these instructions
(intrin.h under Visual Studio, x86intrin.h elsewhere) defines all of the intrinsic functions, including those that are not supported
processor.
At this point, we are require to use one of two main strategies.
1. On POSIX systems, the main compilers (LLVM clang, GNU gcc) allow us to use any intrinsic function after including the header, but they fail to inline the resulting instruction if the target processor does not support them. Because we compile for a generic processor, we would not be able to use most intrinsic functions. Thankfully, more recent versions of these compilers allow us to flag a region of code with a specific target, so that we can compile only some of the code with support for advanced instructions. Thus in our C++, one might notice macros like `TARGET_HASWELL`. It is then our responsability, at runtime, to only run the regions of code (that we call kernels) matching the properties of the runtime processor. The benefit of this approach is that the compiler not only let us use intrinsic functions, but it can also optimize the rest of the code in the kernel with advanced instructions we enabled.
2. Under Visual Studio, the problem is somewhat simpler. Visual Studio will not only provide the intrinsic functions, but it will also allow us to use them. They will compile just fine. It is at runtime that they may cause a crash. So we do not need to mark regions of code for compilation toward advanced processors (e.g., with `TARGET_HASWELL` macros). The downside of the Visual Studio approach is that the compiler is not allowed to use advanced instructions others than those we specify. In principle, this means that Visual Studio has weaker optimization opportunities.
We also handle the special case where a user is compiling using LLVM clang under Windows, [using the Visual Studio toolchain](https://devblogs.microsoft.com/cppblog/clang-llvm-support-in-visual-studio/). If you compile with LLVM clang under Visual Studio, then the header files (intrin.h or x86intrin.h) no longer provides the intrinsic functions that are unsupported by the processor. This appears to be deliberate on the part of the LLVM engineers. With a few lines of code, we handle this scenario just like LLVM clang under a POSIX system, but forcing the inclusion of the specific headers, and rolling our own intrinsic function as needed.
Regenerating Single-Header Files
---------------------------------------
The simdjson.h and simdjson.cpp files in the singleheader directory are not always up-to-date with the rest of the code; they are only ever
systematically regenerated on releases. To ensure you have the latest code, you can regenerate them by running this at the top level:
```bash
mkdir build
cd build
cmake ..
cmake --build . --target amalgamate
```
You need to have a working bash on your system.
The amalgamator script is `amalgamate.sh` generates singleheader/simdjson.h by
reading through include/simdjson.h, copy/pasting each header file into the amalgamated file at the
point it gets included (but only once per header). singleheader/simdjson.cpp is generated from
src/simdjson.cpp the same way, except files under generic/ may be included and copy/pasted multiple
times.
### Usage (CMake on 64-bit platforms like Linux, FreeBSD or macOS)
Requirements: In addition to git, we require a recent version of CMake as well as bash.
1. On macOS, the easiest way to install cmake might be to use [brew](https://brew.sh) and then type
```
brew install cmake
```
2. Under Linux, you might be able to install CMake as follows:
```
apt-get update -qq
apt-get install -y cmake
```
3. On FreeBSD, you might be able to install bash and CMake as follows:
```
pkg update -f
pkg install bash
pkg install cmake
```
You need a recent compiler like clang or gcc. We recommend at least GNU GCC/G++ 7 or LLVM clang 6.
Building: While in the project repository, do the following:
```
mkdir build
cd build
cmake ..
cmake --build .
ctest
```
CMake will build a library. By default, it builds a shared library (e.g., libsimdjson.so on Linux).
You can build a static library:
```
mkdir buildstatic
cd buildstatic
cmake -DSIMDJSON_BUILD_STATIC=ON ..
cmake --build .
ctest
```
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`. You can also do it as part of the `cmake` command: `cmake .. -DCMAKE_CXX_COMPILER=g++`. You may proceed as follows:
```
brew install gcc@8
mkdir build
cd build
export CXX=g++-8 CC=gcc-8
cmake ..
cmake --build .
ctest
```
If your compiler does not default on C++11 support or better you may get failing tests. If so, you may be able to exclude the failing tests by replacing `ctest` with `ctest -E "^quickstart$"`.
Note that the name of directory (`build`) is arbitrary, you can name it as you want (e.g., `buildgcc`) and you can have as many different such directories as you would like (one per configuration).
### Usage (CMake on 64-bit Windows using Visual Studio 2019)
We assume you have a common 64-bit Windows PC with at least Visual Studio 2019.
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
- Create a subdirectory within simdjson, such as `build`.
- Using a shell, go to this newly created directory. You can start a shell directly from GitHub Desktop (Repository > Open in Command Prompt).
- Type `cmake ..` in the shell while in the `build` repository.
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
Though having Visual Studio installed is necessary, one can build simdjson using only cmake commands:
- `mkdir build`
- `cd build`
- `cmake ..`
- `cmake --build . -config Release`
Furthermore, if you have installed LLVM clang on Windows, for example as a component of Visual Studio 2019, you can configure and build simdjson using LLVM clang on Windows using cmake:
- `mkdir build`
- `cd build`
- `cmake .. -T ClangCL`
- `cmake --build . -config Release`
### Various References
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
- [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php)
- https://tools.ietf.org/html/rfc7159
- http://rapidjson.org/md_doc_sax.html
- https://github.com/Geal/parser_benchmarks/tree/master/json
- Gron: A command line tool that makes JSON greppable https://news.ycombinator.com/item?id=16727665
- GoogleGson https://github.com/google/gson
- Jackson https://github.com/FasterXML/jackson
- https://www.yelp.com/dataset_challenge
- RapidJSON. http://rapidjson.org/
Inspiring links:
- https://auth0.com/blog/beating-json-performance-with-protobuf/
- https://gist.github.com/shijuvar/25ad7de9505232c87034b8359543404a
- https://github.com/frankmcsherry/blog/blob/master/posts/2018-02-11.md
-202
View File
@@ -1,202 +0,0 @@
.SUFFIXES:
#
.SUFFIXES: .cpp .o .c .h
.PHONY: clean cleandist
COREDEPSINCLUDE = -Idependencies/rapidjson/include -Idependencies/sajson/include -Idependencies/cJSON -Idependencies/jsmn
EXTRADEPSINCLUDE = -Idependencies/jsoncppdist -Idependencies/json11 -Idependencies/fastjson/src -Idependencies/fastjson/include -Idependencies/gason/src -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src
# users can provide their own additional flags with make EXTRAFLAGS=something
architecture:=$(shell arch)
####
# If you want to specify your own target architecture,
# then define ARCHFLAGS. Otherwise, we set good default.
# E.g., type ' ARCHFLAGS="-march=nehalem" make parse '
###
ifeq ($(architecture),aarch64)
ARCHFLAGS ?= -march=armv8-a+crc+crypto
else
ARCHFLAGS ?= -msse4.2 -mpclmul # lowest supported feature set?
endif
CXXFLAGS = $(ARCHFLAGS) -std=c++17 -Wall -Wextra -Wshadow -Iinclude -Ibenchmark/linux $(EXTRAFLAGS)
CFLAGS = $(ARCHFLAGS) -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src $(EXTRAFLAGS)
# This is a convenience flag
ifdef SANITIZEGOLD
SANITIZE = 1
LINKER = gold
endif
ifdef LINKER
CXXFLAGS += -fuse-ld=$(LINKER)
CFLAGS += -fuse-ld=$(LINKER)
endif
# SANITIZE *implies* DEBUG
ifeq ($(MEMSANITIZE),1)
CXXFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
CFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
else
ifeq ($(SANITIZE),1)
CXXFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
CFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
else
ifeq ($(DEBUG),1)
CXXFLAGS += -g3 -O0
CFLAGS += -g3 -O0
else
# we opt for -O3 for regular builds
CXXFLAGS += -O3
CFLAGS += -O3
endif # ifeq ($(DEBUG),1)
endif # ifeq ($(SANITIZE),1)
endif # ifeq ($(MEMSANITIZE),1)
MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer
TESTEXECUTABLES=jsoncheck numberparsingcheck stringparsingcheck pointercheck
COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompetition distinctuseridcompetition allparserscheckfile allparsingcompetition
SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing
HEADERS= include/simdjson/simdutf8check_haswell.h include/simdjson/simdutf8check_westmere.h include/simdjson/simdutf8check_arm64.h include/simdjson/stringparsing.h include/simdjson/stringparsing_arm64.h include/simdjson/stringparsing_haswell.h include/simdjson/stringparsing_macros.h include/simdjson/stringparsing_westmere.h include/simdjson/numberparsing.h include/simdjson/jsonparser.h include/simdjson/common_defs.h include/simdjson/jsonioutil.h benchmark/benchmark.h benchmark/linux/linux-perf-events.h include/simdjson/parsedjson.h include/simdjson/stage1_find_marks.h include/simdjson/stage1_find_marks_arm64.h include/simdjson/stage1_find_marks_haswell.h include/simdjson/stage1_find_marks_westmere.h include/simdjson/stage1_find_marks_macros.h include/simdjson/stage2_build_tape.h include/simdjson/jsoncharutils.h include/simdjson/jsonformatutils.h include/simdjson/stage1_find_marks_flatten.h include/simdjson/stage1_find_marks_flatten_haswell.h
LIBFILES=src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp
MINIFIERHEADERS=include/simdjson/jsonminifier.h include/simdjson/simdprune_tables.h
MINIFIERLIBFILES=src/jsonminifier.cpp
RAPIDJSON_INCLUDE:=dependencies/rapidjson/include
SAJSON_INCLUDE:=dependencies/sajson/include
JSON11_INCLUDE:=dependencies/json11/json11.hpp
FASTJSON_INCLUDE:=dependencies/include/fastjson/fastjson.h
GASON_INCLUDE:=dependencies/gason/src/gason.h
UJSON4C_INCLUDE:=dependencies/ujson4c/src/ujdecode.c
CJSON_INCLUDE:=dependencies/cJSON/cJSON.h
JSMN_INCLUDE:=dependencies/jsmn/jsmn.h
LIBS=$(RAPIDJSON_INCLUDE) $(SAJSON_INCLUDE) $(JSON11_INCLUDE) $(FASTJSON_INCLUDE) $(GASON_INCLUDE) $(UJSON4C_INCLUDE) $(CJSON_INCLUDE) $(JSMN_INCLUDE)
EXTRAOBJECTS=ujdecode.o
all: $(MAINEXECUTABLES)
competition: $(COMPARISONEXECUTABLES)
.PHONY: benchmark test
benchmark:
bash ./scripts/parser.sh
bash ./scripts/parseandstat.sh
test: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
./basictests
./numberparsingcheck
./stringparsingcheck
./jsoncheck
./pointercheck
./scripts/testjson2json.sh
./scripts/issue150.sh
@echo "It looks like the code is good!"
quiettest: jsoncheck numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
./basictests
./numberparsingcheck
./stringparsingcheck
./jsoncheck
./pointercheck
./scripts/testjson2json.sh
./scripts/issue150.sh
amalgamate:
./amalgamation.sh
$(CXX) $(CXXFLAGS) -o singleheader/demo ./singleheader/amalgamation_demo.cpp -Isingleheader
submodules:
-git submodule update --init --recursive
-touch submodules
$(SAJSON_INCLUDE) $(RAPIDJSON_INCLUDE) $(JSON11_INCLUDE) $(FASTJSON_INCLUDE) $(GASON_INCLUDE) $(UJSON4C_INCLUDE) $(CJSON_INCLUDE) $(JSMN_INCLUDE) : submodules
parse: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o parse $(LIBFILES) benchmark/parse.cpp $(LIBFLAGS)
statisticalmodel: benchmark/statisticalmodel.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o statisticalmodel $(LIBFILES) benchmark/statisticalmodel.cpp $(LIBFLAGS)
parse_noutf8validation: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o parse_noutf8validation -DSIMDJSON_SKIPUTF8VALIDATION $(LIBFILES) benchmark/parse.cpp $(LIBFLAGS)
parse_nonumberparsing: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o parse_nonumberparsing -DSIMDJSON_SKIPNUMBERPARSING $(LIBFILES) benchmark/parse.cpp $(LIBFLAGS)
parse_nostringparsing: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o parse_nostringparsing -DSIMDJSON_SKIPSTRINGPARSING $(LIBFILES) benchmark/parse.cpp $(LIBFLAGS)
jsoncheck:tests/jsoncheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o jsoncheck $(LIBFILES) tests/jsoncheck.cpp -I. $(LIBFLAGS)
basictests:tests/basictests.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o basictests $(LIBFILES) tests/basictests.cpp -I. $(LIBFLAGS)
numberparsingcheck:tests/numberparsingcheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o numberparsingcheck tests/numberparsingcheck.cpp src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/parsedjson.cpp -I. $(LIBFLAGS) -DJSON_TEST_NUMBERS
stringparsingcheck:tests/stringparsingcheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o stringparsingcheck tests/stringparsingcheck.cpp src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/parsedjson.cpp -I. $(LIBFLAGS) -DJSON_TEST_STRINGS
pointercheck:tests/pointercheck.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o pointercheck tests/pointercheck.cpp src/stage2_build_tape.cpp src/jsonioutil.cpp src/jsonparser.cpp src/simdjson.cpp src/stage1_find_marks.cpp src/parsedjson.cpp src/parsedjsoniterator.cpp -I. $(LIBFLAGS)
minifiercompetition: benchmark/minifiercompetition.cpp $(HEADERS) submodules $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIBFILES)
$(CXX) $(CXXFLAGS) -o minifiercompetition $(LIBFILES) $(MINIFIERLIBFILES) benchmark/minifiercompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
minify: tools/minify.cpp $(HEADERS) $(MINIFIERHEADERS) $(LIBFILES) $(MINIFIERLIBFILES)
$(CXX) $(CXXFLAGS) -o minify $(MINIFIERLIBFILES) $(LIBFILES) tools/minify.cpp -I.
json2json: tools/json2json.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o json2json $ tools/json2json.cpp $(LIBFILES) -I.
jsonpointer: tools/jsonpointer.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o jsonpointer $ tools/jsonpointer.cpp $(LIBFILES) -I.
jsonstats: tools/jsonstats.cpp $(HEADERS) $(LIBFILES)
$(CXX) $(CXXFLAGS) -o jsonstats $ tools/jsonstats.cpp $(LIBFILES) -I.
ujdecode.o: $(UJSON4C_INCLUDE)
$(CC) $(CFLAGS) -c dependencies/ujson4c/src/ujdecode.c
parseandstatcompetition: benchmark/parseandstatcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o parseandstatcompetition $(LIBFILES) benchmark/parseandstatcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
distinctuseridcompetition: benchmark/distinctuseridcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o distinctuseridcompetition $(LIBFILES) benchmark/distinctuseridcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
parsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) submodules
$(CXX) $(CXXFLAGS) -o parsingcompetition $(LIBFILES) benchmark/parsingcompetition.cpp -I. $(LIBFLAGS) $(COREDEPSINCLUDE)
allparsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
$(CXX) $(CXXFLAGS) -o allparsingcompetition $(LIBFILES) benchmark/parsingcompetition.cpp $(EXTRAOBJECTS) -I. $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE) -DALLPARSER
allparserscheckfile: tests/allparserscheckfile.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
$(CXX) $(CXXFLAGS) -o allparserscheckfile $(LIBFILES) tests/allparserscheckfile.cpp $(EXTRAOBJECTS) -I. $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE)
.PHONY: clean cppcheck cleandist
cppcheck:
cppcheck --enable=all src/*.cpp benchmarks/*.cpp tests/*.cpp -Iinclude -I. -Ibenchmark/linux
everything: $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
clean:
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
cleandist:
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
-85
View File
@@ -1,85 +0,0 @@
# Notes on simdjson
## Rationale:
The simdjson project serves two purposes:
1. It creates a useful library for parsing JSON data quickly.
2. It is a demonstration of the use of SIMD and pipelined programming techniques to perform a complex and irregular task.
These techniques include the use of large registers and SIMD instructions to process large amounts of input data at once,
to hold larger entities than can typically be held in a single General Purpose Register (GPR), and to perform operations
that are not cheap to perform without use of a SIMD unit (for example table lookup using permute instructions).
The other key technique is that the system is designed to minimize the number of unpredictable branches that must be taken
to perform the task. Modern architectures are both wide and deep (4-wide pipelines with ~14 stages are commonplace). A
recent Intel Architecture processor, for example, can perform 3 256-bit SIMD operations or 2 512-bit SIMD operations per
cycle as well as other operations on general purpose registers or with the load/store unit. An incorrectly predicted branch
will clear this pipeline. While it is rare that a programmer can achieve the maximum throughput on a machine, a developer
may be missing the opportunity to carry out 56 operations for each branch miss.
Many code-bases make use of SIMD and deeply pipelined, "non-branchy", processing for regular tasks. Numerical problems
(e.g. "matrix multiply") or simple 'bulk search' tasks (e.g. "count all the occurrences of a given character in a text",
"find the first occurrence of the string 'foo' in a text") frequently use this class of techniques. We are demonstrating
that these techniques can be applied to much more complex and less regular tasks.
## Design:
### Stage 1: SIMD over bytes; bit vector processing over bytes.
The first stage of our processing must identify key points in our input: the 'structural characters' of JSON (curly and
square braces, colon, and comma), the start and end of strings as delineated by double quote characters, other JSON 'atoms'
that are not distinguishable by simple characters (constructs such as "true", "false", "null" and numbers), as well as
discovering these characters and atoms in the presence of both quoting conventions and backslash escaping conventions.
As such we follow the broad outline of the construction of a structural index as set forth in the Mison paper [XXX]; first,
the discovery of odd-length sequences of backslash characters (which will cause quote characters immediately following to
be escaped and not serve their quoting role but instead be literal charaters), second, the discovery of quote pairs (which
cause structural characters within the quote pairs to also be merely literal characters and have no function as structural
characters), then finally the discovery of structural characters not contained without the quote pairs.
We depart from the Mison paper in terms of method and overall design. In terms of method, the Mison paper uses iteration
over bit vectors to discover backslash sequences and quote pairs; we introduce branch-free techniques to discover both of
these properties.
We also make use of our ability to quickly detect whitespace in this early stage. We can use another bit-vector based
transformation to discover locations in our data that follow a structural character or quote or whitespace and are not whitespace. Excluding locations within strings, and the structural characters we have already discovered,
these locations are the only place that we can expect to see the starts of the JSON 'atoms'. These locations are thus
treated as 'structural' ('pseudo-structural characters').
This stage involves either SIMD processing over bytes or the manipulation of bit arrays that have 1 bit corresponding
to 1 byte of input. As such, it can be quite inefficient for some inputs - it is possible to observe dozens of operations
taking place to discover that there are in fact no odd-numbered sequences of backslashes or quotes in a given block of
input. However, this inefficiency on such inputs is balanced by the fact that it costs no more to run this code over
complex structured input, and the alternatives would generally involve running a number of unpredictable branches (for
example, the loop branches in Mison that iterate over bit vectors).
### Stage 2: The transition from "SIMD over bytes" to "indices"
Our structural, pseudo-structural and other 'interesting' characters are relatively rare (TODO: quantify in detail -
it's typically about 1 in 10). As such, continuing to process them as bit vectors will involve manipulating data structures
that are relatively large as well as being fairly unpredictably spaced. We must transform these bitvectors of "interesting"
locations into offsets.
Note that we can examine the character at the offset to discover what the original function of the item in the bitvector
was. While the JSON structural characters and quotes are relatively self-explanatory (although working only with one offset
at a time, we have lost the distinction between opening quotes and closing quotes, something that was available in Stage 1),
it is a quirk of JSON that the legal atoms can all be distinguished from each other by their first character - 't' for
'true', 'f' for 'false', 'n' for 'null' and the character class [0-9-] for numerical values.
Thus, the offset suffices, as long as we retain our original input.
Our current implementation involves a straightforward transformation of bitmaps to indices by use of the 'count trailing
zeros' operation and the well-known operation to clear the lowest set bit. Note that this implementation introduces an
unpredictable branch; unless there is a regular pattern in our bitmaps, we would expect to have at least one branch miss
for each bitmap.
### Stage 3: Operation over indices
This now works over a dual structure.
1. The "state machine", whose role it is to validate the sequence of structural characters and ensure that the input is at least generally structured like valid JSON (after this stage, the only errors permissible should be malformed atoms and numbers). If and only if the "state machine" reached all accept states, then,
2. The "tape machine" will have produced valid output. The tape machine works blindly over characters writing records to tapes. These records create a lean but somewhat traversable linked structure that, for valid inputs, should represent what we need to know about the JSON input.
FIXME: a lot more detail is required on the operation of both these machines.
+156 -526
View File
@@ -1,576 +1,206 @@
# simdjson : Parsing gigabytes of JSON per second
[![Build Status](https://cloud.drone.io/api/badges/lemire/simdjson/status.svg)](https://cloud.drone.io/lemire/simdjson/)
[![CircleCI](https://circleci.com/gh/lemire/simdjson.svg?style=svg)](https://circleci.com/gh/lemire/simdjson)
[![Build Status](https://img.shields.io/appveyor/ci/lemire/simdjson.svg)](https://ci.appveyor.com/project/lemire/simdjson)
[![][license img]][license]
[![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/lemire/simdjson.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/lemire/simdjson/context:cpp)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&q=proj%3Asimdjson&can=2)
![Ubuntu 18.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
![VS16-CI](https://github.com/simdjson/simdjson/workflows/VS16-CI/badge.svg)
![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.6.0/index.html)
simdjson : Parsing gigabytes of JSON per second
===============================================
## A C++ library to see how fast we can parse JSON with complete validation.
<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++.
JSON documents are everywhere on the Internet. Servers spend a lot of time parsing these documents. We want to accelerate the parsing of JSON per se using commonly available SIMD instructions as much as possible while doing full validation (including character encoding).
* **Fast:** Over 2.5x faster than commonly used production-grade JSON parsers.
* **Record Breaking Features:** Minify JSON at 6 GB/s, validate UTF-8 at 13 GB/s, NDJSON at 3.5 GB/s.
* **Easy:** First-class, easy to use and carefully documented APIs.
* **Beyond DOM:** Try the new On Demand API for twice the speed (>4GB/s).
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
* **Peer Reviewed:** Our research appears in venues like VLDB Journal, Software: Practice and Experience.
<img src="images/logo.png" width="10%">
This library is part of the [Awesome Modern C++](https://awesomecpp.com) list.
Table of Contents
-----------------
## Real-world usage
* [Quick Start](#quick-start)
* [Documentation](#documentation)
* [Performance results](#performance-results)
* [Real-world usage](#real-world-usage)
* [Bindings and Ports of simdjson](#bindings-and-ports-of-simdjson)
* [About simdjson](#about-simdjson)
* [Funding](#funding)
* [Contributing to simdjson](#contributing-to-simdjson)
* [License](#license)
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
- [Yandex ClickHouse](https://github.com/yandex/ClickHouse)
Quick Start
-----------
## Paper
The simdjson library is easily consumable with a single .h and .cpp file.
A description of the design and implementation of simdjson appears at https://arxiv.org/abs/1902.08318 and an informal blog post providing some background and context is at https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/.
0. Prerequisites: `g++` (version 7 or better) or `clang++` (version 6 or better), and a 64-bit system with a command-line shell (e.g., Linux, macOS, freeBSD). We also support programming environnements like Visual Studio and Xcode, but different steps are needed.
1. Pull [simdjson.h](singleheader/simdjson.h) and [simdjson.cpp](singleheader/simdjson.cpp) into a directory, along with the sample file [twitter.json](jsonexamples/twitter.json).
```
wget https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.h https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.cpp https://raw.githubusercontent.com/simdjson/simdjson/master/jsonexamples/twitter.json
```
2. Create `quickstart.cpp`:
Some people [enjoy reading our paper](https://arxiv.org/abs/1902.08318):
```c++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
}
```
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
4. `./quickstart`
```
100 results.
```
[<img src="images/halvarflake.png" width="50%">](https://twitter.com/halvarflake/status/1118459536686362625)
Documentation
-------------
Usage documentation is available:
## Performance results
* [Basics](doc/basics.md) is an overview of how to use simdjson and its APIs.
* [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.6.0/annotated.html) contains the automatically generated API documentation.
simdjson uses three-quarters less instructions than state-of-the-art parser RapidJSON and fifty percent less than sajson. To our knowledge, simdjson is the first fully-validating JSON parser to run at gigabytes per second on commodity processors.
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
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.
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.
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.2 |
| RapidJSON encoding-validation | 0.51 |
| RapidJSON encoding-validation, insitu | 0.71 |
| sajson (insitu, dynamic) | 0.70 |
| sajson (insitu, static) | 0.97 |
| dropbox | 0.14 |
| fastjson | 0.26 |
| gason | 0.85 |
| ultrajson | 0.42 |
| jsmn | 0.28 |
| cJSON | 0.34 |
| 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 |
## Requirements
- We support platforms like Linux or macOS, as well as Windows through Visual Studio 2017 or later.
- A processor with
- AVX2 (i.e., Intel processors starting with the Haswell microarchitecture released 2013 and AMD processors starting with the Zen microarchitecture released 2017),
- or SSE 4.2 and CLMUL (i.e., Intel processors going back to Westmere released in 2010 or AMD processors starting with the Jaguar used in the PS4 and XBox One)
- or a 64-bit ARM processor (ARMv8-A): this covers a wide range of mobile processors, including all Apple processors currently available for sale, going back as far back as the iPhone 5s (2013).
- A recent C++ compiler (e.g., GNU GCC or LLVM CLANG or Visual Studio 2017), we assume C++17. GNU GCC 7 or better or LLVM's clang 6 or better.
- Some benchmark scripts assume bash and other common utilities, but they are optional.
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%">
## License
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
This code is made available under the Apache License 2.0.
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it under the liberal (business-friendly) MIT license.
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).
## Code usage and example
The main API involves populating a `ParsedJson` object which hosts a fully navigable document-object-model (DOM) view of the JSON document. The DOM can be accessed using [JSON Pointer](https://tools.ietf.org/html/rfc6901) paths, for example. The main function is `json_parse` which takes a string containing the JSON document as well as a reference to pre-allocated `ParsedJson` object (which can be reused multiple time). Once you have populated the `ParsedJson` object you can navigate through the DOM with an iterator (e.g., created by `ParsedJson::Iterator pjh(pj)`, see 'Navigating the parsed document').
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
Real-world usage
----------------
/...
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
- [Yandex ClickHouse](https://github.com/yandex/ClickHouse)
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
const char * filename = ... //
If you are planning to use simdjson in a product, please work from one of our releases.
// use whatever means you want to get a string (UTF-8) of your JSON document
padded_string p = get_corpus(filename);
ParsedJson pj;
pj.allocate_capacity(p.size()); // allocate memory for parsing up to p.size() bytes
const int res = json_parse(p, pj); // do the parsing, return 0 on success
// parsing is done!
if (res != 0) {
// You can use the "simdjson/simdjson.h" header to access the error message
std::cout << "Error parsing:" << simdjson::error_message(res) << std::endl;
}
// the ParsedJson document can be used here
// pj can be reused with other json_parse calls.
```
It is also possible to use a simpler API if you do not mind having the overhead
of memory allocation with each new JSON document:
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
const char * filename = ... //
padded_string p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // do the parsing
if( ! pj.is_valid() ) {
// something went wrong
std::cout << pj.get_error_message() << std::endl;
}
```
Though the `padded_string` class is recommended for best performance, you can call `json_parse` and `build_parsed_json`, passing a standard `std::string` object.
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
std::string mystring = ... //
ParsedJson pj;
pj.allocate_capacity(mystring.size()); // allocate memory for parsing up to p.size() bytes
// std::string may not overallocate so a copy will be needed
const int res = json_parse(mystring, pj); // do the parsing, return 0 on success
// parsing is done!
if (res != 0) {
// You can use the "simdjson/simdjson.h" header to access the error message
std::cout << "Error parsing:" << simdjson::error_message(res) << std::endl;
}
// pj can be reused with other json_parse calls.
```
or
```C
#include "simdjson/jsonparser.h"
using namespace simdjson;
/...
std::string mystring = ... //
// std::string may not overallocate so a copy will be needed
ParsedJson pj = build_parsed_json(mystring); // do the parsing
if( ! pj.is_valid() ) {
// something went wrong
std::cout << pj.get_error_message() << std::endl;
}
```
As needed, the `json_parse` and `build_parsed_json` functions copy the input data to a temporary buffer readable up to SIMDJSON_PADDING bytes beyond the end of the data.
## Usage: easy single-header version
See the "singleheader" repository for a single header version. See the included
file "amalgamation_demo.cpp" for usage. This requires no specific build system: just
copy the files in your project in your include path. You can then include them quite simply:
```C
#include <iostream>
#include "simdjson.h"
#include "simdjson.cpp"
using namespace simdjson;
int main(int argc, char *argv[]) {
const char * filename = argv[1];
padded_string p = get_corpus(filename);
ParsedJson pj = build_parsed_json(p); // do the parsing
if( ! pj.is_valid() ) {
std::cout << "not valid" << std::endl;
std::cout << pj.get_error_message() << std::endl;
} else {
std::cout << "valid" << std::endl;
}
return EXIT_SUCCESS;
}
```
Note: In some settings, it might be desirable to precompile `simdjson.cpp` instead of including it.
## Runtime dispatch
On Intel and AMD processors, we get best performance by using the hardware support for AVX2 instructions. However, simdjson also
runs on older Intel and AMD processors. We require a minimum feature support of SSE 4.2 and CLMUL (2010 Intel Westmere or better).
The code automatically detects the feature set of your processor and switches to the right function at runtime (a technical
sometimes called runtime dispatch).
We also support 64-bit ARM. We assume NEON support, and if the cryptographic extension is available, we leverage it, at compile-time.
There is no runtime dispatch on ARM.
## Usage (old-school Makefile on platforms like Linux or macOS)
Requirements: recent clang or gcc, and make. We recommend at least GNU GCC/G++ 7 or LLVM clang 6. A system like Linux or macOS is expected.
To test:
```
make
make test
```
To run benchmarks:
```
make parse
./parse jsonexamples/twitter.json
```
Under Linux, the `parse` command gives a detailed analysis of the performance counters.
To run comparative benchmarks (with other parsers):
```
make benchmark
```
## Usage (CMake on platforms like Linux or macOS)
Requirements: We require a recent version of cmake. On macOS, the easiest way to install cmake might be to use [brew](https://brew.sh) and then type
```
brew install cmake
```
There is an [equivalent brew on Linux which works the same way as well](https://linuxbrew.sh).
You need a recent compiler like clang or gcc. We recommend at least GNU GCC/G++ 7 or LLVM clang 6. For example, you can install a recent compiler with brew:
```
brew install gcc@8
```
Optional: You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`.
Building: While in the project repository, do the following:
```
mkdir build
cd build
cmake ..
make
make test
```
CMake will build a library. By default, it builds a shared library (e.g., libsimdjson.so on Linux).
You can build a static library:
```
mkdir buildstatic
cd buildstatic
cmake -DSIMDJSON_BUILD_STATIC=ON ..
make
make test
```
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You may proceed as follows:
```
brew install gcc@8
mkdir build
cd build
export CXX=g++-8 CC=gcc-8
cmake ..
make
make test
```
## Usage (CMake on Windows using Visual Studio)
We assume you have a common Windows PC with at least Visual Studio 2017 and an x64 processor with AVX2 support (2013 Intel Haswell or later) or SSE 4.2 + CLMUL (2010 Westmere or later).
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
- Create a subdirectory within simdjson, such as `VisualStudio`.
- Using a shell, go to this newly created directory.
- Type `cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..` in the shell while in the `VisualStudio` repository. (Alternatively, if you want to build a DLL, you may use the command line `cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..`.)
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
## Usage (Using `vcpkg` on Windows, Linux and MacOS)
[vcpkg](https://github.com/Microsoft/vcpkg) users on Windows, Linux and MacOS can download and install `simdjson` with one single command from their favorite shell.
On Linux and MacOS:
```
$ ./vcpkg install simdjson
```
will build and install `simdjson` as a static library.
On Windows (64-bit):
```
.\vcpkg.exe install simdjson:x64-windows
```
will build and install `simdjson` as a shared library.
```
.\vcpkg.exe install simdjson:x64-windows-static
```
will build and install `simdjson` as a static library.
These commands will also print out instructions on how to use the library from MSBuild or CMake-based projects.
If you find the version of `simdjson` shipped with `vcpkg` is out-of-date, feel free to report it to `vcpkg` community either by submiting an issue or by creating a PR.
## Tools
- `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
- `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
- `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
- `jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
## Scope
We provide a fast parser, that fully validates an input according to various specifications.
The parser builds a useful immutable (read-only) DOM (document-object model) which can be later accessed.
To simplify the engineering, we make some assumptions.
- We support UTF-8 (and thus ASCII), nothing else (no Latin, no UTF-16). We do not believe this is a genuine limitation, because we do not think there is any serious application that needs to process JSON data without an ASCII or UTF-8 encoding. If the UTF-8 contains a leading BOM, it should be omitted: the user is responsible for detecting and skipping the BOM; UTF-8 BOMs are discouraged.
- All strings in the JSON document may have up to 4294967295 bytes in UTF-8 (4GB). To enforce this constraint, we refuse to parse a document that contains more than 4294967295 bytes (4GB). This should accommodate most JSON documents.
- As allowed by the specification, we allow repeated keys within an object (other parsers like sajson do the same).
- Performance is optimized for JSON documents spanning at least a tens kilobytes up to many megabytes: the performance issues with having to parse many tiny JSON documents or one truly enormous JSON document are different.
_We do not aim to provide a general-purpose JSON library._ A library like RapidJSON offers much more than just parsing, it helps you generate JSON and offers various other convenient functions. We merely parse the document.
## Features
- The input string is unmodified. (Parsers like sajson and RapidJSON use the input string as a buffer.)
- We parse integers and floating-point numbers as separate types which allows us to support large 64-bit integers in [-9223372036854775808,9223372036854775808), like a Java `long` or a C/C++ `long long`. Among the parsers that differentiate between integers and floating-point numbers, not all support 64-bit integers. (For example, sajson rejects JSON files with integers larger than or equal to 2147483648. RapidJSON will parse a file containing an overly long integer like 18446744073709551616 as a floating-point number.) When we cannot represent exactly an integer as a signed 64-bit value, we reject the JSON document.
- We support the full range of 64-bit floating-point numbers (binary64). The values range from ` std::numeric_limits<double>::lowest()` to `std::numeric_limits<double>::max()`, so from -1.7976e308 all the way to 1.7975e308. Extreme values (less or equal to -1e308, greater or equal to 1e308) are rejected: we refuse to parse the input document.
- We test for accurate float parsing with a bound on the [unit of least precision (ULP)](https://en.wikipedia.org/wiki/Unit_in_the_last_place) of one. Practically speaking, this implies 15 digits of accuracy or better.
- We do full UTF-8 validation as part of the parsing. (Parsers like fastjson, gason and dropbox json11 do not do UTF-8 validation. The sajson parser does incomplete UTF-8 validation, accepting code point
sequences like 0xb1 0x87.)
- We fully validate the numbers. (Parsers like gason and ultranjson will accept `[0e+]` as valid JSON.)
- We validate string content for unescaped characters. (Parsers like fastjson and ultrajson accept unescaped line breaks and tabs in strings.)
- We fully validate the white-space characters outside of the strings. Parsers like RapidJSON will accept JSON documents with null characters outside of strings.
## Architecture
The parser works in two stages:
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
## JSON Pointer
We can navigate the parsed JSON using JSON Pointers as per the [RFC6901 standard](https://tools.ietf.org/html/rfc6901).
You can build a tool (jsonpointer) to parse a JSON document and then issue an array of JSON Pointer queries:
```
make jsonpointer
./jsonpointer jsonexamples/small/demo.json /Image/Width /Image/Height /Image/IDs/2
./jsonpointer jsonexamples/twitter.json /statuses/0/id /statuses/1/id /statuses/2/id /statuses/3/id /statuses/4/id /statuses/5/id
```
In C++, given a `ParsedJson`, we can move to a node with the `move_to` method, passing a `std::string` representing the JSON Pointer query.
## Navigating the parsed document
Here is a code sample to dump back the parsed JSON to a string:
```c
ParsedJson::Iterator pjh(pj);
if (!pjh.is_ok()) {
std::cerr << " Could not iterate parsed result. " << std::endl;
return EXIT_FAILURE;
}
compute_dump(pj);
//
// where compute_dump is :
void compute_dump(ParsedJson::Iterator &pjh) {
if (pjh.is_object()) {
std::cout << "{";
if (pjh.down()) {
pjh.print(std::cout); // must be a string
std::cout << ":";
pjh.next();
compute_dump(pjh); // let us recurse
while (pjh.next()) {
std::cout << ",";
pjh.print(std::cout);
std::cout << ":";
pjh.next();
compute_dump(pjh); // let us recurse
}
pjh.up();
}
std::cout << "}";
} else if (pjh.is_array()) {
std::cout << "[";
if (pjh.down()) {
compute_dump(pjh); // let us recurse
while (pjh.next()) {
std::cout << ",";
compute_dump(pjh); // let us recurse
}
pjh.up();
}
std::cout << "]";
} else {
pjh.print(std::cout); // just print the lone value
}
}
```
The following function will find all user.id integers:
```C
void simdjson_scan(std::vector<int64_t> &answer, ParsedJson::Iterator &i) {
while(i.move_forward()) {
if(i.get_scope_type() == '{') {
bool found_user = (i.get_string_length() == 4) && (memcmp(i.get_string(), "user", 4) == 0);
i.move_to_value();
if(found_user) {
if(i.is_object() && i.move_to_key("id",2)) {
if (i.is_integer()) {
answer.push_back(i.get_integer());
}
i.up();
}
}
}
}
}
```
## In-depth comparisons
If you want to see how a wide range of parsers validate a given JSON file:
```
make allparserscheckfile
./allparserscheckfile myfile.json
```
For performance comparisons:
```
make parsingcompetition
./parsingcompetition myfile.json
```
For broader comparisons:
```
make allparsingcompetition
./allparsingcompetition myfile.json
```
Both the `parsingcompetition` and `allparsingcompetition` tools take a `-t` flag which produces
a table-oriented output that can be conventiently parsed by other tools.
## Docker
One can run tests and benchmarks using docker. It especially makes sense under Linux. A privileged access may be needed to get performance counters.
```
git clone https://github.com/lemire/simdjson.git
cd simdjson
docker build -t simdjson .
docker run --privileged -t simdjson
```
## Other programming languages
Bindings and Ports of simdjson
------------------------------
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
- [libpy_simdjson](https://github.com/gerrymanoim/libpy_simdjson/): high-speed Python bindings for simdjson using [libpy](https://github.com/quantopian/libpy).
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
- [simdjson-rs](https://github.com/Licenser/simdjson-rs): Rust port
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings)
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port)
- [simdjson-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).
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
## Various References
- [Google double-conv](https://github.com/google/double-conversion/)
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
- [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php)
- https://tools.ietf.org/html/rfc7159
- The Mison implementation in rust https://github.com/pikkr/pikkr
- http://rapidjson.org/md_doc_sax.html
- https://github.com/Geal/parser_benchmarks/tree/master/json
- Gron: A command line tool that makes JSON greppable https://news.ycombinator.com/item?id=16727665
- GoogleGson https://github.com/google/gson
- Jackson https://github.com/FasterXML/jackson
- https://www.yelp.com/dataset_challenge
- RapidJSON. http://rapidjson.org/
Inspiring links:
- https://auth0.com/blog/beating-json-performance-with-protobuf/
- https://gist.github.com/shijuvar/25ad7de9505232c87034b8359543404a
- https://github.com/frankmcsherry/blog/blob/master/posts/2018-02-11.md
Validating UTF-8 takes no more than 0.7 cycles per byte:
- https://github.com/lemire/fastvalidate-utf-8 https://lemire.me/blog/2018/05/16/validating-utf-8-strings-using-as-little-as-0-7-cycles-per-byte/
## Remarks on JSON parsing
- The JSON spec defines what a JSON parser is:
> A JSON parser transforms a JSON text into another representation. A JSON parser MUST accept all texts that conform to the JSON grammar. A JSON parser MAY accept non-JSON forms or extensions. An implementation may set limits on the size of texts that it accepts. An implementation may set limits on the maximum depth of nesting. An implementation may set limits on the range and precision of numbers. An implementation may set limits on the length and character contents of strings.
* JSON is not JavaScript:
> All JSON is Javascript but NOT all Javascript is JSON. So {property:1} is invalid because property does not have double quotes around it. {'property':1} is also invalid, because it's single quoted while the only thing that can placate the JSON specification is double quoting. JSON is even fussy enough that {"property":.1} is invalid too, because you should have of course written {"property":0.1}. Also, don't even think about having comments or semicolons, you guessed it: they're invalid. (credit:https://github.com/elzr/vim-json)
* The structural characters are:
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
- [fast_jsonparser](https://github.com/anilmaurya/fast_jsonparser): Ruby bindings for the simdjson project.
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
- [simdjson_erlang](https://github.com/ChomperT/simdjson_erlang): erlang bindings.
begin-array = [ left square bracket
begin-object = { left curly bracket
end-array = ] right square bracket
end-object = } right curly bracket
name-separator = : colon
value-separator = , comma
About simdjson
--------------
### Pseudo-structural elements
The simdjson library takes advantage of modern microarchitectures, parallelizing with SIMD vector
instructions, reducing branch misprediction, and reducing data dependency to take advantage of each
CPU's multiple execution cores.
A character is pseudo-structural if and only if:
Some people [enjoy reading our paper](https://arxiv.org/abs/1902.08318): A description of the design
and implementation of simdjson is in our research article:
- Geoff Langdale, Daniel Lemire, [Parsing Gigabytes of JSON per Second](https://arxiv.org/abs/1902.08318), VLDB Journal 28 (6), 2019.
1. Not enclosed in quotes, AND
2. Is a non-whitespace character, AND
3. Its preceding character is either:
(a) a structural character, OR
(b) whitespace.
We have an in-depth paper focused on the UTF-8 validation:
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
- John Keiser, Daniel Lemire, [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090), Software: Practice & Experience (to appear)
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
## Academic References
For the video inclined, <br />
[![simdjson at QCon San Francisco 2019](http://img.youtube.com/vi/wlvKAT7SZIQ/0.jpg)](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
(it was the best voted talk, we're kinda proud of it).
- T.Mühlbauer, W.Rödiger, R.Seilbeck, A.Reiser, A.Kemper, and T.Neumann. Instant loading for main memory databases. PVLDB, 6(14):17021713, 2013. (SIMD-based CSV parsing)
- Mytkowicz, Todd, Madanlal Musuvathi, and Wolfram Schulte. "Data-parallel finite-state machines." ACM SIGARCH Computer Architecture News. Vol. 42. No. 1. ACM, 2014.
- Lu, Yifan, et al. "Tree structured data processing on GPUs." Cloud Computing, Data Science & Engineering-Confluence, 2017 7th International Conference on. IEEE, 2017.
- Sidhu, Reetinder. "High throughput, tree automata based XML processing using FPGAs." Field-Programmable Technology (FPT), 2013 International Conference on. IEEE, 2013.
- Dai, Zefu, Nick Ni, and Jianwen Zhu. "A 1 cycle-per-byte XML parsing accelerator." Proceedings of the 18th annual ACM/SIGDA international symposium on Field programmable gate arrays. ACM, 2010.
- Lin, Dan, et al. "Parabix: Boosting the efficiency of text processing on commodity processors." High Performance Computer Architecture (HPCA), 2012 IEEE 18th International Symposium on. IEEE, 2012. http://parabix.costar.sfu.ca/export/1783/docs/HPCA2012/final_ieee/final.pdf
- Deshmukh, V. M., and G. R. Bamnote. "An empirical evaluation of optimization parameters in XML parsing for performance enhancement." Computer, Communication and Control (IC4), 2015 International Conference on. IEEE, 2015.
- Moussalli, Roger, et al. "Efficient XML Path Filtering Using GPUs." ADMS@ VLDB. 2011.
- Jianliang, Ma, et al. "Parallel speculative dom-based XML parser." High Performance Computing and Communication & 2012 IEEE 9th International Conference on Embedded Software and Systems (HPCC-ICESS), 2012 IEEE 14th International Conference on. IEEE, 2012.
- Li, Y., Katsipoulakis, N.R., Chandramouli, B., Goldstein, J. and Kossmann, D., 2017. Mison: a fast JSON parser for data analytics. Proceedings of the VLDB Endowment, 10(10), pp.1118-1129. http://www.vldb.org/pvldb/vol10/p1118-li.pdf
- Cameron, Robert D., et al. "Parallel scanning with bitstream addition: An xml case study." European Conference on Parallel Processing. Springer, Berlin, Heidelberg, 2011.
- Cameron, Robert D., Kenneth S. Herdy, and Dan Lin. "High performance XML parsing using parallel bit stream technology." Proceedings of the 2008 conference of the center for advanced studies on collaborative research: meeting of minds. ACM, 2008.
- Shah, Bhavik, et al. "A data parallel algorithm for XML DOM parsing." International XML Database Symposium. Springer, Berlin, Heidelberg, 2009.
- Cameron, Robert D., and Dan Lin. "Architectural support for SWAR text processing with parallel bit streams: the inductive doubling principle." ACM Sigplan Notices. Vol. 44. No. 3. ACM, 2009.
- Amagasa, Toshiyuki, Mana Seino, and Hiroyuki Kitagawa. "Energy-Efficient XML Stream Processing through Element-Skipping Parsing." Database and Expert Systems Applications (DEXA), 2013 24th International Workshop on. IEEE, 2013.
- Medforth, Nigel Woodland. "icXML: Accelerating Xerces-C 3.1. 1 using the Parabix Framework." (2013).
- Zhang, Qiang Scott. Embedding Parallel Bit Stream Technology Into Expat. Diss. Simon Fraser University, 2010.
- Cameron, Robert D., et al. "Fast Regular Expression Matching with Bit-parallel Data Streams."
- Lin, Dan. Bits filter: a high-performance multiple string pattern matching algorithm for malware detection. Diss. School of Computing Science-Simon Fraser University, 2010.
- Yang, Shiyang. Validation of XML Document Based on Parallel Bit Stream Technology. Diss. Applied Sciences: School of Computing Science, 2013.
- N. Nakasato, "Implementation of a parallel tree method on a GPU", Journal of Computational Science, vol. 3, no. 3, pp. 132-141, 2012.
## Funding
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant number RGPIN-2017-03910.
Funding
-------
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant
number RGPIN-2017-03910.
[license]: LICENSE
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
Contributing to simdjson
------------------------
Head over to [CONTRIBUTING.md](CONTRIBUTING.md) for information on contributing to simdjson, and
[HACKING.md](HACKING.md) for information on source, building, and architecture/design.
License
-------
This code is made available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it under the liberal (business-friendly) MIT license.
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the Boost license (http://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
+69
View File
@@ -0,0 +1,69 @@
# 0.5
## Highlights
Performance
* Faster and simpler UTF-8 validation with the lookup4 algorithm https://github.com/simdjson/simdjson/pull/993
* We improved the performance of simdjson under Visual Studio by about 25%. Users will still get better performance with clang-cl (+30%) but the gap has been reduced. https://github.com/simdjson/simdjson/pull/1031
Code usability
* In `parse_many`, when parsing streams of JSON documetns, we give to the users runtime control as to whether threads are used (via the parser.threaded attribute). https://github.com/simdjson/simdjson/issues/925
* Prefixed public macros to avoid name clashes with other libraries. https://github.com/simdjson/simdjson/issues/1035
* Better documentation regarding package managers (brew, MSYS2, conan, apt, vcpkg, FreeBSD package manager, etc.).
* Better documentation regarding CMake usage.
Standards
* We improved standard compliance with respect to both the JSON RFC 8259 and JSON Pointer RFC 6901. We added the at_pointer method to nodes for standard-compliant JSON Pointer queries. The legacy `at(std::string_view)` method remains but is deprecated since it is not standard-compliant as per RFC 6901.
* We removed computed GOTOs without sacrificing performance thus improving the C++ standard compliance (since computed GOTOs are compiler-specific extensions).
* Better support for C++20 https://github.com/simdjson/simdjson/pull/1050
# 0.4
## Highlights
- Test coverage has been greatly improved and we have resolved many static-analysis warnings on different systems.
- We added a fast (8GB/s) minifier that works directly on JSON strings.
- We added fast (10GB/s) UTF-8 validator that works directly on strings (any strings, including non-JSON).
- The array and object elements have a constant-time size() method.
- Performance improvements to the API (type(), get<>()).
- The parse_many function (ndjson) has been entirely reworked. It now uses a single secondary thread instead of several new threads.
- We have introduced a faster UTF-8 validation algorithm (lookup3) for all kernels (ARM, x64 SSE, x64 AVX).
- C++11 support for older compilers and systems.
- FreeBSD support (and tests).
- We support the clang front-end compiler (clangcl) under Visual Studio.
- It is now possible to target ARM platforms under Visual Studio.
- The simdjson library will never abort or print to standard output/error.
# 0.3
## Highlights
- **Multi-Document Parsing:** Read a bundle of JSON documents (ndjson) 2-4x faster than doing it
individually. [API docs](https://github.com/simdjson/simdjson/blob/master/doc/basics.md#newline-delimited-json-ndjson-and-json-lines) / [Design Details](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md)
- **Simplified API:** The API has been completely revamped for ease of use, including a new JSON
navigation API and fluent support for error code *and* exception styles of error handling with a
single API. [Docs](https://github.com/simdjson/simdjson/blob/master/doc/basics.md#the-basics-loading-and-parsing-json-documents)
- **Exact Float Parsing:** Now simdjson parses floats flawlessly *without* any performance loss,
thanks to [great work by @michaeleisel and @lemire](https://github.com/simdjson/simdjson/pull/558).
[Blog Post](https://lemire.me/blog/2020/03/10/fast-float-parsing-in-practice/)
- **Even Faster:** The fastest parser got faster! With a [shiny new UTF-8 validator](https://github.com/simdjson/simdjson/pull/387)
and meticulously refactored SIMD core, simdjson 0.3 is 15% faster than before, running at 2.5 GB/s
(where 0.2 ran at 2.2 GB/s).
## Minor Highlights
- Fallback implementation: simdjson now has a non-SIMD fallback implementation, and can run even on
very old 64-bit machines.
- Automatic allocation: as part of API simplification, the parser no longer has to be preallocated--
it will adjust automatically when it encounters larger files.
- Runtime selection API: We've exposed simdjson's runtime CPU detection and implementation selection
as an API, so you can tell what implementation we detected and test with other implementations.
- Error handling your way: Whether you use exceptions or check error codes, simdjson lets you handle
errors in your style. APIs that can fail return simdjson_result<T>, letting you check the error
code before using the result. But if you are more comfortable with exceptions, skip the error code
and cast straight to T, and exceptions will be thrown automatically if an error happens. Use the
same API either way!
- Error chaining: We also worked to keep non-exception error-handling short and sweet. Instead of
having to check the error code after every single operation, now you can *chain* JSON navigation
calls like looking up an object field or array element, or casting to a string, so that you only
have to check the error code once at the very end.
-158
View File
@@ -1,158 +0,0 @@
#!/bin/bash
########################################################################
# Generates an "amalgamation build" for roaring. Inspired by similar
# script used by whefs.
########################################################################
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
echo "We are about to amalgamate all simdjson files into one source file. "
echo "See https://www.sqlite.org/amalgamation.html and https://en.wikipedia.org/wiki/Single_Compilation_Unit for rationale. "
AMAL_H="simdjson.h"
AMAL_C="simdjson.cpp"
# order does not matter
ALLCFILES="
$SCRIPTPATH/src/simdjson.cpp
$SCRIPTPATH/src/jsonioutil.cpp
$SCRIPTPATH/src/jsonminifier.cpp
$SCRIPTPATH/src/jsonparser.cpp
$SCRIPTPATH/src/stage1_find_marks.cpp
$SCRIPTPATH/src/stage2_build_tape.cpp
$SCRIPTPATH/src/parsedjson.cpp
$SCRIPTPATH/src/parsedjsoniterator.cpp
"
# order matters
ALLCHEADERS="
$SCRIPTPATH/include/simdjson/simdjson_version.h
$SCRIPTPATH/include/simdjson/portability.h
$SCRIPTPATH/include/simdjson/isadetection.h
$SCRIPTPATH/include/simdjson/simdjson.h
$SCRIPTPATH/include/simdjson/common_defs.h
$SCRIPTPATH/include/simdjson/padded_string.h
$SCRIPTPATH/include/simdjson/jsoncharutils.h
$SCRIPTPATH/include/simdjson/jsonformatutils.h
$SCRIPTPATH/include/simdjson/jsonioutil.h
$SCRIPTPATH/include/simdjson/simdprune_tables.h
$SCRIPTPATH/include/simdjson/simdutf8check_haswell.h
$SCRIPTPATH/include/simdjson/simdutf8check_westmere.h
$SCRIPTPATH/include/simdjson/simdutf8check_arm64.h
$SCRIPTPATH/include/simdjson/jsonminifier.h
$SCRIPTPATH/include/simdjson/parsedjson.h
$SCRIPTPATH/include/simdjson/stage1_find_marks.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_flatten.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_flatten_haswell.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_macros.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_westmere.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_haswell.h
$SCRIPTPATH/include/simdjson/stage1_find_marks_arm64.h
$SCRIPTPATH/include/simdjson/stringparsing.h
$SCRIPTPATH/include/simdjson/stringparsing_macros.h
$SCRIPTPATH/include/simdjson/stringparsing_westmere.h
$SCRIPTPATH/include/simdjson/stringparsing_haswell.h
$SCRIPTPATH/include/simdjson/stringparsing_arm64.h
$SCRIPTPATH/include/simdjson/numberparsing.h
$SCRIPTPATH/include/simdjson/stage2_build_tape.h
$SCRIPTPATH/include/simdjson/jsonparser.h
"
for i in ${ALLCHEADERS} ${ALLCFILES}; do
test -e $i && continue
echo "FATAL: source file [$i] not found."
exit 127
done
function stripinc()
{
sed -e '/# *include *"/d' -e '/# *include *<simdjson\//d'
}
function dofile()
{
RELFILE=${1#"$SCRIPTPATH/"}
echo "/* begin file $RELFILE */"
# echo "#line 8 \"$1\"" ## redefining the line/file is not nearly as useful as it sounds for debugging. It breaks IDEs.
stripinc < $1
echo "/* end file $RELFILE */"
}
timestamp=$(date)
echo "Creating ${AMAL_H}..."
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${AMAL_H}"
{
for h in ${ALLCHEADERS}; do
dofile $h
done
} >> "${AMAL_H}"
echo "Creating ${AMAL_C}..."
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${AMAL_C}"
{
echo "#include \"${AMAL_H}\""
echo ""
echo "/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */"
echo "#ifdef DMALLOC"
echo "#include \"dmalloc.h\""
echo "#endif"
echo ""
for h in ${ALLCFILES}; do
dofile $h
done
} >> "${AMAL_C}"
DEMOCPP="amalgamation_demo.cpp"
echo "Creating ${DEMOCPP}..."
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${DEMOCPP}"
cat <<< '
#include <iostream>
#include "simdjson.h"
#include "simdjson.cpp"
int main(int argc, char *argv[]) {
if(argc < 2) {
std::cerr << "Please specify a filename " << std::endl;
}
const char * filename = argv[1];
simdjson::padded_string p = simdjson::get_corpus(filename);
simdjson::ParsedJson pj = simdjson::build_parsed_json(p); // do the parsing
if( ! pj.is_valid() ) {
std::cout << "not valid" << std::endl;
} else {
std::cout << "valid" << std::endl;
}
return EXIT_SUCCESS;
}
' >> "${DEMOCPP}"
echo "Done with all files generation. "
echo "Files have been written to directory: $PWD "
ls -la ${AMAL_C} ${AMAL_H} ${DEMOCPP}
echo "Giving final instructions:"
CPPBIN=${DEMOCPP%%.*}
echo "Try :"
echo "c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json "
SINGLEHDR=$SCRIPTPATH/singleheader
echo "Copying files to $SCRIPTPATH/singleheader "
mkdir -p $SINGLEHDR
echo "c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json " > $SINGLEHDR/README.md
cp ${AMAL_C} ${AMAL_H} ${DEMOCPP} $SINGLEHDR
ls $SINGLEHDR
cd $SINGLEHDR && c++ -O3 -std=c++17 -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json
lowercase(){
echo "$1" | tr 'A-Z' 'a-z'
}
OS=`lowercase \`uname\``
+49 -7
View File
@@ -1,8 +1,50 @@
target_include_directories(${SIMDJSON_LIB_NAME}
INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmark>
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmark/linux>
)
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
add_cpp_benchmark(parse)
add_cpp_benchmark(statisticalmodel)
if (TARGET benchmark::benchmark)
add_executable(bench_sax bench_sax.cpp)
target_link_libraries(bench_sax 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)
add_executable(parse parse.cpp)
add_executable(parse_stream parse_stream.cpp)
add_executable(statisticalmodel statisticalmodel.cpp)
add_executable(parse_noutf8validation parse.cpp)
target_compile_definitions(parse_noutf8validation PRIVATE SIMDJSON_SKIPUTF8VALIDATION)
add_executable(parse_nonumberparsing parse.cpp)
target_compile_definitions(parse_nonumberparsing PRIVATE SIMDJSON_SKIPNUMBERPARSING)
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 competition-core)
add_executable(minifiercompetition minifiercompetition.cpp)
target_link_libraries(minifiercompetition competition-core)
add_executable(parseandstatcompetition parseandstatcompetition.cpp)
target_link_libraries(parseandstatcompetition competition-core)
add_executable(parsingcompetition parsingcompetition.cpp)
target_link_libraries(parsingcompetition competition-core)
add_executable(allparsingcompetition parsingcompetition.cpp)
target_link_libraries(allparsingcompetition competition-all)
target_compile_definitions(allparsingcompetition PRIVATE ALLPARSER)
endif()
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)
endif()
include(checkperf.cmake)
+19
View File
@@ -0,0 +1,19 @@
# From the ROOT, run:
# docker build -t simdjsonbench -f benchmark/Dockerfile . && docker run --privileged -t simdjsonbench
FROM gcc:8.3
# # Build latest
# ENV latest_release=v0.2.1
# WORKDIR /usr/src/$latest_release/
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ -b $latest_release .
# RUN make parse
# # Build master
# WORKDIR /usr/src/master/
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ .
# RUN make parse
# Build the current source
COPY . /usr/src/current/
WORKDIR /usr/src/current/
RUN make checkperf
+732
View File
@@ -0,0 +1,732 @@
#include <benchmark/benchmark.h>
#include "simdjson.h"
#include <sstream>
using namespace simdjson;
using namespace benchmark;
using namespace std;
const padded_string EMPTY_ARRAY("[]", 2);
const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
static void recover_one_string(State& state) {
dom::parser parser;
const std::string_view data = "\"one string\"";
padded_string docdata{data};
// we do not want mem. alloc. in the loop.
auto error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse string" << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::string_view v;
error = doc.get(v);
if (error) {
cerr << "could not get string" << error << endl;
return;
}
benchmark::DoNotOptimize(v);
}
}
BENCHMARK(recover_one_string);
static void serialize_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
std::string serial = simdjson::minify(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// we validate the result
{
auto serial = simdjson::minify(doc);
dom::element doc2; // we parse the minified output
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
auto serial2 = simdjson::minify(doc2); // we minify a second time
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_big_string_to_string(State& state) {
dom::parser parser;
std::vector<char> content;
content.push_back('\"');
for(size_t i = 0 ; i < 100000; i ++) {
content.push_back('0' + char(i%10)); // we add what looks like a long list of digits
}
content.push_back('\"');
dom::element doc;
simdjson::error_code error;
if ((error = parser.parse(content.data(), content.size()).get(doc))) {
cerr << "could not parse big string" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
auto serial = simdjson::to_string(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_big_string_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_twitter_to_string(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
auto serial = simdjson::to_string(doc);
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// we validate the result
{
auto serial = simdjson::to_string(doc);
dom::element doc2; // we parse the stringify output
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
auto serial2 = simdjson::to_string(doc2); // we stringify again
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void serialize_twitter_string_builder(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
if((error = parser.allocate(docdata.size()))) {
cout << error << endl;
return;
}
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
size_t bytes = 0;
simdjson::internal::string_builder<> sb;// not part of our public API, for internal use
for (simdjson_unused auto _ : state) {
sb.clear();
sb.append(doc);
std::string_view serial = sb.str();
bytes += serial.size();
benchmark::DoNotOptimize(serial);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(serialize_twitter_string_builder)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void numbers_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
double x;
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_scan);
static void numbers_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (auto e : arr) {
double x;
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
container[pos++] = x;
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_size_scan);
static void numbers_type_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
dom::element_type actual_type = e.type();
if(actual_type != dom::element_type::DOUBLE) {
cerr << "found a node that is not an number?" << endl; break;
}
double x;
error = e.get(x);
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_type_scan);
static void numbers_type_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
return;
}
for (simdjson_unused auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (auto e : arr) {
dom::element_type actual_type = e.type();
if(actual_type != dom::element_type::DOUBLE) {
cerr << "found a node that is not an number?" << endl; break;
}
double x;
error = e.get(x);
container[pos++] = x;
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_type_size_scan);
static void numbers_load_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
// this may hit the disk, but probably just once
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
break;
}
std::vector<double> container;
for (auto e : arr) {
double x;
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_load_scan);
static void numbers_load_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr;
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
// this may hit the disk, but probably just once
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
break;
}
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (auto e : arr) {
double x;
if ((error = e.get(x))) { cerr << "found a node that is not an number?" << endl; break;}
container[pos++] = x;
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_load_size_scan);
#if SIMDJSON_EXCEPTIONS
static void numbers_exceptions_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
std::vector<double> container;
for (double x : arr) {
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_exceptions_scan);
static void numbers_exceptions_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (auto e : arr) {
container[pos++] = double(e);
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_exceptions_size_scan);
static void numbers_type_exceptions_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
std::vector<double> container;
for (auto e : arr) {
dom::element_type actual_type = e.type();
if(actual_type != dom::element_type::DOUBLE) {
cerr << "found a node that is not an number?" << endl; break;
}
container.push_back(double(e));
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_type_exceptions_scan);
static void numbers_type_exceptions_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::array arr = parser.load(NUMBERS_JSON);
for (simdjson_unused auto _ : state) {
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (auto e : arr) {
dom::element_type actual_type = e.type();
if(actual_type != dom::element_type::DOUBLE) {
cerr << "found a node that is not an number?" << endl; break;
}
container[pos++] = double(e);
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_type_exceptions_size_scan);
static void numbers_exceptions_load_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
for (simdjson_unused auto _ : state) {
// this may hit the disk, but probably just once
dom::array arr = parser.load(NUMBERS_JSON);
std::vector<double> container;
for (double x : arr) {
container.push_back(x);
}
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_exceptions_load_scan);
static void numbers_exceptions_load_size_scan(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
for (simdjson_unused auto _ : state) {
// this may hit the disk, but probably just once
dom::array arr = parser.load(NUMBERS_JSON);
std::vector<double> container;
container.resize(arr.size());
size_t pos = 0;
for (double x : arr) {
container[pos++] = x;
}
if(pos != container.size()) { cerr << "bad count" << endl; }
benchmark::DoNotOptimize(container.data());
benchmark::ClobberMemory();
}
}
BENCHMARK(numbers_exceptions_load_size_scan);
static void twitter_count(State& state) {
// Prints the number of results in twitter.json
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
for (simdjson_unused auto _ : state) {
uint64_t result_count = doc["search_metadata"]["count"];
if (result_count != 100) { return; }
}
}
BENCHMARK(twitter_count);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_count(State& state) {
// Prints the number of results in twitter.json
padded_string json = padded_string::load(TWITTER_JSON);
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
ParsedJson::Iterator iter(pj);
// uint64_t result_count = doc["search_metadata"]["count"];
if (!iter.move_to_key("search_metadata")) { return; }
if (!iter.move_to_key("count")) { return; }
if (!iter.is_integer()) { return; }
int64_t result_count = iter.get_integer();
if (result_count != 100) { return; }
}
}
BENCHMARK(iterator_twitter_count);
SIMDJSON_POP_DISABLE_WARNINGS
static void twitter_default_profile(State& state) {
// Count unique users with a default profile.
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
for (simdjson_unused auto _ : state) {
set<string_view> default_users;
for (dom::object tweet : doc["statuses"]) {
dom::object user = tweet["user"];
if (user["default_profile"]) {
default_users.insert(user["screen_name"]);
}
}
if (default_users.size() != 86) { return; }
}
}
BENCHMARK(twitter_default_profile);
static void twitter_image_sizes(State& state) {
// Count unique image sizes
dom::parser parser;
dom::element doc = parser.load(TWITTER_JSON);
simdjson::error_code error;
for (simdjson_unused auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
for (dom::object tweet : doc["statuses"]) {
dom::array media;
if (not (error = tweet["entities"]["media"].get(media))) {
for (dom::object image : media) {
for (auto size : image["sizes"].get<dom::object>()) {
image_sizes.insert({ size.value["w"], size.value["h"] });
}
}
}
}
if (image_sizes.size() != 15) { return; };
}
}
BENCHMARK(twitter_image_sizes);
#endif // SIMDJSON_EXCEPTIONS
static void error_code_twitter_count(State& state) noexcept {
// Prints the number of results in twitter.json
dom::parser parser;
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { return; }
for (simdjson_unused auto _ : state) {
uint64_t value;
if ((error = doc["search_metadata"]["count"].get(value))) { return; }
if (value != 100) { return; }
}
}
BENCHMARK(error_code_twitter_count);
static void error_code_twitter_default_profile(State& state) noexcept {
// Count unique users with a default profile.
dom::parser parser;
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
for (simdjson_unused auto _ : state) {
set<string_view> default_users;
dom::array tweets;
if ((error = doc["statuses"].get(tweets))) { return; }
for (dom::element tweet : tweets) {
dom::object user;
if ((error = tweet["user"].get(user))) { return; }
bool default_profile;
if ((error = user["default_profile"].get(default_profile))) { return; }
if (default_profile) {
std::string_view screen_name;
if ((error = user["screen_name"].get(screen_name))) { return; }
default_users.insert(screen_name);
}
}
if (default_users.size() != 86) { return; }
}
}
BENCHMARK(error_code_twitter_default_profile);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_default_profile(State& state) {
// Count unique users with a default profile.
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
set<string_view> default_users;
ParsedJson::Iterator iter(pj);
// for (dom::object tweet : doc["statuses"]) {
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
if (iter.down()) { // first status
do {
// dom::object user = tweet["user"];
if (!(iter.move_to_key("user") && iter.is_object())) { return; }
// if (user["default_profile"]) {
if (iter.move_to_key("default_profile")) {
if (iter.is_true()) {
if (!iter.up()) { return; } // back to user
// 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()));
}
if (!iter.up()) { return; } // back to user
}
if (!iter.up()) { return; } // back to status
} while (iter.next()); // next status
}
if (default_users.size() != 86) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(iterator_twitter_default_profile);
static void error_code_twitter_image_sizes(State& state) noexcept {
// Count unique image sizes
dom::parser parser;
simdjson::error_code error;
dom::element doc;
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
for (simdjson_unused auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
dom::array statuses;
if ((error = doc["statuses"].get(statuses))) { return; }
for (dom::element tweet : statuses) {
dom::array images;
if (not (error = tweet["entities"]["media"].get(images))) {
for (dom::element image : images) {
dom::object sizes;
if ((error = image["sizes"].get(sizes))) { return; }
for (auto size : sizes) {
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 });
}
}
}
}
if (image_sizes.size() != 15) { return; };
}
}
BENCHMARK(error_code_twitter_image_sizes);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void iterator_twitter_image_sizes(State& state) {
// Count unique image sizes
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
ParsedJson pj = build_parsed_json(json);
for (simdjson_unused auto _ : state) {
set<tuple<uint64_t, uint64_t>> image_sizes;
ParsedJson::Iterator iter(pj);
// for (dom::object tweet : doc["statuses"]) {
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
if (iter.down()) { // first status
do {
// dom::object media;
// not_found = tweet["entities"]["media"].get(media);
// if (!not_found) {
if (iter.move_to_key("entities")) {
if (!iter.is_object()) { return; }
if (iter.move_to_key("media")) {
if (!iter.is_array()) { return; }
// for (dom::object image : media) {
if (iter.down()) { // first media
do {
// for (auto [key, size] : dom::object(image["sizes"])) {
if (!(iter.move_to_key("sizes") && iter.is_object())) { return; }
if (iter.down()) { // first size
do {
iter.move_to_value();
// image_sizes.insert({ size["w"], size["h"] });
if (!(iter.move_to_key("w")) && !iter.is_integer()) { return; }
uint64_t width = iter.get_integer();
if (!iter.up()) { return; } // back to size
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 });
} while (iter.next()); // next size
if (!iter.up()) { return; } // back to sizes
}
if (!iter.up()) { return; } // back to image
} while (iter.next()); // next image
if (!iter.up()) { return; } // back to media
}
if (!iter.up()) { return; } // back to entities
}
if (!iter.up()) { return; } // back to status
}
} while (iter.next()); // next status
}
if (image_sizes.size() != 15) { return; };
}
}
BENCHMARK(iterator_twitter_image_sizes);
static void print_json(State& state) noexcept {
// Prints the number of results in twitter.json
dom::parser parser;
padded_string json;
auto error = padded_string::load(TWITTER_JSON).get(json);
if (error) { std::cerr << error << std::endl; return; }
int code = json_parse(json, parser);
if (code) { cerr << error_message(code) << endl; return; }
for (simdjson_unused auto _ : state) {
std::stringstream s;
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
}
}
BENCHMARK(print_json);
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK_MAIN();
+26
View File
@@ -0,0 +1,26 @@
#include "simdjson.h"
#include <iostream>
#include <sstream>
#include <random>
#include <vector>
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#include <benchmark/benchmark.h>
SIMDJSON_POP_DISABLE_WARNINGS
#include "partial_tweets/ondemand.h"
#include "partial_tweets/iter.h"
#include "partial_tweets/dom.h"
#include "largerandom/ondemand.h"
#include "largerandom/iter.h"
#include "largerandom/dom.h"
#include "kostya/ondemand.h"
#include "kostya/iter.h"
#include "kostya/dom.h"
#include "distinctuserid/ondemand.h"
#include "distinctuserid/dom.h"
BENCHMARK_MAIN();
+191
View File
@@ -0,0 +1,191 @@
#include <benchmark/benchmark.h>
#include "simdjson.h"
using namespace simdjson;
using namespace benchmark;
using namespace std;
const padded_string EMPTY_ARRAY("[]", 2);
const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
const char *GSOC_JSON = SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json";
static void unicode_validate_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
bool is_ok = simdjson::validate_utf8(docdata.data(), docdata.size());
bytes += docdata.size();
benchmark::DoNotOptimize(is_ok);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(unicode_validate_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void parse_twitter(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(TWITTER_JSON).get(docdata);
if(error) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
dom::element doc;
bytes += docdata.size();
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse twitter.json" << error << endl;
return;
}
benchmark::DoNotOptimize(doc);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(parse_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
static void parse_gsoc(State& state) {
dom::parser parser;
padded_string docdata;
auto error = padded_string::load(GSOC_JSON).get(docdata);
if(error) {
cerr << "could not parse gsoc-2018.json" << error << endl;
return;
}
// we do not want mem. alloc. in the loop.
error = parser.allocate(docdata.size());
if(error) {
cout << error << endl;
return;
}
size_t bytes = 0;
for (simdjson_unused auto _ : state) {
bytes += docdata.size();
dom::element doc;
if ((error = parser.parse(docdata).get(doc))) {
cerr << "could not parse gsoc-2018.json" << error << endl;
return;
}
benchmark::DoNotOptimize(doc);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
state.counters["Gigabytes"] = benchmark::Counter(
double(bytes), benchmark::Counter::kIsRate,
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
}
BENCHMARK(parse_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
return *(std::max_element(std::begin(v), std::end(v)));
})->DisplayAggregatesOnly(true);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void json_parse(State& state) {
ParsedJson pj;
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
auto error = json_parse(EMPTY_ARRAY, pj);
if (error) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(json_parse);
static void parser_parse_error_code(State& state) {
dom::parser parser;
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
auto error = parser.parse(EMPTY_ARRAY).error();
if (error) { return; }
}
}
BENCHMARK(parser_parse_error_code);
#if SIMDJSON_EXCEPTIONS
static void parser_parse_exception(State& state) {
dom::parser parser;
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
for (simdjson_unused auto _ : state) {
try {
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
} catch(simdjson_error &j) {
cout << j.what() << endl;
return;
}
}
}
BENCHMARK(parser_parse_exception);
#endif // SIMDJSON_EXCEPTIONS
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
static void build_parsed_json(State& state) {
for (simdjson_unused auto _ : state) {
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
if (!parser.valid) { return; }
}
}
SIMDJSON_POP_DISABLE_WARNINGS
BENCHMARK(build_parsed_json);
static void document_parse_error_code(State& state) {
for (simdjson_unused auto _ : state) {
dom::parser parser;
auto error = parser.parse(EMPTY_ARRAY).error();
if (error) { return; }
}
}
BENCHMARK(document_parse_error_code);
#if SIMDJSON_EXCEPTIONS
static void document_parse_exception(State& state) {
for (simdjson_unused auto _ : state) {
try {
dom::parser parser;
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
} catch(simdjson_error &j) {
cout << j.what() << endl;
return;
}
}
}
BENCHMARK(document_parse_exception);
#endif // SIMDJSON_EXCEPTIONS
BENCHMARK_MAIN();
+14
View File
@@ -0,0 +1,14 @@
#include "simdjson.h"
#include "simdjson.cpp"
#include <iostream>
#include <sstream>
#include <random>
#include <vector>
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
#include <benchmark/benchmark.h>
SIMDJSON_POP_DISABLE_WARNINGS
#include "partial_tweets/sax.h"
#include "largerandom/sax.h"
BENCHMARK_MAIN();
+472
View File
@@ -0,0 +1,472 @@
#include "event_counter.h"
#include <cassert>
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <unistd.h>
#include <cinttypes>
#include <initializer_list>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#include "simdjson.h"
#include <functional>
#include "benchmarker.h"
using namespace simdjson;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::to_string;
using std::vector;
using std::ostream;
using std::ofstream;
using std::exception;
// Stash the exe_name in main() for functions to use
char* exe_name;
void print_usage(ostream& out) {
out << "Usage: " << exe_name << " [-v] [-n #] [-s STAGE] [-a ARCH]" << endl;
out << endl;
out << "Runs the parser against jsonexamples/generated json files in a loop, measuring speed and other statistics." << endl;
out << endl;
out << "Options:" << endl;
out << endl;
out << "-n # - Number of iterations per file. Default: 400" << endl;
out << "-i # - Number of times to iterate a single file before moving to the next. Default: 20" << endl;
out << "-v - Verbose output." << endl;
out << "-s STAGE - Stop after the given stage." << endl;
out << " -s stage1 - Stop after find_structural_bits." << endl;
out << " -s all - Run all stages." << endl;
out << "-a ARCH - Use the parser with the designated architecture (HASWELL, WESTMERE" << endl;
out << " or ARM64). By default, detects best supported architecture." << endl;
}
void exit_usage(string message) {
cerr << message << endl;
cerr << endl;
print_usage(cerr);
exit(EXIT_FAILURE);
}
struct option_struct {
bool stage1_only = false;
int32_t iterations = 400;
int32_t iteration_step = 50;
bool verbose = false;
option_struct(int argc, char **argv) {
int c;
while ((c = getopt(argc, argv, "vtn:i:a:s:")) != -1) {
switch (c) {
case 'n':
iterations = atoi(optarg);
break;
case 'i':
iteration_step = atoi(optarg);
break;
case 'v':
verbose = true;
break;
case 'a': {
auto impl = simdjson::available_implementations[optarg];
if(impl && impl->supported_by_runtime_system()) {
simdjson::active_implementation = impl;
} else {
std::cerr << "implementation " << optarg << " not found or not supported " << std::endl;
}
}
break;
case 's':
if (!strcmp(optarg, "stage1")) {
stage1_only = true;
} else if (!strcmp(optarg, "all")) {
stage1_only = false;
} else {
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
}
break;
default:
exit_error(string("Unexpected argument ") + std::string(1,static_cast<char>(c)));
}
}
}
template<typename F>
void each_stage(const F& f) const {
f(BenchmarkStage::STAGE1);
if (!this->stage1_only) {
f(BenchmarkStage::STAGE2);
f(BenchmarkStage::ALL);
}
}
};
struct feature_benchmarker {
benchmarker utf8;
benchmarker utf8_miss;
benchmarker escape;
benchmarker escape_miss;
benchmarker empty;
benchmarker empty_miss;
benchmarker struct7;
benchmarker struct7_miss;
benchmarker struct7_full;
benchmarker struct15;
benchmarker struct15_miss;
benchmarker struct23;
benchmarker struct23_miss;
feature_benchmarker(event_collector& collector) :
utf8 (SIMDJSON_BENCHMARK_DATA_DIR "generated/utf-8.json", collector),
utf8_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/utf-8-miss.json", collector),
escape (SIMDJSON_BENCHMARK_DATA_DIR "generated/escape.json", collector),
escape_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/escape-miss.json", collector),
empty (SIMDJSON_BENCHMARK_DATA_DIR "generated/0-structurals.json", collector),
empty_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/0-structurals-miss.json", collector),
struct7 (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals.json", collector),
struct7_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals-miss.json", collector),
struct7_full (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals-full.json", collector),
struct15 (SIMDJSON_BENCHMARK_DATA_DIR "generated/15-structurals.json", collector),
struct15_miss(SIMDJSON_BENCHMARK_DATA_DIR "generated/15-structurals-miss.json", collector),
struct23 (SIMDJSON_BENCHMARK_DATA_DIR "generated/23-structurals.json", collector),
struct23_miss(SIMDJSON_BENCHMARK_DATA_DIR "generated/23-structurals-miss.json", collector)
{
}
simdjson_really_inline void run_iterations(size_t iterations, bool stage1_only=false) {
struct7.run_iterations(iterations, stage1_only);
struct7_miss.run_iterations(iterations, stage1_only);
struct7_full.run_iterations(iterations, stage1_only);
utf8.run_iterations(iterations, stage1_only);
utf8_miss.run_iterations(iterations, stage1_only);
escape.run_iterations(iterations, stage1_only);
escape_miss.run_iterations(iterations, stage1_only);
empty.run_iterations(iterations, stage1_only);
empty_miss.run_iterations(iterations, stage1_only);
struct15.run_iterations(iterations, stage1_only);
struct15_miss.run_iterations(iterations, stage1_only);
struct23.run_iterations(iterations, stage1_only);
struct23_miss.run_iterations(iterations, stage1_only);
}
double cost_per_block(BenchmarkStage stage, const benchmarker& feature, size_t feature_blocks, const benchmarker& base) const {
return (feature[stage].best.elapsed_ns() - base[stage].best.elapsed_ns()) / double(feature_blocks);
}
// Whether we're recording cache miss and branch miss events
bool has_events() const {
return empty.collector.has_events();
}
// Base cost of any block (including empty ones)
double base_cost(BenchmarkStage stage) const {
return (empty[stage].best.elapsed_ns() / double(empty.stats->blocks));
}
// Extra cost of a 1-7 structural block over an empty block
double struct1_7_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct7, struct7.stats->blocks_with_1_structural, empty);
}
// Extra cost of an 1-7-structural miss
double struct1_7_miss_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct7_miss, struct7_miss.stats->blocks_with_1_structural, struct7);
}
// Rate of 1-7-structural misses per 8-structural flip
double struct1_7_miss_rate(BenchmarkStage stage) const {
if (!has_events()) { return 1; }
return struct7_miss[stage].best.branch_misses() - struct7[stage].best.branch_misses() / double(struct7_miss.stats->blocks_with_1_structural_flipped);
}
// Extra cost of an 8-15 structural block over a 1-7 structural block
double struct8_15_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct15, struct15.stats->blocks_with_8_structurals, struct7);
}
// Extra cost of an 8-15-structural miss over a 1-7 miss
double struct8_15_miss_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct15_miss, struct15_miss.stats->blocks_with_8_structurals_flipped, struct15);
}
// Rate of 8-15-structural misses per 8-structural flip
double struct8_15_miss_rate(BenchmarkStage stage) const {
if (!has_events()) { return 1; }
return double(struct15_miss[stage].best.branch_misses() - struct15[stage].best.branch_misses()) / double(struct15_miss.stats->blocks_with_8_structurals_flipped);
}
// Extra cost of a 16+-structural block over an 8-15 structural block (actual varies based on # of structurals!)
double struct16_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct23, struct23.stats->blocks_with_16_structurals, struct15);
}
// Extra cost of a 16-structural miss over an 8-15 miss
double struct16_miss_cost(BenchmarkStage stage) const {
return cost_per_block(stage, struct23_miss, struct23_miss.stats->blocks_with_16_structurals_flipped, struct23);
}
// Rate of 16-structural misses per 16-structural flip
double struct16_miss_rate(BenchmarkStage stage) const {
if (!has_events()) { return 1; }
return double(struct23_miss[stage].best.branch_misses() - struct23[stage].best.branch_misses()) / double(struct23_miss.stats->blocks_with_16_structurals_flipped);
}
// Extra cost of having UTF-8 in a block
double utf8_cost(BenchmarkStage stage) const {
return cost_per_block(stage, utf8, utf8.stats->blocks_with_utf8, struct7_full);
}
// Extra cost of a UTF-8 miss
double utf8_miss_cost(BenchmarkStage stage) const {
return cost_per_block(stage, utf8_miss, utf8_miss.stats->blocks_with_utf8_flipped, utf8);
}
// Rate of UTF-8 misses per UTF-8 flip
double utf8_miss_rate(BenchmarkStage stage) const {
if (!has_events()) { return 1; }
return double(utf8_miss[stage].best.branch_misses() - utf8[stage].best.branch_misses()) / double(utf8_miss.stats->blocks_with_utf8_flipped);
}
// Extra cost of having escapes in a block
double escape_cost(BenchmarkStage stage) const {
return cost_per_block(stage, escape, escape.stats->blocks_with_escapes, struct7_full);
}
// Extra cost of an escape miss
double escape_miss_cost(BenchmarkStage stage) const {
return cost_per_block(stage, escape_miss, escape_miss.stats->blocks_with_escapes_flipped, escape);
}
// Rate of escape misses per escape flip
double escape_miss_rate(BenchmarkStage stage) const {
if (!has_events()) { return 1; }
return double(escape_miss[stage].best.branch_misses() - escape[stage].best.branch_misses()) / double(escape_miss.stats->blocks_with_escapes_flipped);
}
double calc_expected_feature_cost(BenchmarkStage stage, const benchmarker& file) const {
// Expected base ns/block (empty)
json_stats& stats = *file.stats;
double expected = base_cost(stage) * double(stats.blocks);
expected += struct1_7_cost(stage) * double(stats.blocks_with_1_structural);
expected += utf8_cost(stage) * double(stats.blocks_with_utf8);
expected += escape_cost(stage) * double(stats.blocks_with_escapes);
expected += struct8_15_cost(stage) * double(stats.blocks_with_8_structurals);
expected += struct16_cost(stage) * double(stats.blocks_with_16_structurals);
return expected / double(stats.blocks);
}
double calc_expected_miss_cost(BenchmarkStage stage, const benchmarker& file) const {
// Expected base ns/block (empty)
json_stats& stats = *file.stats;
double expected = struct1_7_miss_cost(stage) * double(stats.blocks_with_1_structural_flipped) * struct1_7_miss_rate(stage);
expected += utf8_miss_cost(stage) * double(stats.blocks_with_utf8_flipped) * utf8_miss_rate(stage);
expected += escape_miss_cost(stage) * double(stats.blocks_with_escapes_flipped) * escape_miss_rate(stage);
expected += struct8_15_miss_cost(stage) * double(stats.blocks_with_8_structurals_flipped) * struct8_15_miss_rate(stage);
expected += struct16_miss_cost(stage) * double(stats.blocks_with_16_structurals_flipped) * struct16_miss_rate(stage);
return expected / double(stats.blocks);
}
double calc_expected_misses(BenchmarkStage stage, const benchmarker& file) const {
json_stats& stats = *file.stats;
double expected = double(stats.blocks_with_1_structural_flipped) * struct1_7_miss_rate(stage);
expected += double(stats.blocks_with_utf8_flipped) * utf8_miss_rate(stage);
expected += double(stats.blocks_with_escapes_flipped) * escape_miss_rate(stage);
expected += double(stats.blocks_with_8_structurals_flipped) * struct8_15_miss_rate(stage);
expected += double(stats.blocks_with_16_structurals_flipped) * struct16_miss_rate(stage);
return expected;
}
double calc_expected(BenchmarkStage stage, const benchmarker& file) const {
return calc_expected_feature_cost(stage, file) + calc_expected_miss_cost(stage, file);
}
void print(const option_struct& options) const {
printf("\n");
printf("Features in ns/block (64 bytes):\n");
printf("\n");
printf("| %-8s ", "Stage");
printf("| %8s ", "Base");
printf("| %8s ", "7 Struct");
printf("| %8s ", "UTF-8");
printf("| %8s ", "Escape");
printf("| %8s ", "15 Str.");
printf("| %8s ", "16+ Str.");
printf("| %15s ", "7 Struct Miss");
printf("| %15s ", "UTF-8 Miss");
printf("| %15s ", "Escape Miss");
printf("| %15s ", "15 Str. Miss");
printf("| %15s ", "16+ Str. Miss");
printf("|\n");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|\n");
options.each_stage([&](auto stage) {
printf("| %-8s ", benchmark_stage_name(stage));
printf("| %8.3g ", base_cost(stage));
printf("| %8.3g ", struct1_7_cost(stage));
printf("| %8.3g ", utf8_cost(stage));
printf("| %8.3g ", escape_cost(stage));
printf("| %8.3g ", struct8_15_cost(stage));
printf("| %8.3g ", struct16_cost(stage));
if (has_events()) {
printf("| %8.3g (%3d%%) ", struct1_7_miss_cost(stage), int(struct1_7_miss_rate(stage)*100));
printf("| %8.3g (%3d%%) ", utf8_miss_cost(stage), int(utf8_miss_rate(stage)*100));
printf("| %8.3g (%3d%%) ", escape_miss_cost(stage), int(escape_miss_rate(stage)*100));
printf("| %8.3g (%3d%%) ", struct8_15_miss_cost(stage), int(struct8_15_miss_rate(stage)*100));
printf("| %8.3g (%3d%%) ", struct16_miss_cost(stage), int(struct16_miss_rate(stage)*100));
} else {
printf("| %8.3g ", struct1_7_miss_cost(stage));
printf("| %8.3g ", utf8_miss_cost(stage));
printf("| %8.3g ", escape_miss_cost(stage));
printf("| %8.3g ", struct8_15_miss_cost(stage));
printf("| %8.3g ", struct16_miss_cost(stage));
}
printf("|\n");
});
}
};
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
double actual = results[stage].best.elapsed_ns() / double(results.stats->blocks);
double calc = features.calc_expected(stage, results);
double actual_misses = results[stage].best.branch_misses();
double calc_misses = features.calc_expected_misses(stage, results);
double calc_miss_cost = features.calc_expected_miss_cost(stage, results);
printf(" | %-8s ", benchmark_stage_name(stage));
printf("| %-15s ", filename);
printf("| %8.3g ", features.calc_expected_feature_cost(stage, results));
printf("| %8.3g ", calc_miss_cost);
printf("| %8.3g ", calc);
printf("| %8.3g ", actual);
printf("| %+8.3g ", actual - calc);
printf("| %13llu ", (long long unsigned)(calc_misses));
if (features.has_events()) {
printf("| %13llu ", (long long unsigned)(actual_misses));
printf("| %+13lld ", (long long int)(actual_misses - calc_misses));
double miss_adjustment = calc_miss_cost * (double(int64_t(actual_misses - calc_misses)) / calc_misses);
printf("| %8.3g ", calc_miss_cost + miss_adjustment);
printf("| %+8.3g ", actual - (calc + miss_adjustment));
}
printf("|\n");
}
int main(int argc, char *argv[]) {
// Read options
exe_name = argv[0];
option_struct options(argc, argv);
if (options.verbose) {
verbose_stream = &cout;
}
// Initialize the event collector. We put this early so if it prints an error message, it's the
// first thing printed.
event_collector collector;
// Set up benchmarkers by reading all files
feature_benchmarker features(collector);
benchmarker gsoc_2018(SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json", collector);
benchmarker twitter(SIMDJSON_BENCHMARK_DATA_DIR "twitter.json", collector);
benchmarker random(SIMDJSON_BENCHMARK_DATA_DIR "random.json", collector);
// Run the benchmarks
progress_bar progress(options.iterations, 100);
// Put the if (options.stage1_only) *outside* the loop so that run_iterations will be optimized
if (options.stage1_only) {
for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
if (!options.verbose) { progress.print(iteration); }
features.run_iterations(options.iteration_step, true);
gsoc_2018.run_iterations(options.iteration_step, true);
twitter.run_iterations(options.iteration_step, true);
random.run_iterations(options.iteration_step, true);
}
} else {
for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
if (!options.verbose) { progress.print(iteration); }
features.run_iterations(options.iteration_step, false);
gsoc_2018.run_iterations(options.iteration_step, false);
twitter.run_iterations(options.iteration_step, false);
random.run_iterations(options.iteration_step, false);
}
}
if (!options.verbose) { progress.erase(); }
features.print(options);
// Gauge effectiveness
if (options.verbose) {
printf("\n");
printf(" Effectiveness Check: Estimated vs. Actual ns/block for real files:\n");
printf("\n");
printf(" | %8s ", "Stage");
printf("| %-15s ", "File");
printf("| %11s ", "Est. (Base)");
printf("| %11s ", "Est. (Miss)");
printf("| %8s ", "Est.");
printf("| %8s ", "Actual");
printf("| %8s ", "Diff");
printf("| %13s ", "Est. Misses");
if (features.has_events()) {
printf("| %13s ", "Actual Misses");
printf("| %13s ", "Diff (Misses)");
printf("| %13s ", "Adjusted Miss");
printf("| %13s ", "Adjusted Diff");
}
printf("|\n");
printf(" |%.10s", "---------------------------------------");
printf("|%.17s", "---------------------------------------");
printf("|%.13s", "---------------------------------------");
printf("|%.13s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.10s", "---------------------------------------");
printf("|%.15s", "---------------------------------------");
if (features.has_events()) {
printf("|%.15s", "---------------------------------------");
printf("|%.15s", "---------------------------------------");
printf("|%.15s", "---------------------------------------");
printf("|%.15s", "---------------------------------------");
}
printf("|\n");
options.each_stage([&](auto stage) {
print_file_effectiveness(stage, "gsoc-2018.json", gsoc_2018, features);
print_file_effectiveness(stage, "twitter.json", twitter, features);
print_file_effectiveness(stage, "random.json", random, features);
});
}
return EXIT_SUCCESS;
}
+75 -189
View File
@@ -1,82 +1,7 @@
#ifndef _BENCHMARK_H_
#define _BENCHMARK_H_
#include <float.h>
#include <stdint.h>
#include <time.h>
#ifdef __x86_64__
const char *unitname = "cycles";
#define RDTSC_START(cycles) \
do { \
uint32_t cyc_high, cyc_low; \
__asm volatile("cpuid\n" \
"rdtsc\n" \
"mov %%edx, %0\n" \
"mov %%eax, %1" \
: "=r"(cyc_high), "=r"(cyc_low) \
: \
: /* no read only */ \
"%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
); \
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
} while (0)
#define RDTSC_STOP(cycles) \
do { \
uint32_t cyc_high, cyc_low; \
__asm volatile("rdtscp\n" \
"mov %%edx, %0\n" \
"mov %%eax, %1\n" \
"cpuid" \
: "=r"(cyc_high), "=r"(cyc_low) \
: /* no read only registers */ \
: "%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
); \
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
} while (0)
#else
const char *unitname = " (clock units) ";
#define RDTSC_START(cycles) \
do { \
cycles = clock(); \
} while (0)
#define RDTSC_STOP(cycles) \
do { \
cycles = clock(); \
} while (0)
#endif
static __attribute__((noinline)) uint64_t rdtsc_overhead_func(uint64_t dummy) {
return dummy;
}
uint64_t global_rdtsc_overhead = (uint64_t)UINT64_MAX;
#define RDTSC_SET_OVERHEAD(test, repeat) \
do { \
uint64_t cycles_start, cycles_final, cycles_diff; \
uint64_t min_diff = UINT64_MAX; \
for (int i = 0; i < repeat; i++) { \
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
RDTSC_START(cycles_start); \
test; \
RDTSC_STOP(cycles_final); \
cycles_diff = (cycles_final - cycles_start); \
if (cycles_diff < min_diff) \
min_diff = cycles_diff; \
} \
global_rdtsc_overhead = min_diff; \
} while (0)
double diff(timespec start, timespec end) {
return ((end.tv_nsec + 1000000000 * end.tv_sec) -
(start.tv_nsec + 1000000000 * start.tv_sec)) /
1000000000.0;
}
#include "event_counter.h"
/*
* Prints the best number of operations per cycle where
@@ -86,138 +11,99 @@ double diff(timespec start, timespec end) {
*/
#define BEST_TIME(name, test, expected, pre, repeat, size, verbose) \
do { \
if (global_rdtsc_overhead == UINT64_MAX) { \
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
} \
if (verbose) \
printf("%-40s\t: ", name); \
std::printf("%-40s\t: ", name); \
else \
printf("\"%-40s\"", name); \
std::printf("\"%-40s\"", name); \
fflush(NULL); \
uint64_t cycles_start, cycles_final, cycles_diff; \
uint64_t min_diff = (uint64_t)-1; \
double min_sumclockdiff = DBL_MAX; \
uint64_t sum_diff = 0; \
double sumclockdiff = 0; \
struct timespec time1, time2; \
for (int i = 0; i < repeat; i++) { \
event_collector collector; \
event_aggregate aggregate{}; \
for (decltype(repeat) i = 0; i < repeat; i++) { \
pre; \
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
clock_gettime(CLOCK_REALTIME, &time1); \
RDTSC_START(cycles_start); \
std::atomic_thread_fence(std::memory_order_acquire); \
collector.start(); \
if (test != expected) { \
fprintf(stderr, "not expected (%d , %d )", (int)test, (int)expected); \
std::fprintf(stderr, "not expected (%d , %d )", (int)test, \
(int)expected); \
break; \
} \
RDTSC_STOP(cycles_final); \
clock_gettime(CLOCK_REALTIME, &time2); \
double thistiming = diff(time1, time2); \
sumclockdiff += thistiming; \
if (thistiming < min_sumclockdiff) \
min_sumclockdiff = thistiming; \
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
if (cycles_diff < min_diff) \
min_diff = cycles_diff; \
sum_diff += cycles_diff; \
std::atomic_thread_fence(std::memory_order_release); \
event_count allocate_count = collector.end(); \
aggregate << allocate_count; \
} \
uint64_t S = size; \
float cycle_per_op = (min_diff) / (double)S; \
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
double avg_gb_per_s = \
((double)S * repeat) / ((sumclockdiff)*1000.0 * 1000.0 * 1000.0); \
double max_gb_per_s = \
((double)S) / ((min_sumclockdiff)*1000.0 * 1000.0 * 1000.0); \
if (verbose) \
printf(" %7.3f %s per input byte (best) ", cycle_per_op, unitname); \
if (verbose) \
printf(" %7.3f %s per input byte (avg) ", avg_cycle_per_op, unitname); \
if (verbose) \
printf(" %7.3f GB/s (error margin: %.3f GB/s)", max_gb_per_s, \
-avg_gb_per_s + max_gb_per_s); \
if (!verbose) \
printf(" %20.3f %20.3f %20.3f %20.3f ", cycle_per_op, \
avg_cycle_per_op - cycle_per_op, max_gb_per_s, \
-avg_gb_per_s + max_gb_per_s); \
printf("\n"); \
fflush(NULL); \
if (collector.has_events()) { \
std::printf("%7.3f", \
aggregate.best.cycles() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" cycles/byte "); \
} \
std::printf("\t"); \
std::printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" instructions/byte "); \
} \
std::printf("\t"); \
} \
double gb = static_cast<double>(size) / 1000000000.0; \
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" GB/s "); \
} \
std::printf("\t"); \
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" documents/s "); \
} \
std::printf("\n"); \
std::fflush(NULL); \
} while (0)
// like BEST_TIME, but no check
#define BEST_TIME_NOCHECK(name, test, pre, repeat, size, verbose) \
do { \
if (global_rdtsc_overhead == UINT64_MAX) { \
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
} \
if (verbose) \
printf("%-40s\t: ", name); \
fflush(NULL); \
uint64_t cycles_start, cycles_final, cycles_diff; \
uint64_t min_diff = (uint64_t)-1; \
uint64_t sum_diff = 0; \
for (int i = 0; i < repeat; i++) { \
std::printf("%-40s\t: ", name); \
else \
std::printf("\"%-40s\"", name); \
std::fflush(NULL); \
event_collector collector; \
event_aggregate aggregate{}; \
for (decltype(repeat) i = 0; i < repeat; i++) { \
pre; \
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
RDTSC_START(cycles_start); \
std::atomic_thread_fence(std::memory_order_acquire); \
collector.start(); \
test; \
RDTSC_STOP(cycles_final); \
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
if (cycles_diff < min_diff) \
min_diff = cycles_diff; \
sum_diff += cycles_diff; \
std::atomic_thread_fence(std::memory_order_release); \
event_count allocate_count = collector.end(); \
aggregate << allocate_count; \
} \
uint64_t S = size; \
float cycle_per_op = (min_diff) / (double)S; \
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
if (verbose) \
printf(" %.3f %s per input byte (best) ", cycle_per_op, unitname); \
if (verbose) \
printf(" %.3f %s per input byte (avg) ", avg_cycle_per_op, unitname); \
if (verbose) \
printf("\n"); \
if (!verbose) \
printf(" %.3f ", cycle_per_op); \
fflush(NULL); \
} while (0)
// like BEST_TIME except that we run a function to check the result
#define BEST_TIME_CHECK(test, check, pre, repeat, size, verbose) \
do { \
if (global_rdtsc_overhead == UINT64_MAX) { \
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
} \
if (verbose) \
printf("%-60s\t:\n", #test); \
fflush(NULL); \
uint64_t cycles_start, cycles_final, cycles_diff; \
uint64_t min_diff = (uint64_t)-1; \
uint64_t sum_diff = 0; \
for (int i = 0; i < repeat; i++) { \
pre; \
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
RDTSC_START(cycles_start); \
test; \
RDTSC_STOP(cycles_final); \
if (!check) { \
printf("error"); \
break; \
if (collector.has_events()) { \
std::printf("%7.3f", \
aggregate.best.cycles() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" cycles/byte "); \
} \
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
if (cycles_diff < min_diff) \
min_diff = cycles_diff; \
sum_diff += cycles_diff; \
std::printf("\t"); \
std::printf("%7.3f", \
aggregate.best.instructions() / static_cast<double>(size)); \
if (verbose) { \
std::printf(" instructions/byte "); \
} \
std::printf("\t"); \
} \
uint64_t S = size; \
float cycle_per_op = (min_diff) / (double)S; \
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
if (verbose) \
printf(" %.3f cycles per operation (best) ", cycle_per_op); \
if (verbose) \
printf("\t%.3f cycles per operation (avg) ", avg_cycle_per_op); \
if (verbose) \
printf("\n"); \
if (!verbose) \
printf(" %.3f ", cycle_per_op); \
fflush(NULL); \
double gb = static_cast<double>(size) / 1000000000.0; \
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" GB/s "); \
} \
std::printf("\t"); \
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
if (verbose) { \
std::printf(" documents/s "); \
} \
std::printf("\n"); \
std::fflush(NULL); \
} while (0)
#endif
+533
View File
@@ -0,0 +1,533 @@
#ifndef __BENCHMARKER_H
#define __BENCHMARKER_H
#include "event_counter.h"
#include "simdjson.h" // For SIMDJSON_DISABLE_DEPRECATED_WARNINGS
#include <cassert>
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <unistd.h>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#include "simdjson.h"
#include <functional>
using namespace simdjson;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::to_string;
using std::vector;
using std::ostream;
using std::ofstream;
using std::exception;
using std::min;
using std::max;
// Initialize "verbose" to go nowhere. We'll read options in main() and set to cout if verbose is true.
std::ofstream dev_null;
ostream *verbose_stream = &dev_null;
const size_t BYTES_PER_BLOCK = 64;
ostream& verbose() {
return *verbose_stream;
}
void exit_error(string message) {
cerr << message << endl;
exit(EXIT_FAILURE);
}
struct json_stats {
size_t bytes = 0;
size_t blocks = 0;
size_t structurals = 0;
size_t blocks_with_utf8 = 0;
size_t blocks_with_utf8_flipped = 0;
size_t blocks_with_escapes = 0;
size_t blocks_with_escapes_flipped = 0;
size_t blocks_with_0_structurals = 0;
size_t blocks_with_0_structurals_flipped = 0;
size_t blocks_with_1_structural = 0;
size_t blocks_with_1_structural_flipped = 0;
size_t blocks_with_8_structurals = 0;
size_t blocks_with_8_structurals_flipped = 0;
size_t blocks_with_16_structurals = 0;
size_t blocks_with_16_structurals_flipped = 0;
json_stats(const padded_string& json, const dom::parser& parser) {
bytes = json.size();
blocks = bytes / BYTES_PER_BLOCK;
if (bytes % BYTES_PER_BLOCK > 0) { blocks++; } // Account for remainder block
structurals = parser.implementation->n_structural_indexes-1;
// Calculate stats on blocks that will trigger utf-8 if statements / mispredictions
bool last_block_has_utf8 = false;
for (size_t block=0; block<blocks; block++) {
// Find utf-8 in the block
size_t block_start = block*BYTES_PER_BLOCK;
size_t block_end = block_start+BYTES_PER_BLOCK;
if (block_end > json.size()) { block_end = json.size(); }
bool block_has_utf8 = false;
for (size_t i=block_start; i<block_end; i++) {
if (json.data()[i] & 0x80) {
block_has_utf8 = true;
break;
}
}
if (block_has_utf8) {
blocks_with_utf8++;
}
if (block > 0 && last_block_has_utf8 != block_has_utf8) {
blocks_with_utf8_flipped++;
}
last_block_has_utf8 = block_has_utf8;
}
// Calculate stats on blocks that will trigger escape if statements / mispredictions
bool last_block_has_escapes = false;
for (size_t block=0; block<blocks; block++) {
// Find utf-8 in the block
size_t block_start = block*BYTES_PER_BLOCK;
size_t block_end = block_start+BYTES_PER_BLOCK;
if (block_end > json.size()) { block_end = json.size(); }
bool block_has_escapes = false;
for (size_t i=block_start; i<block_end; i++) {
if (json.data()[i] == '\\') {
block_has_escapes = true;
break;
}
}
if (block_has_escapes) {
blocks_with_escapes++;
}
if (block > 0 && last_block_has_escapes != block_has_escapes) {
blocks_with_escapes_flipped++;
}
last_block_has_escapes = block_has_escapes;
}
// Calculate stats on blocks that will trigger structural count if statements / mispredictions
bool last_block_has_0_structurals = false;
bool last_block_has_1_structural = false;
bool last_block_has_8_structurals = false;
bool last_block_has_16_structurals = false;
size_t structural=0;
for (size_t block=0; block<blocks; block++) {
// Count structurals in the block
int block_structurals=0;
while (structural < parser.implementation->n_structural_indexes && parser.implementation->structural_indexes[structural] < (block+1)*BYTES_PER_BLOCK) {
block_structurals++;
structural++;
}
bool block_has_0_structurals = block_structurals == 0;
if (block_has_0_structurals) {
blocks_with_0_structurals++;
}
if (block > 0 && last_block_has_0_structurals != block_has_0_structurals) {
blocks_with_0_structurals_flipped++;
}
last_block_has_0_structurals = block_has_0_structurals;
bool block_has_1_structural = block_structurals >= 1;
if (block_has_1_structural) {
blocks_with_1_structural++;
}
if (block > 0 && last_block_has_1_structural != block_has_1_structural) {
blocks_with_1_structural_flipped++;
}
last_block_has_1_structural = block_has_1_structural;
bool block_has_8_structurals = block_structurals >= 8;
if (block_has_8_structurals) {
blocks_with_8_structurals++;
}
if (block > 0 && last_block_has_8_structurals != block_has_8_structurals) {
blocks_with_8_structurals_flipped++;
}
last_block_has_8_structurals = block_has_8_structurals;
bool block_has_16_structurals = block_structurals >= 16;
if (block_has_16_structurals) {
blocks_with_16_structurals++;
}
if (block > 0 && last_block_has_16_structurals != block_has_16_structurals) {
blocks_with_16_structurals_flipped++;
}
last_block_has_16_structurals = block_has_16_structurals;
}
}
};
struct progress_bar {
int max_value;
int total_ticks;
double ticks_per_value;
int next_tick;
progress_bar(int _max_value, int _total_ticks) : max_value(_max_value), total_ticks(_total_ticks), ticks_per_value(double(_total_ticks)/_max_value), next_tick(0) {
fprintf(stderr, "[");
for (int i=0;i<total_ticks;i++) {
fprintf(stderr, " ");
}
fprintf(stderr, "]");
for (int i=0;i<total_ticks+1;i++) {
fprintf(stderr, "\b");
}
}
void print(int value) {
double ticks = value*ticks_per_value;
if (ticks >= total_ticks) {
ticks = total_ticks-1;
}
int tick;
for (tick=next_tick; tick <= ticks && tick <= total_ticks; tick++) {
fprintf(stderr, "=");
}
next_tick = tick;
}
void erase() const {
for (int i=0;i<next_tick+1;i++) {
fprintf(stderr, "\b");
}
for (int tick=0; tick<=total_ticks+2; tick++) {
fprintf(stderr, " ");
}
for (int tick=0; tick<=total_ticks+2; tick++) {
fprintf(stderr, "\b");
}
}
};
/**
* The speed at which we can allocate memory is strictly system specific.
* It depends on the OS and the runtime library. It is subject to various
* system-specific knobs. It is not something that we can reasonably
* benchmark with crude timings.
* If someone wants to optimize how simdjson allocate memory, then it will
* almost surely require a distinct benchmarking tool. What is meant by
* "memory allocation" also requires a definition. Doing "new char[size]" can
* do many different things depending on the system.
*/
enum class BenchmarkStage {
ALL, // This excludes allocation
ALLOCATE,
STAGE1,
STAGE2
};
const char* benchmark_stage_name(BenchmarkStage stage) {
switch (stage) {
case BenchmarkStage::ALL: return "All (Without Allocation)";
case BenchmarkStage::ALLOCATE: return "Allocate";
case BenchmarkStage::STAGE1: return "Stage 1";
case BenchmarkStage::STAGE2: return "Stage 2";
default: return "Unknown";
}
}
struct benchmarker {
// JSON text from loading the file. Owns the memory.
padded_string json{};
// JSON filename
const char *filename;
// Event collector that can be turned on to measure cycles, missed branches, etc.
event_collector& collector;
// Statistics about the JSON file independent of its speed (amount of utf-8, structurals, etc.).
// Loaded on first parse.
json_stats* stats;
// Speed and event summary for full parse (stage 1 and stage 2, but *excluding* allocation)
event_aggregate all_stages_without_allocation{};
// Speed and event summary for stage 1
event_aggregate stage1{};
// Speed and event summary for stage 2
event_aggregate stage2{};
// Speed and event summary for allocation
event_aggregate allocate_stage{};
// Speed and event summary for the repeatly-parsing mode
event_aggregate loop{};
benchmarker(const char *_filename, event_collector& _collector)
: filename(_filename), collector(_collector), stats(NULL) {
verbose() << "[verbose] loading " << filename << endl;
auto error = padded_string::load(filename).get(json);
if (error) {
exit_error(string("Could not load the file ") + filename);
}
verbose() << "[verbose] loaded " << filename << endl;
}
~benchmarker() {
if (stats) {
delete stats;
}
}
benchmarker(const benchmarker&) = delete;
benchmarker& operator=(const benchmarker&) = delete;
const event_aggregate& operator[](BenchmarkStage stage) const {
switch (stage) {
case BenchmarkStage::ALL: return this->all_stages_without_allocation;
case BenchmarkStage::STAGE1: return this->stage1;
case BenchmarkStage::STAGE2: return this->stage2;
case BenchmarkStage::ALLOCATE: return this->allocate_stage;
default: exit_error("Unknown stage"); return this->all_stages_without_allocation;
}
}
int iterations() const {
return all_stages_without_allocation.iterations;
}
simdjson_really_inline void run_iteration(bool stage1_only, bool hotbuffers=false) {
// Allocate dom::parser
collector.start();
dom::parser parser;
// We always allocate at least 64KB. Smaller allocations may actually be slower under some systems.
error_code error = parser.allocate(json.size() < 65536 ? 65536 : json.size());
if (error) {
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON result: " + error_message(error));
}
event_count allocate_count = collector.end();
allocate_stage << allocate_count;
// Run it once to get hot buffers
if(hotbuffers) {
auto result = parser.parse((const uint8_t *)json.data(), json.size());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
}
verbose() << "[verbose] allocated memory for parsed JSON " << endl;
// Stage 1 (find structurals)
collector.start();
error = parser.implementation->stage1((const uint8_t *)json.data(), json.size(), false);
event_count stage1_count = collector.end();
stage1 << stage1_count;
if (error) {
exit_error(string("Failed to parse ") + filename + " during stage 1: " + error_message(error));
}
// Stage 2 (unified machine) and the rest
if (stage1_only) {
all_stages_without_allocation << stage1_count;
} else {
event_count stage2_count;
collector.start();
error = parser.implementation->stage2(parser.doc);
if (error) {
exit_error(string("Failed to parse ") + filename + " during stage 2 parsing " + error_message(error));
}
stage2_count = collector.end();
stage2 << stage2_count;
all_stages_without_allocation << stage1_count + stage2_count;
}
// Calculate stats the first time we parse
if (stats == NULL) {
if (stage1_only) { // we need stage 2 once
error = parser.implementation->stage2(parser.doc);
if (error) {
printf("Warning: failed to parse during stage 2. Unable to acquire statistics.\n");
}
}
stats = new json_stats(json, parser);
}
}
void run_loop(size_t iterations) {
dom::parser parser;
auto firstresult = parser.parse((const uint8_t *)json.data(), json.size());
if (firstresult.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
}
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());
if (result.error()) {
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
}
}
event_count all_loop_count = collector.end();
loop << all_loop_count;
}
simdjson_really_inline void run_iterations(size_t iterations, bool stage1_only, bool hotbuffers=false) {
for (size_t i = 0; i<iterations; i++) {
run_iteration(stage1_only, hotbuffers);
}
run_loop(iterations);
}
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
template<typename T>
void print_aggregate(const char* prefix, const T& stage) const {
printf("%s%-13s: %8.4f ns per block (%6.2f%%) - %8.4f ns per byte - %8.4f ns per structural - %8.4f GB/s\n",
prefix,
"Speed",
stage.elapsed_ns() / static_cast<double>(stats->blocks), // per block
percent(stage.elapsed_sec(), all_stages_without_allocation.elapsed_sec()), // %
stage.elapsed_ns() / static_cast<double>(stats->bytes), // per byte
stage.elapsed_ns() / static_cast<double>(stats->structurals), // per structural
(static_cast<double>(json.size()) / 1000000000.0) / stage.elapsed_sec() // GB/s
);
if (collector.has_events()) {
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f GHz est. frequency\n",
prefix,
"Cycles",
stage.cycles() / static_cast<double>(stats->blocks),
percent(stage.cycles(), all_stages_without_allocation.cycles()),
stage.cycles() / static_cast<double>(stats->bytes),
stage.cycles() / static_cast<double>(stats->structurals),
(stage.cycles() / stage.elapsed_sec()) / 1000000000.0
);
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f per cycle\n",
prefix,
"Instructions",
stage.instructions() / static_cast<double>(stats->blocks),
percent(stage.instructions(), all_stages_without_allocation.instructions()),
stage.instructions() / static_cast<double>(stats->bytes),
stage.instructions() / static_cast<double>(stats->structurals),
stage.instructions() / static_cast<double>(stage.cycles())
);
// NOTE: removed cycles/miss because it is a somewhat misleading stat
printf("%s%-13s: %7.0f branch misses (%6.2f%%) - %.0f cache misses (%6.2f%%) - %.2f cache references\n",
prefix,
"Misses",
stage.branch_misses(),
percent(stage.branch_misses(), all_stages_without_allocation.branch_misses()),
stage.cache_misses(),
percent(stage.cache_misses(), all_stages_without_allocation.cache_misses()),
stage.cache_references()
);
}
}
static double percent(size_t a, size_t b) {
return 100.0 * static_cast<double>(a) / static_cast<double>(b);
}
static double percent(double a, double b) {
return 100.0 * a / b;
}
void print(bool tabbed_output) const {
if (tabbed_output) {
char* filename_copy = (char*)malloc(strlen(filename)+1);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Validated CRT_SECURE safe here
strcpy(filename_copy, filename);
SIMDJSON_POP_DISABLE_WARNINGS
#if defined(__linux__)
char* base = ::basename(filename_copy);
#else
char* base = filename_copy;
#endif
if (strlen(base) >= 5 && !strcmp(base+strlen(base)-5, ".json")) {
base[strlen(base)-5] = '\0';
}
double gb = static_cast<double>(json.size()) / 1000000000.0;
if (collector.has_events()) {
printf("\"%s\"\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
base,
allocate_stage.best.cycles() / static_cast<double>(json.size()),
stage1.best.cycles() / static_cast<double>(json.size()),
stage2.best.cycles() / static_cast<double>(json.size()),
all_stages_without_allocation.best.cycles() / static_cast<double>(json.size()),
gb / all_stages_without_allocation.best.elapsed_sec(),
gb / stage1.best.elapsed_sec(),
gb / stage2.best.elapsed_sec());
} else {
printf("\"%s\"\t\t\t\t\t%f\t%f\t%f\n",
base,
gb / all_stages_without_allocation.best.elapsed_sec(),
gb / stage1.best.elapsed_sec(),
gb / stage2.best.elapsed_sec());
}
free(filename_copy);
} else {
printf("\n");
printf("%s\n", filename);
printf("%s\n", string(strlen(filename), '=').c_str());
printf("%9zu blocks - %10zu bytes - %5zu structurals (%5.1f %%)\n", stats->bytes / BYTES_PER_BLOCK, stats->bytes, stats->structurals, percent(stats->structurals, stats->bytes));
if (stats) {
printf("special blocks with: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
stats->blocks_with_utf8, percent(stats->blocks_with_utf8, stats->blocks),
stats->blocks_with_escapes, percent(stats->blocks_with_escapes, stats->blocks),
stats->blocks_with_0_structurals, percent(stats->blocks_with_0_structurals, stats->blocks),
stats->blocks_with_1_structural, percent(stats->blocks_with_1_structural, stats->blocks),
stats->blocks_with_8_structurals, percent(stats->blocks_with_8_structurals, stats->blocks),
stats->blocks_with_16_structurals, percent(stats->blocks_with_16_structurals, stats->blocks));
printf("special block flips: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
stats->blocks_with_utf8_flipped, percent(stats->blocks_with_utf8_flipped, stats->blocks),
stats->blocks_with_escapes_flipped, percent(stats->blocks_with_escapes_flipped, stats->blocks),
stats->blocks_with_0_structurals_flipped, percent(stats->blocks_with_0_structurals_flipped, stats->blocks),
stats->blocks_with_1_structural_flipped, percent(stats->blocks_with_1_structural_flipped, stats->blocks),
stats->blocks_with_8_structurals_flipped, percent(stats->blocks_with_8_structurals_flipped, stats->blocks),
stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
}
printf("\n");
printf("All Stages (excluding allocation)\n");
print_aggregate("| " , all_stages_without_allocation.best);
// frequently, allocation is a tiny fraction of the running time so we omit it
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
printf("|- Allocation\n");
print_aggregate("| ", allocate_stage.best);
}
printf("|- Stage 1\n");
print_aggregate("| ", stage1.best);
printf("|- Stage 2\n");
print_aggregate("| ", stage2.best);
if (collector.has_events()) {
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
double freqall = (all_stages_without_allocation.best.cycles() / all_stages_without_allocation.best.elapsed_sec()) / 1000000000.0;
double freqmin = min(freq1, freq2);
double freqmax = max(freq1, freq2);
if((freqall < 0.95 * freqmin) or (freqall > 1.05 * freqmax)) {
printf("\nWarning: The processor frequency fluctuates in an expected way!!!\n"
"Range for stage 1 and stage 2 : [%.3f GHz, %.3f GHz], overall: %.3f GHz.\n",
freqmin, freqmax, freqall);
}
}
printf("\n%.1f documents parsed per second (best)\n", 1.0/static_cast<double>(all_stages_without_allocation.best.elapsed_sec()));
}
}
};
#endif
+101
View File
@@ -0,0 +1,101 @@
# Relevant targets:
# checkperf-parse: builds the reference checkperf-parse, syncing reference repository if needed
# checkperf: builds the targets needed for checkperf (parse, perfdiff, checkperf-parse)
# update-checkperf-repo: updates the reference repository we're checking performance against
# checkperf-repo: initialize and sync reference repository (first time only)
# TEST checkperf: runs the actual checkperf test
# Clone the repository if it's not there
find_package(Git QUIET)
if (SIMDJSON_IS_UNDER_GIT AND SIMDJSON_GIT AND Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) ) # We use "-C" which requires a recent git
message(STATUS "Git is available and it is recent. We are enabling checkperf targets.")
# sync_git_repository(myrepo ...) creates two targets:
# myrepo - if the repo does not exist, creates and syncs it against the origin branch
# update_myrepo - will update the repo against the origin branch (and create if needed)
function(sync_git_repository name dir remote branch url)
# This conditionally creates the git repository
add_custom_command(
OUTPUT ${dir}/.git/config
COMMAND ${GIT_EXECUTABLE} init ${dir}
COMMAND ${GIT_EXECUTABLE} -C ${dir} remote add ${remote} ${url}
)
add_custom_target(init-${name} DEPENDS ${dir}/.git/config)
# This conditionally syncs the git repository, first time only
add_custom_command(
OUTPUT ${dir}/.git/FETCH_HEAD
COMMAND ${GIT_EXECUTABLE} remote set-url ${remote} ${url}
COMMAND ${GIT_EXECUTABLE} fetch --depth=1 ${remote} ${branch}
COMMAND ${GIT_EXECUTABLE} reset --hard ${remote}/${branch}
WORKING_DIRECTORY ${dir}
DEPENDS init-${name}
)
# This is the ${name} target, which will create and sync the repo first time only
add_custom_target(${name} DEPENDS ${dir}/.git/FETCH_HEAD)
# This is the update-${name} target, which will sync the repo (creating it if needed)
add_custom_target(
update-${name}
COMMAND ${GIT_EXECUTABLE} remote set-url ${remote} ${url}
COMMAND ${GIT_EXECUTABLE} fetch --depth=1 ${remote} ${branch}
COMMAND ${GIT_EXECUTABLE} reset --hard ${remote}/${branch}
WORKING_DIRECTORY ${dir}
DEPENDS init-${name}
)
endfunction(sync_git_repository)
set(SIMDJSON_CHECKPERF_REMOTE origin CACHE STRING "Remote repository to compare performance against")
set(SIMDJSON_CHECKPERF_BRANCH master CACHE STRING "Branch to compare performance against")
set(SIMDJSON_CHECKPERF_DIR ${CMAKE_CURRENT_BINARY_DIR}/checkperf-reference/${SIMDJSON_CHECKPERF_BRANCH} CACHE STRING "Location to put checkperf performance comparison repository")
set(SIMDJSON_CHECKPERF_ARGS ${EXAMPLE_JSON} CACHE STRING "Arguments to pass to parse during checkperf")
sync_git_repository(checkperf-repo ${SIMDJSON_CHECKPERF_DIR} ${SIMDJSON_CHECKPERF_REMOTE} ${SIMDJSON_CHECKPERF_BRANCH} ${SIMDJSON_GITHUB_REPOSITORY})
# Commands to cause cmake on benchmark/checkperf-master/build/
# - first, copy CMakeCache.txt
add_custom_command(
OUTPUT ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
COMMAND ${CMAKE_COMMAND} -E make_directory ${SIMDJSON_CHECKPERF_DIR}/build
COMMAND ${CMAKE_COMMAND} -E copy ${SIMDJSON_USER_CMAKECACHE} ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
DEPENDS checkperf-repo simdjson-user-cmakecache
)
# - second, cmake ..
add_custom_command(
OUTPUT ${SIMDJSON_CHECKPERF_DIR}/build/cmake_install.cmake # We make many things but this seems the most cross-platform one we can depend on
COMMAND
${CMAKE_COMMAND} -E env CXX=${CMAKE_CXX_COMPILER} CC=${CMAKE_C_COMPILER}
${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_COMPETITION=OFF -G ${CMAKE_GENERATOR} ..
WORKING_DIRECTORY ${SIMDJSON_CHECKPERF_DIR}/build
DEPENDS ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
)
# - third, build parse.
if (CMAKE_CONFIGURATION_TYPES)
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/$<CONFIGURATION>/parse)
else()
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/parse)
endif()
add_custom_target(
checkperf-parse ALL # TODO is ALL necessary?
# Build parse
COMMAND ${CMAKE_COMMAND} --build . --target parse --config $<CONFIGURATION>
WORKING_DIRECTORY ${SIMDJSON_CHECKPERF_DIR}/build
DEPENDS ${SIMDJSON_CHECKPERF_DIR}/build/cmake_install.cmake # We make many things but this seems the most cross-platform one we can depend on
)
# Target to build everything needed for the checkperf test
add_custom_target(checkperf DEPENDS parse perfdiff checkperf-parse)
# Add the actual checkperf test
add_test(
NAME checkperf
# 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 DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
else()
if (CMAKE_GENERATOR MATCHES Ninja)
message(STATUS "We disable the checkperf targets under Ninja.")
else()
message(STATUS "Either git is unavailable or else it is too old. We are disabling checkperf targets.")
endif()
endif ()
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include <vector>
#include <cstdint>
#include "event_counter.h"
#include "json_benchmark.h"
bool equals(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; }
void remove_duplicates(std::vector<int64_t> &v) {
std::sort(v.begin(), v.end());
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());
}
//
// Interface
//
namespace distinct_user_id {
template<typename T> static void DistinctUserID(benchmark::State &state);
} // namespace
//
// Implementation
//
#include "dom.h"
namespace distinct_user_id {
using namespace simdjson;
template<typename T> static void DistinctUserID(benchmark::State &state) {
//
// Load the JSON file
//
constexpr const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
error_code error;
padded_string json;
if ((error = padded_string::load(TWITTER_JSON).get(json))) {
std::cerr << error << std::endl;
state.SkipWithError("error loading");
return;
}
JsonBenchmark<T, Dom>(state, json);
}
} // namespace distinct_user_id
+89
View File
@@ -0,0 +1,89 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinctuserid.h"
namespace distinct_user_id {
using namespace simdjson;
simdjson_really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element);
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::array array) {
for (auto child : array) {
simdjson_recurse(v, child);
}
}
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::object object) {
for (auto [key, value] : object) {
if((key.size() == 4) && (memcmp(key.data(), "user", 4) == 0)) {
// we are in an object under the key "user"
simdjson::error_code error;
simdjson::dom::object child_object;
simdjson::dom::object child_array;
if (not (error = value.get(child_object))) {
for (auto [child_key, child_value] : child_object) {
if((child_key.size() == 2) && (memcmp(child_key.data(), "id", 2) == 0)) {
int64_t x;
if (not (error = child_value.get(x))) {
v.push_back(x);
}
}
simdjson_recurse(v, child_value);
}
} else if (not (error = value.get(child_array))) {
simdjson_recurse(v, child_array);
}
// end of: we are in an object under the key "user"
} else {
simdjson_recurse(v, value);
}
}
}
simdjson_really_inline void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
simdjson_unused simdjson::error_code error;
simdjson::dom::array array;
simdjson::dom::object object;
if (not (error = element.get(array))) {
simdjson_recurse(v, array);
} else if (not (error = element.get(object))) {
simdjson_recurse(v, object);
}
}
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<int64_t> &Result() { return ids; }
simdjson_really_inline size_t ItemCount() { return ids.size(); }
private:
dom::parser parser{};
std::vector<int64_t> ids{};
};
void print_vec(const std::vector<int64_t> &v) {
for (auto i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
}
simdjson_really_inline bool Dom::Run(const padded_string &json) {
ids.clear();
dom::element doc = parser.parse(json);
simdjson_recurse(ids, doc);
remove_duplicates(ids);
return true;
}
BENCHMARK_TEMPLATE(DistinctUserID, Dom);
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "distinctuserid.h"
namespace distinct_user_id {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
public:
OnDemand() {
if(!displayed_implementation) {
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
displayed_implementation = true;
}
}
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<int64_t> &Result() { return ids; }
simdjson_really_inline size_t ItemCount() { return ids.size(); }
private:
ondemand::parser parser{};
std::vector<int64_t> ids{};
static inline bool displayed_implementation = false;
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
ids.clear();
// Walk the document, parsing as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc["statuses"]) {
// We believe that all statuses have a matching
// user, and we are willing to throw when they do not:
//
// You might think that you do not need the braces, but
// you do, otherwise you will get the wrong answer. That is
// because you can only have one active object or array
// at a time.
{
ondemand::object user = tweet["user"];
int64_t id = user["id"];
ids.push_back(id);
}
// Not all tweets have a "retweeted_status", but when they do
// we want to go and find the user within.
auto retweet = tweet["retweeted_status"];
if(!retweet.error()) {
ondemand::object retweet_content = retweet;
ondemand::object reuser = retweet_content["user"];
int64_t rid = reuser["id"];
ids.push_back(rid);
}
}
remove_duplicates(ids);
return true;
}
BENCHMARK_TEMPLATE(DistinctUserID, OnDemand);
} // namespace distinct_user_id
#endif // SIMDJSON_EXCEPTIONS
+110 -51
View File
@@ -1,9 +1,13 @@
#include "simdjson/jsonparser.h"
#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"
@@ -13,6 +17,8 @@
#include "sajson.h"
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
bool equals(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; }
@@ -30,52 +36,103 @@ void print_vec(const std::vector<int64_t> &v) {
std::cout << std::endl;
}
void simdjson_scan(std::vector<int64_t> &answer,
simdjson::ParsedJson::Iterator &i) {
while (i.move_forward()) {
if (i.get_scope_type() == '{') {
bool found_user = (i.get_string_length() == 4) &&
(memcmp(i.get_string(), "user", 4) == 0);
i.move_to_value();
if (found_user) {
if (i.is_object() && i.move_to_key("id", 2)) {
if (i.is_integer()) {
answer.push_back(i.get_integer());
}
i.up();
}
// 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);
}
}
__attribute__((noinline)) std::vector<int64_t>
simdjson_just_dom(simdjson::ParsedJson &pj) {
simdjson_really_inline std::vector<int64_t>
simdjson_just_dom(simdjson::dom::element doc) {
std::vector<int64_t> answer;
simdjson::ParsedJson::Iterator i(pj);
simdjson_scan(answer, i);
simdjson_recurse(answer, doc);
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
simdjson_really_inline std::vector<int64_t>
simdjson_compute_stats(const simdjson::padded_string &p) {
std::vector<int64_t> answer;
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
if (!pj.is_valid()) {
return 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);
}
simdjson::ParsedJson::Iterator i(pj);
simdjson_scan(answer, i);
remove_duplicates(answer);
return answer;
}
__attribute__((noinline)) bool
simdjson_really_inline simdjson::error_code
simdjson_just_parse(const simdjson::padded_string &p) {
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
bool answer = !pj.is_valid();
return answer;
simdjson::dom::parser parser;
return parser.parse(p).error();
}
void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
@@ -130,7 +187,7 @@ void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
}
}
__attribute__((noinline)) std::vector<int64_t>
simdjson_really_inline std::vector<int64_t>
sasjon_just_dom(sajson::document &d) {
std::vector<int64_t> answer;
sajson_traverse(answer, d.get_root());
@@ -138,7 +195,7 @@ sasjon_just_dom(sajson::document &d) {
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
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());
@@ -155,7 +212,7 @@ sasjon_compute_stats(const simdjson::padded_string &p) {
return answer;
}
__attribute__((noinline)) bool
simdjson_really_inline bool
sasjon_just_parse(const simdjson::padded_string &p) {
char *buffer = (char *)malloc(p.size());
memcpy(buffer, p.data(), p.size());
@@ -206,7 +263,7 @@ void rapid_traverse(std::vector<int64_t> &answer, const rapidjson::Value &v) {
}
}
__attribute__((noinline)) std::vector<int64_t>
simdjson_really_inline std::vector<int64_t>
rapid_just_dom(rapidjson::Document &d) {
std::vector<int64_t> answer;
rapid_traverse(answer, d);
@@ -214,7 +271,7 @@ rapid_just_dom(rapidjson::Document &d) {
return answer;
}
__attribute__((noinline)) std::vector<int64_t>
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);
@@ -232,7 +289,7 @@ rapid_compute_stats(const simdjson::padded_string &p) {
return answer;
}
__attribute__((noinline)) bool
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());
@@ -275,19 +332,18 @@ int main(int argc, char *argv[]) {
<< std::endl;
}
simdjson::padded_string p;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
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() > 1024 * 1024)
std::cout << p.size() / (1024 * 1024) << " MB ";
else if (p.size() > 1024)
std::cout << p.size() / 1024 << " KB ";
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;
@@ -312,27 +368,30 @@ int main(int argc, char *argv[]) {
size_t size = s1.size();
int repeat = 500;
int volume = p.size();
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,
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), false, , repeat,
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::ParsedJson dsimdjson = simdjson::build_parsed_json(p);
BEST_TIME("simdjson (just dom) ", simdjson_just_dom(dsimdjson).size(), size,
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());
char *buffer = (char *)malloc(p.size() + 1);
buffer[p.size()] = '\0';
memcpy(buffer, p.data(), p.size());
rapidjson::Document drapid;
drapid.ParseInsitu<kParseValidateEncodingFlag>(buffer);
+152
View File
@@ -0,0 +1,152 @@
#ifndef __EVENT_COUNTER_H
#define __EVENT_COUNTER_H
#include <cassert>
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <unistd.h>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#include "simdjson.h"
using std::string;
using std::vector;
using std::chrono::steady_clock;
using std::chrono::time_point;
using std::chrono::duration;
struct event_count {
duration<double> elapsed;
vector<unsigned long long> event_counts;
event_count() : elapsed(0), event_counts{0,0,0,0,0} {}
event_count(const duration<double> _elapsed, const vector<unsigned long long> _event_counts) : elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count& other): elapsed(other.elapsed), event_counts(other.event_counts) { }
// The types of counters (so we can read the getter more easily)
enum event_counter_types {
CPU_CYCLES,
INSTRUCTIONS,
BRANCH_MISSES,
CACHE_REFERENCES,
CACHE_MISSES
};
double elapsed_sec() const { return duration<double>(elapsed).count(); }
double elapsed_ns() const { return duration<double, std::nano>(elapsed).count(); }
double cycles() const { return static_cast<double>(event_counts[CPU_CYCLES]); }
double instructions() const { return static_cast<double>(event_counts[INSTRUCTIONS]); }
double branch_misses() const { return static_cast<double>(event_counts[BRANCH_MISSES]); }
double cache_references() const { return static_cast<double>(event_counts[CACHE_REFERENCES]); }
double cache_misses() const { return static_cast<double>(event_counts[CACHE_MISSES]); }
event_count& operator=(const event_count& other) {
this->elapsed = other.elapsed;
this->event_counts = other.event_counts;
return *this;
}
event_count operator+(const event_count& other) const {
return event_count(elapsed+other.elapsed, {
event_counts[0]+other.event_counts[0],
event_counts[1]+other.event_counts[1],
event_counts[2]+other.event_counts[2],
event_counts[3]+other.event_counts[3],
event_counts[4]+other.event_counts[4],
});
}
void operator+=(const event_count& other) {
*this = *this + other;
}
};
struct event_aggregate {
int iterations = 0;
event_count total{};
event_count best{};
event_count worst{};
event_aggregate() {}
void operator<<(const event_count& other) {
if (iterations == 0 || other.elapsed < best.elapsed) {
best = other;
}
if (iterations == 0 || other.elapsed > worst.elapsed) {
worst = other;
}
iterations++;
total += other;
}
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
double cycles() const { return total.cycles() / iterations; }
double instructions() const { return total.instructions() / iterations; }
double branch_misses() const { return total.branch_misses() / iterations; }
double cache_references() const { return total.cache_references() / iterations; }
double cache_misses() const { return total.cache_misses() / iterations; }
};
struct event_collector {
event_count count{};
time_point<steady_clock> start_clock{};
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector(bool quiet = false) : linux_events(vector<int>{
PERF_COUNT_HW_CPU_CYCLES,
PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_MISSES,
PERF_COUNT_HW_CACHE_REFERENCES,
PERF_COUNT_HW_CACHE_MISSES
}, quiet) {}
bool has_events() {
return linux_events.is_working();
}
#else
event_collector(simdjson_unused bool _quiet = false) {}
bool has_events() {
return false;
}
#endif
simdjson_really_inline void start() {
#if defined(__linux)
linux_events.start();
#endif
start_clock = steady_clock::now();
}
simdjson_really_inline event_count& end() {
time_point<steady_clock> end_clock = steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
#endif
count.elapsed = end_clock - start_clock;
return count;
}
};
#endif
+57
View File
@@ -0,0 +1,57 @@
#include "simdjson.h"
#include <chrono>
#include <cstring>
#include <iostream>
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
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);
std::chrono::time_point<std::chrono::steady_clock> end_clock =
std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed = end_clock - start_clock;
return (static_cast<double>(p.size()) / (1000000000.)) / elapsed.count();
}
int main(int argc, char *argv[]) {
int optind = 1;
if (optind >= argc) {
std::cerr << "Reads document as far as possible. " << std::endl;
std::cerr << "Usage: " << argv[0] << " <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;
bench(filename, p);
double meanval = 0;
double maxval = 0;
double minval = 10000;
std::cout << "file size: "<< (static_cast<double>(p.size()) / (1000000000.)) << " GB" <<std::endl;
size_t times = p.size() > 1000000000 ? 5 : 50;
#if __cpp_exceptions
try {
#endif
for(size_t i = 0; i < times; i++) {
double tval = bench(filename, p);
if(maxval < tval) maxval = tval;
if(minval > tval) minval = tval;
meanval += tval;
}
#if __cpp_exceptions
} catch (const std::exception &) { // caught by reference to base
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
#endif
std::cout << "average speed: " << meanval / static_cast<double>(times) << " GB/s"<< std::endl;
std::cout << "min speed : " << minval << " GB/s" << std::endl;
std::cout << "max speed : " << maxval << " GB/s" << std::endl;
return EXIT_SUCCESS;
}
+80
View File
@@ -0,0 +1,80 @@
#pragma once
template<typename B, typename R> static void JsonBenchmark(benchmark::State &state, const simdjson::padded_string &json) {
event_collector collector(true);
event_aggregate events;
// Warmup and equality check (make sure the data is right!)
B bench;
if (!bench.Run(json)) { state.SkipWithError("warmup tweet reading failed"); return; }
{
R reference;
if (!reference.Run(json)) { state.SkipWithError("reference tweet reading failed"); return; }
if (bench.Result() != reference.Result()) { state.SkipWithError("results are not the same"); return; }
}
// Run the benchmark
for (simdjson_unused auto _ : state) {
collector.start();
if (!bench.Run(json)) { state.SkipWithError("tweet reading failed"); return; }
events << collector.end();
}
state.SetBytesProcessed(json.size() * state.iterations());
state.SetItemsProcessed(bench.ItemCount() * state.iterations());
state.counters["best_bytes_per_sec"] = benchmark::Counter(double(json.size()) / events.best.elapsed_sec());
state.counters["best_items_per_sec"] = benchmark::Counter(double(bench.ItemCount()) / events.best.elapsed_sec());
state.counters["docs_per_sec"] = benchmark::Counter(1.0, benchmark::Counter::kIsIterationInvariantRate);
state.counters["best_docs_per_sec"] = benchmark::Counter(1.0 / events.best.elapsed_sec());
if (collector.has_events()) {
state.counters["instructions"] = events.instructions();
state.counters["cycles"] = events.cycles();
state.counters["branch_miss"] = events.branch_misses();
state.counters["cache_miss"] = events.cache_misses();
state.counters["cache_ref"] = events.cache_references();
state.counters["instructions_per_byte"] = events.instructions() / double(json.size());
state.counters["instructions_per_cycle"] = events.instructions() / events.cycles();
state.counters["cycles_per_byte"] = events.cycles() / double(json.size());
state.counters["frequency"] = benchmark::Counter(events.cycles(), benchmark::Counter::kIsIterationInvariantRate);
state.counters["best_instructions"] = events.best.instructions();
state.counters["best_cycles"] = events.best.cycles();
state.counters["best_branch_miss"] = events.best.branch_misses();
state.counters["best_cache_miss"] = events.best.cache_misses();
state.counters["best_cache_ref"] = events.best.cache_references();
state.counters["best_instructions_per_byte"] = events.best.instructions() / double(json.size());
state.counters["best_instructions_per_cycle"] = events.best.instructions() / events.best.cycles();
state.counters["best_cycles_per_byte"] = events.best.cycles() / double(json.size());
state.counters["best_frequency"] = events.best.cycles() / events.best.elapsed_sec();
}
state.counters["bytes"] = benchmark::Counter(double(json.size()));
state.counters["items"] = benchmark::Counter(double(bench.ItemCount()));
// Build the label
using namespace std;
stringstream label;
label << fixed << setprecision(2);
label << "[best:";
label << " throughput=" << setw(6) << (double(json.size()) / 1000000000.0 / events.best.elapsed_sec()) << " GB/s";
label << " doc_throughput=" << setw(6) << uint64_t(1.0 / events.best.elapsed_sec()) << " docs/s";
if (collector.has_events()) {
label << " instructions=" << setw(12) << uint64_t(events.best.instructions()) << setw(0);
label << " cycles=" << setw(12) << uint64_t(events.best.cycles()) << setw(0);
label << " branch_miss=" << setw(8) << uint64_t(events.best.branch_misses()) << setw(0);
label << " cache_miss=" << setw(8) << uint64_t(events.best.cache_misses()) << setw(0);
label << " cache_ref=" << setw(10) << uint64_t(events.best.cache_references()) << setw(0);
}
label << " items=" << setw(10) << bench.ItemCount() << setw(0);
label << " avg_time=" << setw(10) << uint64_t(events.elapsed_ns()) << setw(0) << " ns";
label << "]";
state.SetLabel(label.str());
}
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
dom::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
container.clear();
for (auto point : parser.parse(json)["coordinates"]) {
container.emplace_back(my_point{point["x"], point["y"], point["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, Dom);
namespace sum {
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
dom::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
sum = { 0, 0, 0 };
count = 0;
for (auto coord : parser.parse(json)["coordinates"]) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, Dom);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
using namespace simdjson::builtin;
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
ondemand::parser parser{};
std::vector<my_point> container{};
simdjson_really_inline simdjson_result<double> first_double(ondemand::json_iterator &iter, const char *key) {
if (!iter.start_object() || ondemand::raw_json_string(iter.field_key()) != key || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
simdjson_really_inline simdjson_result<double> next_double(ondemand::json_iterator &iter, const char *key) {
if (!iter.has_next_field() || ondemand::raw_json_string(iter.field_key()) != key || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
container.clear();
using std::cerr;
using std::endl;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("coordinates")) { cerr << "find coordinates field failed" << endl; return false; }
if (iter.start_array()) {
do {
container.emplace_back(my_point{first_double(iter, "x"), next_double(iter, "y"), next_double(iter, "z")});
if (iter.skip_container()) { return false; } // Skip the rest of the coordinates object
} while (iter.has_next_element());
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, Iter);
namespace sum {
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("coordinates")) { return false; }
if (!iter.start_array()) { return false; }
do {
if (!iter.start_object() || !iter.find_field_raw("x")) { return false; }
sum.x += iter.consume_double();
if (!iter.has_next_field() || !iter.find_field_raw("y")) { return false; }
sum.y += iter.consume_double();
if (!iter.has_next_field() || !iter.find_field_raw("z")) { return false; }
sum.z += iter.consume_double();
if (iter.skip_container()) { return false; } // Skip the rest of the coordinates object
count++;
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, Iter);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+95
View File
@@ -0,0 +1,95 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
//
// Interface
//
namespace kostya {
template<typename T> static void Kostya(benchmark::State &state);
namespace sum {
template<typename T> static void KostyaSum(benchmark::State &state);
}
using namespace simdjson;
static void append_coordinate(std::default_random_engine &e, std::uniform_real_distribution<> &dis, std::stringstream &myss) {
using std::endl;
myss << R"( {)" << endl;
myss << R"( "x": )" << dis(e) << "," << endl;
myss << R"( "y": )" << dis(e) << "," << endl;
myss << R"( "z": )" << dis(e) << "," << endl;
myss << R"( "name": ")" << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << char('a'+dis(e)*25) << " " << int(dis(e)*10000) << "\"," << endl;
myss << R"( "opts": {)" << endl;
myss << R"( "1": [)" << endl;
myss << R"( 1,)" << endl;
myss << R"( true)" << endl;
myss << R"( ])" << endl;
myss << R"( })" << endl;
myss << R"( })";
}
static std::string build_json_array(size_t N) {
using namespace std;
default_random_engine e;
uniform_real_distribution<> dis(0, 1);
stringstream myss;
myss << R"({)" << endl;
myss << R"( "coordinates": [)" << endl;
for (size_t i=1; i<N; i++) {
append_coordinate(e, dis, myss); myss << "," << endl;
}
append_coordinate(e, dis, myss); myss << endl;
myss << R"( ],)" << endl;
myss << R"( "info": "some info")" << endl;
myss << R"(})" << endl;
string answer = myss.str();
cout << "Creating a source file spanning " << (answer.size() + 512) / 1024 << " KB " << endl;
return answer;
}
static const padded_string &get_built_json_array() {
static 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); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const my_point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
} // namespace kostya
//
// Implementation
//
#include <vector>
#include "event_counter.h"
#include "dom.h"
#include "json_benchmark.h"
namespace kostya {
template<typename T> static void Kostya(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
namespace sum {
template<typename T> static void KostyaSum(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
}
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "kostya.h"
namespace kostya {
using namespace simdjson;
using namespace simdjson::builtin;
class OnDemand {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
ondemand::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
container.clear();
using std::cout;
using std::endl;
auto doc = parser.iterate(json);
for (ondemand::object coord : doc["coordinates"]) {
container.emplace_back(my_point{coord["x"], coord["y"], coord["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(Kostya, OnDemand);
namespace sum {
class OnDemand {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto doc = parser.iterate(json);
for (ondemand::object coord : doc["coordinates"]) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(KostyaSum, OnDemand);
} // namespace sum
} // namespace kostya
#endif // SIMDJSON_EXCEPTIONS
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
using namespace simdjson;
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
dom::parser parser{};
std::vector<my_point> container{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
container.clear();
for (auto point : parser.parse(json)) {
container.emplace_back(my_point{point["x"], point["y"], point["z"]});
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, Dom);
namespace sum {
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
dom::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
sum = { 0, 0, 0 };
count = 0;
for (auto coord : parser.parse(json)) {
sum.x += double(coord["x"]);
sum.y += double(coord["y"]);
sum.z += double(coord["z"]);
count++;
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandomSum, Dom);
} // namespace sum
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+92
View File
@@ -0,0 +1,92 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
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 double first_double(ondemand::json_iterator &iter) {
if (iter.start_object().error() || iter.field_key().error() || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
simdjson_really_inline double next_double(ondemand::json_iterator &iter) {
if (!iter.has_next_field() || iter.field_key().error() || iter.field_value()) { throw "Invalid field"; }
return iter.consume_double();
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
container.clear();
auto iter = parser.iterate_raw(json).value();
if (iter.start_array()) {
do {
container.emplace_back(my_point{first_double(iter), next_double(iter), next_double(iter)});
if (iter.has_next_field()) { throw "Too many fields"; }
} while (iter.has_next_element());
}
return true;
}
BENCHMARK_TEMPLATE(LargeRandom, Iter);
namespace sum {
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline my_point &Result() { return sum; }
simdjson_really_inline size_t ItemCount() { return count; }
private:
ondemand::parser parser{};
my_point sum{};
size_t count{};
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
sum = {0,0,0};
count = 0;
auto iter = parser.iterate_raw(json).value();
if (!iter.start_array()) { return false; }
do {
if (!iter.start_object() || iter.field_key().value() != "x" || iter.field_value()) { return false; }
sum.x += iter.consume_double();
if (!iter.has_next_field() || iter.field_key().value() != "y" || iter.field_value()) { return false; }
sum.y += iter.consume_double();
if (!iter.has_next_field() || iter.field_key().value() != "z" || iter.field_value()) { return false; }
sum.z += iter.consume_double();
if (*iter.advance() != '}') { return false; }
count++;
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(LargeRandomSum, Iter);
} // namespace sum
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
//
// Interface
//
namespace largerandom {
template<typename T> static void LargeRandom(benchmark::State &state);
namespace sum {
template<typename T> static void LargeRandomSum(benchmark::State &state);
}
using namespace simdjson;
static std::string build_json_array(size_t N) {
std::default_random_engine e;
std::uniform_real_distribution<> dis(0, 1);
std::stringstream myss;
myss << "[" << std::endl;
if(N > 0) {
myss << "{ \"x\":" << dis(e) << ", \"y\":" << dis(e) << ", \"z\":" << dis(e) << "}" << std::endl;
}
for(size_t i = 1; i < N; i++) {
myss << "," << std::endl;
myss << "{ \"x\":" << dis(e) << ", \"y\":" << dis(e) << ", \"z\":" << dis(e) << "}";
}
myss << std::endl;
myss << "]" << std::endl;
std::string answer = myss.str();
std::cout << "Creating a source file spanning " << (answer.size() + 512) / 1024 << " KB " << std::endl;
return answer;
}
static const padded_string &get_built_json_array() {
static padded_string json = build_json_array(1000000);
return json;
}
struct my_point {
double x;
double y;
double z;
simdjson_really_inline bool operator==(const my_point &other) const {
return x == other.x && y == other.y && z == other.z;
}
simdjson_really_inline bool operator!=(const my_point &other) const { return !(*this == other); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const my_point &p) {
return o << p.x << "," << p.y << "," << p.z << std::endl;
}
} // namespace largerandom
//
// Implementation
//
#include <vector>
#include "event_counter.h"
#include "dom.h"
#include "json_benchmark.h"
namespace largerandom {
template<typename T> static void LargeRandom(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
namespace sum {
template<typename T> static void LargeRandomSum(benchmark::State &state) {
JsonBenchmark<T, Dom>(state, get_built_json_array());
}
}
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
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();
auto doc = parser.iterate(json);
for (ondemand::object coord : doc) {
container.emplace_back(my_point{coord["x"], coord["y"], coord["z"]});
}
return true;
}
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
+121
View File
@@ -0,0 +1,121 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "largerandom.h"
namespace largerandom {
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<my_point> &Result() { return container; }
simdjson_really_inline size_t ItemCount() { return container.size(); }
private:
simdjson_really_inline error_code RunNoExcept(const padded_string &json) noexcept;
error_code Allocate(size_t new_capacity);
std::unique_ptr<uint8_t[]> string_buf{};
size_t capacity{};
dom_parser_implementation dom_parser{};
std::vector<my_point> container{};
};
struct sax_point_reader_visitor {
public:
std::vector<my_point> &points;
enum {GOT_X=0, GOT_Y=1, GOT_Z=2, GOT_SOMETHING_ELSE=4};
size_t idx{GOT_SOMETHING_ELSE};
double buffer[3]={};
explicit sax_point_reader_visitor(std::vector<my_point> &_points) : points(_points) {}
simdjson_really_inline error_code visit_object_start(json_iterator &) {
idx = 0;
return SUCCESS;
}
simdjson_really_inline error_code visit_primitive(json_iterator &, const uint8_t *value) {
if(idx == GOT_SOMETHING_ELSE) { return simdjson::SUCCESS; }
return numberparsing::parse_double(value).get(buffer[idx]);
}
simdjson_really_inline error_code visit_object_end(json_iterator &) {
points.emplace_back(my_point{buffer[0], buffer[1], buffer[2]});
return SUCCESS;
}
simdjson_really_inline error_code visit_document_start(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_key(json_iterator &, const uint8_t * key) {
switch(key[1]) {
// Technically, we should check the other characters
// in the key, but we are cheating to go as fast
// as possible.
case 'x':
idx = GOT_X;
break;
case 'y':
idx = GOT_Y;
break;
case 'z':
idx = GOT_Z;
break;
default:
idx = GOT_SOMETHING_ELSE;
}
return SUCCESS;
}
simdjson_really_inline error_code visit_array_start(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_array_end(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_document_end(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_empty_array(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_empty_object(json_iterator &) { return SUCCESS; }
simdjson_really_inline error_code visit_root_primitive(json_iterator &, const uint8_t *) { return SUCCESS; }
simdjson_really_inline error_code increment_count(json_iterator &) { return SUCCESS; }
};
// NOTE: this assumes the dom_parser is already allocated
bool Sax::Run(const padded_string &json) noexcept {
auto error = RunNoExcept(json);
if (error) { std::cerr << error << std::endl; return false; }
return true;
}
error_code Sax::RunNoExcept(const padded_string &json) noexcept {
container.clear();
// Allocate capacity if needed
if (capacity < json.size()) {
SIMDJSON_TRY( Allocate(json.size()) );
}
// Run stage 1 first.
SIMDJSON_TRY( dom_parser.stage1(json.u8data(), json.size(), false) );
// Then walk the document, parsing the tweets as we go
json_iterator iter(dom_parser, 0);
sax_point_reader_visitor visitor(container);
SIMDJSON_TRY( iter.walk_document<false>(visitor) );
return SUCCESS;
}
error_code Sax::Allocate(size_t new_capacity) {
// string_capacity copied from document::allocate
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
if (capacity == 0) { // set max depth the first time only
if (auto error = dom_parser.set_max_depth(DEFAULT_MAX_DEPTH)) { return error; }
}
capacity = new_capacity;
return SUCCESS;
}
BENCHMARK_TEMPLATE(LargeRandom, Sax);
} // namespace largerandom
#endif // SIMDJSON_EXCEPTIONS
+42 -22
View File
@@ -1,14 +1,22 @@
// https://github.com/WojciechMula/toys/blob/master/000helpers/linux-perf-events.h
#pragma once
#ifdef __linux__
#ifdef __has_include
#if __has_include(<asm/unistd.h>)
#include <asm/unistd.h> // for __NR_perf_event_open
#else
#warning "Header asm/unistd.h cannot be found though it is a linux system. Are linux headers missing?"
#endif
#else // no __has_include
// Please insure that linux headers have been installed.
#include <asm/unistd.h> // for __NR_perf_event_open
#endif
#include <linux/perf_event.h> // for perf event constants
#include <sys/ioctl.h> // for ioctl
#include <unistd.h> // for syscall
#include <cerrno> // for errno
#include <cstring> // for memset
#include <cstring> // for std::memset
#include <stdexcept>
#include <iostream>
@@ -17,14 +25,15 @@
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
int fd;
bool working;
perf_event_attr attribs;
int num_events;
std::vector<uint64_t> temp_result_vec;
std::vector<uint64_t> ids;
perf_event_attr attribs{};
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> ids{};
bool quiet;
public:
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
explicit LinuxEvents(std::vector<int> config_vec, bool _quiet=false) : fd(0), working(true), quiet{_quiet} {
std::memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE;
attribs.size = sizeof(attribs);
attribs.disabled = 1;
@@ -43,7 +52,7 @@ public:
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
fd = syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags);
fd = static_cast<int>(syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (fd == -1) {
report_error("perf_event_open");
}
@@ -56,25 +65,29 @@ public:
temp_result_vec.resize(num_events * 2 + 1);
}
~LinuxEvents() { close(fd); }
~LinuxEvents() { if (fd != -1) { close(fd); } }
inline void start() {
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_RESET)");
}
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_RESET)");
}
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
}
}
}
inline void end(std::vector<unsigned long long> &results) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
}
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
}
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
report_error("read");
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
report_error("read");
}
}
// our actual results are in slots 1,3,5, ... of this structure
// we really should be checking our ids obtained earlier to be safe
@@ -83,10 +96,17 @@ public:
}
}
bool is_working() {
return working;
}
private:
void report_error(const std::string &context) {
if (working)
std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
if (!quiet) {
if (working) {
std::cerr << (context + ": " + std::string(strerror(errno))) << std::endl;
}
}
working = false;
}
};
+61 -46
View File
@@ -2,9 +2,9 @@
#include <unistd.h>
#include "benchmark.h"
#include "simdjson/jsonioutil.h"
#include "simdjson/jsonminifier.h"
#include "simdjson/jsonparser.h"
#include "simdjson.h"
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
// #define RAPIDJSON_SSE2 // bad
// #define RAPIDJSON_SSE42 // bad
@@ -14,8 +14,10 @@
#include "rapidjson/writer.h"
#include "sajson.h"
using namespace simdjson;
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
using namespace simdjson;
std::string rapid_stringme_insitu(char *json) {
Document d;
@@ -43,6 +45,17 @@ std::string rapid_stringme(char *json) {
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;
@@ -65,28 +78,32 @@ int main(int argc, char *argv[]) {
}
const char *filename = argv[optind];
simdjson::padded_string p;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
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() > 1024 * 1024)
std::cout << p.size() / (1024 * 1024) << " MB ";
else if (p.size() > 1024)
std::cout << p.size() / 1024 << " KB ";
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::allocate_padded_buffer(p.size() + 1);
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;
int volume = p.size();
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");
@@ -101,20 +118,25 @@ int main(int argc, char *argv[]) {
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 = simdjson::json_minify((const uint8_t *)buffer, p.size(),
(uint8_t *)buffer);
if (verbose)
std::cout << "json_minify length is " << outlength << std::endl;
size_t outlength;
uint8_t *cbuffer = (uint8_t *)buffer;
BEST_TIME("json_minify", simdjson::json_minify(cbuffer, p.size(), cbuffer),
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(), outlength * 100.0 / p.size());
outlength, p.size(), static_cast<double>(outlength) * 100.0 / static_cast<double>(p.size()));
/***
* Is it worth it to minify before parsing?
@@ -124,9 +146,14 @@ int main(int argc, char *argv[]) {
false, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
char *mini_buffer = simdjson::allocate_padded_buffer(p.size() + 1);
size_t minisize = simdjson::json_minify((const uint8_t *)p.data(), p.size(),
(uint8_t *)mini_buffer);
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(),
@@ -150,31 +177,19 @@ int main(int argc, char *argv[]) {
.is_valid(),
true, memcpy(buffer, mini_buffer, p.size()), repeat, volume, !just_data);
simdjson::ParsedJson pj;
bool is_alloc_ok = pj.allocate_capacity(p.size(), 1024);
if (!is_alloc_ok) {
fprintf(stderr, "failed to allocate memory\n");
return EXIT_FAILURE;
}
simdjson::dom::parser parser;
bool automated_reallocation = false;
BEST_TIME("simdjson orig",
simdjson::json_parse((const uint8_t *)buffer, p.size(), pj,
automated_reallocation),
true, memcpy(buffer, p.data(), p.size()), repeat, volume,
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);
simdjson::ParsedJson pj2;
bool is_alloc_ok2 = pj2.allocate_capacity(p.size(), 1024);
if (!is_alloc_ok2) {
fprintf(stderr, "failed to allocate memory\n");
return EXIT_FAILURE;
}
automated_reallocation = false;
BEST_TIME("simdjson despaced",
simdjson::json_parse((const uint8_t *)buffer, minisize, pj2,
automated_reallocation),
true, memcpy(buffer, mini_buffer, p.size()), repeat, volume,
!just_data);
free(buffer);
free(ast_buffer);
free(mini_buffer);
+181 -348
View File
@@ -1,9 +1,11 @@
#include "event_counter.h"
#include <cassert>
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#include <unistd.h>
#endif
#include <unistd.h>
#include <cinttypes>
#include <cstdio>
@@ -26,368 +28,199 @@
#ifdef __linux__
#include <libgen.h>
#endif
//#define DEBUG
#include "simdjson/common_defs.h"
#include "simdjson/isadetection.h"
#include "simdjson/jsonioutil.h"
#include "simdjson/jsonparser.h"
#include "simdjson/parsedjson.h"
#include "simdjson/stage1_find_marks.h"
#include "simdjson/stage2_build_tape.h"
namespace simdjson {
Architecture _find_best_supported_implementation() {
constexpr uint32_t haswell_flags =
instruction_set::AVX2 | instruction_set::PCLMULQDQ |
instruction_set::BMI1 | instruction_set::BMI2;
constexpr uint32_t westmere_flags =
instruction_set::SSE42 | instruction_set::PCLMULQDQ;
uint32_t supports = detect_supported_architectures();
// Order from best to worst (within architecture)
if ((haswell_flags & supports) == haswell_flags) {
return Architecture::HASWELL;
}
if ((westmere_flags & supports) == westmere_flags) {
return Architecture::WESTMERE;
}
if (instruction_set::NEON)
return Architecture::ARM64;
return Architecture::NONE;
#include "simdjson.h"
#include <functional>
#include "benchmarker.h"
using namespace simdjson;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::to_string;
using std::vector;
using std::ostream;
using std::ofstream;
using std::exception;
// Stash the exe_name in main() for functions to use
char* exe_name;
void print_usage(ostream& out) {
out << "Usage: " << exe_name << " [-vt] [-n #] [-s STAGE] [-a ARCH] <jsonfile> ..." << endl;
out << endl;
out << "Runs the parser against the given json files in a loop, measuring speed and other statistics." << endl;
out << endl;
out << "Options:" << endl;
out << endl;
out << "-n # - Number of iterations per file. Default: 200" << endl;
out << "-i # - Number of times to iterate a single file before moving to the next. Default: 20" << endl;
out << "-t - Tabbed data output" << endl;
out << "-v - Verbose output." << endl;
out << "-s stage1 - Stop after find_structural_bits." << endl;
out << "-s all - Run all stages." << endl;
out << "-C - Leave the buffers cold (includes page allocation and related OS tasks during parsing, speed tied to OS performance)" << endl;
out << "-H - Make the buffers hot (reduce page allocation and related OS tasks during parsing) [default]" << endl;
out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << endl;
out << " implementation supported on the host machine." << endl;
for (auto impl : simdjson::available_implementations) {
if(impl->supported_by_runtime_system()) {
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
}
}
}
using unified_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj);
using stage1_functype = int(const uint8_t *buf, size_t len, ParsedJson &pj);
extern unified_functype *unified_ptr;
extern stage1_functype *stage1_ptr;
int unified_machine_dispatch(const uint8_t *buf, size_t len, ParsedJson &pj) {
Architecture best_implementation = _find_best_supported_implementation();
// Selecting the best implementation
switch (best_implementation) {
#ifdef IS_X86_64
case Architecture::HASWELL:
unified_ptr = &unified_machine<Architecture::HASWELL>;
break;
case Architecture::WESTMERE:
unified_ptr = &unified_machine<Architecture::WESTMERE>;
break;
#endif
#ifdef IS_ARM64
case Architecture::ARM64:
unified_ptr = &unified_machine<Architecture::ARM64>;
break;
#endif
default:
std::cerr << "The processor is not supported by simdjson." << std::endl;
return simdjson::UNEXPECTED_ERROR;
}
return unified_ptr(buf, len, pj);
void exit_usage(string message) {
cerr << message << endl;
cerr << endl;
print_usage(cerr);
exit(EXIT_FAILURE);
}
// Responsible to select the best json_parse implementation
int find_structural_bits_dispatch(const uint8_t *buf, size_t len,
ParsedJson &pj) {
Architecture best_implementation = _find_best_supported_implementation();
// Selecting the best implementation
switch (best_implementation) {
#ifdef IS_X86_64
case Architecture::HASWELL:
stage1_ptr = &find_structural_bits<Architecture::HASWELL>;
break;
case Architecture::WESTMERE:
stage1_ptr = &find_structural_bits<Architecture::WESTMERE>;
break;
#endif
#ifdef IS_ARM64
case Architecture::ARM64:
stage1_ptr = &find_structural_bits<Architecture::ARM64>;
break;
#endif
default:
std::cerr << "The processor is not supported by simdjson." << std::endl;
return simdjson::UNEXPECTED_ERROR;
}
struct option_struct {
vector<char*> files{};
bool stage1_only = false;
return stage1_ptr(buf, len, pj);
}
int32_t iterations = 200;
int32_t iteration_step = -1;
stage1_functype *stage1_ptr = &find_structural_bits_dispatch;
unified_functype *unified_ptr = &unified_machine_dispatch;
} // namespace simdjson
int main(int argc, char *argv[]) {
bool verbose = false;
bool dump = false;
bool json_output = false;
bool force_one_iteration = false;
bool just_data = false;
#ifndef _MSC_VER
int c;
bool tabbed_output = false;
/**
* Benchmarking on a cold parser instance means that the parsing may include
* memory allocation at the OS level. This may lead to apparently odd results
* such that higher speed under the Windows Subsystem for Linux than under the
* regular Windows, for the same machine. It is arguably misleading to benchmark
* how the OS allocates memory, when we really want to just benchmark simdjson.
*/
bool hotbuffers = true;
while ((c = getopt(argc, argv, "1vdt")) != -1) {
switch (c) {
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
case 'd':
dump = true;
break;
case 'j':
json_output = true;
break;
case '1':
force_one_iteration = true;
break;
default:
abort();
}
}
#else
int optind = 1;
#endif
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <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;
}
if (verbose) {
std::cout << "[verbose] loading " << filename << std::endl;
}
simdjson::padded_string p;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
if (verbose) {
std::cout << "[verbose] loaded " << filename << " (" << p.size()
<< " bytes)" << std::endl;
}
#if defined(DEBUG)
const uint32_t iterations = 1;
#else
const uint32_t iterations =
force_one_iteration ? 1 : (p.size() < 1 * 1000 * 1000 ? 1000 : 10);
#endif
std::vector<double> res;
res.resize(iterations);
if (!just_data)
printf("number of iterations %u \n", iterations);
#if !defined(__linux__)
#define SQUASH_COUNTERS
if (just_data) {
printf("just_data (-t) flag only works under linux.\n");
}
#endif
{ // practice run
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (allocok) {
simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj);
simdjson::unified_ptr(
(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t
*)(const uint8_t *)
p.data(),
p.size(), pj);
}
}
#ifndef SQUASH_COUNTERS
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;
results.resize(evts.size());
unsigned long cy0 = 0, cy1 = 0, cy2 = 0;
unsigned long cl0 = 0, cl1 = 0, cl2 = 0;
unsigned long mis0 = 0, mis1 = 0, mis2 = 0;
unsigned long cref0 = 0, cref1 = 0, cref2 = 0;
unsigned long cmis0 = 0, cmis1 = 0, cmis2 = 0;
#endif
bool isok = true;
#ifndef SQUASH_COUNTERS
for (uint32_t i = 0; i < iterations; i++) {
if (verbose) {
std::cout << "[verbose] iteration # " << i << std::endl;
}
unified.start();
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
return EXIT_FAILURE;
}
unified.end(results);
cy0 += results[0];
cl0 += results[1];
mis0 += results[2];
cref0 += results[3];
cmis0 += results[4];
if (verbose) {
std::cout << "[verbose] allocated memory for parsed JSON " << std::endl;
}
unified.start();
isok = (simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj) ==
simdjson::SUCCESS);
unified.end(results);
cy1 += results[0];
cl1 += results[1];
mis1 += results[2];
cref1 += results[3];
cmis1 += results[4];
if (!isok) {
std::cout << "Failed during stage 1" << std::endl;
break;
}
unified.start();
isok = isok &&
(simdjson::SUCCESS ==
simdjson::unified_ptr((const uint8_t *)p.data(), p.size(), pj));
unified.end(results);
cy2 += results[0];
cl2 += results[1];
mis2 += results[2];
cref2 += results[3];
cmis2 += results[4];
if (!isok) {
std::cout << "Failed during stage 2" << std::endl;
break;
}
}
#endif
// we do it again, this time just measuring the elapsed time
for (uint32_t i = 0; i < iterations; i++) {
if (verbose) {
std::cout << "[verbose] iteration # " << i << std::endl;
}
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
return EXIT_FAILURE;
}
if (verbose) {
std::cout << "[verbose] allocated memory for parsed JSON " << std::endl;
}
option_struct(int argc, char **argv) {
int c;
auto start = std::chrono::steady_clock::now();
isok = (simdjson::stage1_ptr((const uint8_t *)p.data(), p.size(), pj) ==
simdjson::SUCCESS);
isok = isok &&
(simdjson::SUCCESS ==
simdjson::unified_ptr((const uint8_t *)p.data(), p.size(), pj));
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> secs = end - start;
res[i] = secs.count();
if (!isok) {
std::cerr << pj.get_error_message() << std::endl;
std::cerr << "Could not parse. " << std::endl;
return EXIT_FAILURE;
}
}
simdjson::ParsedJson pj =
build_parsed_json(p); // do the parsing again to get the stats
if (!pj.is_valid()) {
std::cerr << pj.get_error_message() << std::endl;
std::cerr << "Could not parse. " << std::endl;
return EXIT_FAILURE;
}
double min_result = *min_element(res.begin(), res.end());
double speedinGBs = (p.size()) / (min_result * 1000000000.0);
#ifndef SQUASH_COUNTERS
unsigned long total = cy0 + cy1 + cy2;
if (just_data) {
float cpb0 = (double)cy0 / (iterations * p.size());
float cpb1 = (double)cy1 / (iterations * p.size());
float cpb2 = (double)cy2 / (iterations * p.size());
float cpbtotal = (double)total / (iterations * p.size());
char *newfile = (char *)malloc(strlen(filename) + 1);
if (newfile == NULL) {
return EXIT_FAILURE;
}
::strcpy(newfile, filename);
char *snewfile = ::basename(newfile);
size_t nl = strlen(snewfile);
for (size_t j = nl - 1; j > 0; j--) {
if (snewfile[j] == '.') {
snewfile[j] = '\0';
while ((c = getopt(argc, argv, "vtn:i:a:s:HC")) != -1) {
switch (c) {
case 'n':
iterations = atoi(optarg);
break;
case 'i':
iteration_step = atoi(optarg);
break;
case 't':
tabbed_output = true;
break;
case 'v':
verbose = true;
break;
case 'a': {
const implementation *impl = simdjson::available_implementations[optarg];
if ((!impl) || (!impl->supported_by_runtime_system())) {
std::string exit_message = string("Unsupported option value -a ") + optarg + ": expected -a with one of ";
for (auto imple : simdjson::available_implementations) {
if(imple->supported_by_runtime_system()) {
exit_message += imple->name();
exit_message += " ";
}
}
exit_usage(exit_message);
}
simdjson::active_implementation = impl;
break;
}
case 'C':
hotbuffers = false;
break;
case 'H':
hotbuffers = true;
break;
case 's':
if (!strcmp(optarg, "stage1")) {
stage1_only = true;
} else if (!strcmp(optarg, "all")) {
stage1_only = false;
} else {
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
}
break;
default:
// reaching here means an argument was given to getopt() which did not have a case label
exit_usage("Unexpected argument - missing case for option "+
std::string(1,static_cast<char>(c))+
" (programming error)");
}
}
if (iteration_step == -1) {
iteration_step = iterations / 50;
if (iteration_step < 200) { iteration_step = 200; }
if (iteration_step > iterations) { iteration_step = iterations; }
}
// All remaining arguments are considered to be files
for (int i=optind; i<argc; i++) {
files.push_back(argv[i]);
}
if (files.empty()) {
exit_usage("No files specified");
}
}
};
int main(int argc, char *argv[]) {
// Read options
exe_name = argv[0];
option_struct options(argc, argv);
if (options.verbose) {
verbose_stream = &cout;
verbose() << "Implementation: " << simdjson::active_implementation->name() << endl;
}
// Start collecting events. We put this early so if it prints an error message, it's the
// first thing printed.
event_collector collector;
// Print preamble
if (!options.tabbed_output) {
printf("number of iterations %u \n", options.iterations);
}
// Set up benchmarkers by reading all files
vector<benchmarker*> benchmarkers;
for (size_t i=0; i<options.files.size(); i++) {
benchmarkers.push_back(new benchmarker(options.files[i], collector));
}
// Run the benchmarks
progress_bar progress(options.iterations, 50);
// Put the if (options.stage1_only) *outside* the loop so that run_iterations will be optimized
if (options.stage1_only) {
for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
if (!options.verbose) { progress.print(iteration); }
// Benchmark each file once per iteration
for (size_t f=0; f<options.files.size(); f++) {
verbose() << "[verbose] " << benchmarkers[f]->filename << " iterations #" << iteration << "-" << (iteration+options.iteration_step-1) << endl;
benchmarkers[f]->run_iterations(options.iteration_step, true, options.hotbuffers);
}
}
printf("\"%s\"\t%f\t%f\t%f\t%f\t%f\n", snewfile, cpb0, cpb1, cpb2, cpbtotal,
speedinGBs);
free(newfile);
} else {
printf("number of bytes %ld number of structural chars %u ratio %.3f\n",
p.size(), pj.n_structural_indexes,
(double)pj.n_structural_indexes / p.size());
printf("mem alloc instructions: %10lu cycles: %10lu (%.2f %%) ins/cycles: "
"%.2f mis. branches: %10lu (cycles/mis.branch %.2f) cache accesses: "
"%10lu (failure %10lu)\n",
cl0 / iterations, cy0 / iterations, 100. * cy0 / total,
(double)cl0 / cy0, mis0 / iterations, (double)cy0 / mis0,
cref1 / iterations, cmis0 / iterations);
printf(" mem alloc runs at %.2f cycles per input byte.\n",
(double)cy0 / (iterations * p.size()));
printf("stage 1 instructions: %10lu cycles: %10lu (%.2f %%) ins/cycles: "
"%.2f mis. branches: %10lu (cycles/mis.branch %.2f) cache accesses: "
"%10lu (failure %10lu)\n",
cl1 / iterations, cy1 / iterations, 100. * cy1 / total,
(double)cl1 / cy1, mis1 / iterations, (double)cy1 / mis1,
cref1 / iterations, cmis1 / iterations);
printf(" stage 1 runs at %.2f cycles per input byte.\n",
(double)cy1 / (iterations * p.size()));
for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
if (!options.verbose) { progress.print(iteration); }
// Benchmark each file once per iteration
for (size_t f=0; f<options.files.size(); f++) {
verbose() << "[verbose] " << benchmarkers[f]->filename << " iterations #" << iteration << "-" << (iteration+options.iteration_step-1) << endl;
benchmarkers[f]->run_iterations(options.iteration_step, false, options.hotbuffers);
}
}
}
if (!options.verbose) { progress.erase(); }
printf("stage 2 instructions: %10lu cycles: %10lu (%.2f %%) ins/cycles: "
"%.2f mis. branches: %10lu (cycles/mis.branch %.2f) cache "
"accesses: %10lu (failure %10lu)\n",
cl2 / iterations, cy2 / iterations, 100. * cy2 / total,
(double)cl2 / cy2, mis2 / iterations, (double)cy2 / mis2,
cref2 / iterations, cmis2 / iterations);
printf(" stage 2 runs at %.2f cycles per input byte and ",
(double)cy2 / (iterations * p.size()));
printf("%.2f cycles per structural character.\n",
(double)cy2 / (iterations * pj.n_structural_indexes));
for (size_t i=0; i<options.files.size(); i++) {
benchmarkers[i]->print(options.tabbed_output);
delete benchmarkers[i];
}
printf(" all stages: %.2f cycles per input byte.\n",
(double)total / (iterations * p.size()));
printf("Estimated average frequency: %.3f GHz.\n",
(double)total / (iterations * min_result * 1000000000.0));
}
#endif
if (!just_data) {
std::cout << "Min: " << min_result << " bytes read: " << p.size()
<< " Gigabytes/second: " << speedinGBs << std::endl;
}
if (json_output) {
isok = isok && pj.print_json(std::cout);
}
if (dump) {
isok = isok && pj.dump_raw_tape(std::cout);
}
if (!isok) {
fprintf(stderr, " Parsing failed. \n ");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
+191
View File
@@ -0,0 +1,191 @@
#include <algorithm>
#include <chrono>
#include <iostream>
#include <map>
#include <vector>
#include "simdjson.h"
#define NB_ITERATION 20
#define MIN_BATCH_SIZE 10000
#define MAX_BATCH_SIZE 10000000
bool test_baseline = false;
bool test_per_batch = true;
bool test_best_batch = false;
bool compare(std::pair<size_t, double> i, std::pair<size_t, double> j) {
return i.second > j.second;
}
int main(int argc, char *argv[]) {
if (argc <= 1) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
exit(1);
}
const char *filename = argv[1];
auto[p, err] = simdjson::padded_string::load(filename);
if (err) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
if (test_baseline) {
std::wclog << "Baseline: Getline + normal parse... " << std::endl;
std::cout << "Gigabytes/second\t"
<< "Nb of documents parsed" << std::endl;
for (auto i = 0; i < 3; i++) {
// Actual test
simdjson::dom::parser parser;
simdjson::error_code alloc_error = parser.allocate(p.size());
if (alloc_error) {
std::cerr << alloc_error << std::endl;
return EXIT_FAILURE;
}
std::istringstream ss(std::string(p.data(), p.size()));
auto start = std::chrono::steady_clock::now();
int count = 0;
std::string line;
int parse_res = simdjson::SUCCESS;
while (getline(ss, line)) {
// TODO we're likely triggering simdjson's padding reallocation here. Is
// that intentional?
parser.parse(line);
count++;
}
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> secs = end - start;
double speedinGBs = static_cast<double>(p.size()) /
(static_cast<double>(secs.count()) * 1000000000.0);
std::cout << speedinGBs << "\t\t\t\t" << count << std::endl;
if (parse_res != simdjson::SUCCESS) {
std::cerr << "Parsing failed" << std::endl;
exit(1);
}
}
}
std::map<size_t, double> batch_size_res;
if (test_per_batch) {
std::wclog << "parse_many: Speed per batch_size... from " << MIN_BATCH_SIZE
<< " bytes to " << MAX_BATCH_SIZE << " bytes..." << std::endl;
std::cout << "Batch Size\t"
<< "Gigabytes/second\t"
<< "Nb of documents parsed" << std::endl;
for (size_t i = MIN_BATCH_SIZE; i <= MAX_BATCH_SIZE;
i += (MAX_BATCH_SIZE - MIN_BATCH_SIZE) / 100) {
batch_size_res.insert(std::pair<size_t, double>(i, 0));
int count;
for (size_t j = 0; j < 5; j++) {
// Actual test
simdjson::dom::parser parser;
simdjson::error_code error;
auto start = std::chrono::steady_clock::now();
count = 0;
simdjson::dom::document_stream docs;
if ((error = parser.parse_many(p, i).get(docs))) {
std::wcerr << "Parsing failed with: " << error << std::endl;
exit(1);
}
for (auto result : docs) {
error = result.error();
if (error) {
std::wcerr << "Parsing failed with: " << error << std::endl;
exit(1);
}
count++;
}
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> secs = end - start;
double speedinGBs = static_cast<double>(p.size()) /
(static_cast<double>(secs.count()) * 1000000000.0);
if (speedinGBs > batch_size_res.at(i))
batch_size_res[i] = speedinGBs;
}
std::cout << i << "\t\t" << std::fixed << std::setprecision(3)
<< batch_size_res.at(i) << "\t\t\t\t" << count << std::endl;
}
}
size_t optimal_batch_size{};
double best_speed{};
if (test_per_batch) {
std::pair<size_t, double> best_results;
best_results =
(*min_element(batch_size_res.begin(), batch_size_res.end(), compare));
optimal_batch_size = best_results.first;
best_speed = best_results.second;
} else {
optimal_batch_size = MIN_BATCH_SIZE;
}
std::wclog << "Seemingly optimal batch_size: " << optimal_batch_size << "..."
<< std::endl;
std::wclog << "Best speed: " << best_speed << "..." << std::endl;
if (test_best_batch) {
std::wclog << "Starting speed test... Best of " << NB_ITERATION
<< " iterations..." << std::endl;
std::vector<double> res;
for (int i = 0; i < NB_ITERATION; i++) {
// Actual test
simdjson::dom::parser parser;
simdjson::error_code error;
auto start = std::chrono::steady_clock::now();
// This includes allocation of the parser
simdjson::dom::document_stream docs;
if ((error = parser.parse_many(p, optimal_batch_size).get(docs))) {
std::wcerr << "Parsing failed with: " << error << std::endl;
exit(1);
}
for (auto result : docs) {
error = result.error();
if (error) {
std::wcerr << "Parsing failed with: " << error << std::endl;
exit(1);
}
}
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> secs = end - start;
res.push_back(secs.count());
}
double min_result = *min_element(res.begin(), res.end());
double speedinGBs =
static_cast<double>(p.size()) / (min_result * 1000000000.0);
std::cout << "Min: " << min_result << " bytes read: " << p.size()
<< " Gigabytes/second: " << speedinGBs << std::endl;
}
#ifdef SIMDJSON_THREADS_ENABLED
// Multithreading probably does not help matters for small files (less than 10
// MB).
if (p.size() < 10000000) {
std::cout << std::endl;
std::cout << "Warning: your file is small and the performance results are "
"probably meaningless"
<< std::endl;
std::cout << "as far as multithreaded performance goes." << std::endl;
std::cout << std::endl;
std::cout
<< "Try to concatenate the file with itself to generate a large one."
<< std::endl;
std::cout << "In bash: " << std::endl;
std::cout << "for i in {1..1000}; do cat '" << filename
<< "' >> bar.ndjson; done" << std::endl;
std::cout << argv[0] << " bar.ndjson" << std::endl;
}
#endif
return 0;
}
+275 -78
View File
@@ -1,7 +1,10 @@
#include "simdjson/jsonparser.h"
#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"
@@ -11,6 +14,8 @@
#include "sajson.h"
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
using namespace simdjson;
struct stat_s {
@@ -43,63 +48,196 @@ void print_stat(const stat_t &s) {
s.true_count, s.false_count);
}
__attribute__((noinline)) stat_t
simdjson_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
simdjson::ParsedJson pj = build_parsed_json(p);
answer.valid = pj.is_valid();
if (!answer.valid) {
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;
size_t tape_idx = 0;
uint64_t tape_val = pj.tape[tape_idx++];
uint8_t type = (tape_val >> 56);
size_t how_many = 0;
assert(type == 'r');
how_many = tape_val & JSON_VALUE_MASK;
for (; tape_idx < how_many; tape_idx++) {
tape_val = pj.tape[tape_idx];
// uint64_t payload = tape_val & JSON_VALUE_MASK;
type = (tape_val >> 56);
switch (type) {
case 'l': // we have a long int
answer.number_count++;
tape_idx++; // skipping the integer
break;
case 'd': // we have a double
answer.number_count++;
tape_idx++; // skipping the double
break;
case 'n': // we have a null
answer.null_count++;
break;
case 't': // we have a true
answer.true_count++;
break;
case 'f': // we have a false
answer.false_count++;
break;
case '{': // we have an object
answer.object_count++;
break;
case '}': // we end an object
break;
case '[': // we start an array
answer.array_count++;
break;
case ']': // we end an array
break;
default:
break; // ignore
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++;
}
return answer;
}
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
@@ -145,15 +283,18 @@ void sajson_traverse(stat_t &stats, const sajson::value &node) {
}
}
__attribute__((noinline)) stat_t
sasjon_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
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;
@@ -203,16 +344,19 @@ void rapid_traverse(stat_t &stats, const rapidjson::Value &v) {
}
}
__attribute__((noinline)) stat_t
rapid_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
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;
@@ -226,6 +370,32 @@ rapid_compute_stats(const simdjson::padded_string &p) {
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;
@@ -257,19 +427,18 @@ int main(int argc, char *argv[]) {
<< std::endl;
}
simdjson::padded_string p;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
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() > 1024 * 1024)
std::cout << p.size() / (1024 * 1024) << " MB ";
else if (p.size() > 1024)
std::cout << p.size() / 1024 << " KB ";
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;
@@ -284,6 +453,11 @@ int main(int argc, char *argv[]) {
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: ");
@@ -292,14 +466,37 @@ int main(int argc, char *argv[]) {
assert(stat_equal(s1, s2));
assert(stat_equal(s1, s3));
int repeat = 50;
int volume = p.size();
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("sasjon ", sasjon_compute_stats(p).valid, true, , repeat, volume,
!just_data);
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);
}
}
+248 -114
View File
@@ -1,7 +1,8 @@
#include "simdjson/jsonparser.h"
#include "simdjson.h"
#include <unistd.h>
#ifndef _MSC_VER
#include "linux-perf-events.h"
#include <unistd.h>
#ifdef __linux__
#include <libgen.h>
#endif //__linux__
@@ -11,6 +12,9 @@
#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"
@@ -20,6 +24,12 @@
#include "sajson.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#include <boost/json/parser.hpp>
#include <boost/json/monotonic_resource.hpp>
#ifdef ALLPARSER
#include "fastjson.cpp"
@@ -41,11 +51,13 @@ extern "C" {
#endif
SIMDJSON_POP_DISABLE_WARNINGS
using namespace rapidjson;
#ifdef ALLPARSER
// fastjson has a tricky interface
void on_json_error(void *, const fastjson::ErrorContext &ec) {
void on_json_error(void *, simdjson_unused const fastjson::ErrorContext &ec) {
// std::cerr<<"ERROR: "<<ec.mesg<<std::endl;
}
bool fastjson_parse(const char *input) {
@@ -57,88 +69,139 @@ bool fastjson_parse(const char *input) {
// end of fastjson stuff
#endif
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 << "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;
exit(1);
}
const char *filename = argv[optind];
if (optind + 1 < argc) {
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
<< std::endl;
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;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &e) { // caught by reference to base
std::cout << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
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 has ";
if (p.size() > 1024 * 1024)
std::cout << p.size() / (1024 * 1024) << " MB ";
else if (p.size() > 1024)
std::cout << p.size() / 1024 << " KB ";
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 << std::endl;
std::cout << p.size() << " B";
std::cout << ": will run " << repeat << " iterations." << std::endl;
}
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size(), 1024);
if (!allocok) {
std::cerr << "can't allocate memory" << std::endl;
return EXIT_FAILURE;
}
int repeat = (p.size() < 1 * 1000 * 1000 ? 1000 : 10);
int volume = p.size();
size_t volume = p.size();
if (just_data) {
printf("%-42s %20s %20s %20s %20s \n", "name", "cycles_per_byte",
"cycles_per_byte_err", "gb_per_s", "gb_per_s_err");
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);
}
if (!just_data)
BEST_TIME("simdjson (dynamic mem) ", build_parsed_json(p).is_valid(), true,
, repeat, volume, !just_data);
// (static alloc)
BEST_TIME("simdjson ", json_parse(p, pj), simdjson::SUCCESS, , repeat, volume,
!just_data);
simdjson::dom::parser parser;
BEST_TIME("simdjson ", parser.parse(p).error(), simdjson::SUCCESS, , repeat,
volume, !just_data);
rapidjson::Document d;
char *buffer = (char *)malloc(p.size() + 1);
memcpy(buffer, p.data(), p.size());
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, memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("RapidJSON (insitu)",
d.ParseInsitu<kParseValidateEncodingFlag>(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,
memcpy(buffer, p.data(), p.size()) && (buffer[p.size()] = '\0'),
std::memcpy(buffer, p.data(), p.size()) &&
(buffer[p.size()] = '\0'),
repeat, volume, !just_data);
{
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);
}
{
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
@@ -146,52 +209,61 @@ int main(int argc, char *argv[]) {
sajson::parse(sajson::dynamic_allocation(),
sajson::mutable_string_view(p.size(), buffer))
.is_valid(),
true, memcpy(buffer, p.data(), p.size()), repeat, volume,
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 *)malloc(ast_buffer_size * sizeof(size_t));
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, memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
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, memcpy(buffer, p.data(), p.size()), repeat, volume,
false, std::memcpy(buffer, p.data(), p.size()), repeat, volume,
!just_data);
BEST_TIME("fastjson ", fastjson_parse(buffer), true,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
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, memcpy(buffer, p.data(), p.size()), repeat, volume,
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,
memcpy(buffer, p.data(), p.size()), repeat, volume, !just_data);
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 parser;
jsmn_init(&parser);
memcpy(buffer, p.data(), p.size());
jsmn_parser jparser;
jsmn_init(&jparser);
std::memcpy(buffer, p.data(), p.size());
buffer[p.size()] = '\0';
BEST_TIME(
"jsmn ",
(jsmn_parse(&parser, buffer, p.size(), tokens.get(), p.size()) > 0),
true, jsmn_init(&parser), repeat, volume, !just_data);
BEST_TIME("jsmn ",
(jsmn_parse(&jparser, buffer, p.size(), tokens.get(),
static_cast<unsigned int>(p.size())) > 0),
true, jsmn_init(&jparser), repeat, volume, !just_data);
}
memcpy(buffer, p.data(), p.size());
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,
@@ -209,12 +281,13 @@ int main(int argc, char *argv[]) {
#endif
if (!just_data)
BEST_TIME("memcpy ",
(memcpy(buffer, p.data(), p.size()) == buffer), true, , repeat,
volume, !just_data);
(std::memcpy(buffer, p.data(), p.size()) == buffer), true, ,
repeat, volume, !just_data);
#ifdef __linux__
if (!just_data) {
printf("\n \n <doing additional analysis with performance counters (Linux "
"only)>\n");
std::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);
@@ -227,64 +300,125 @@ int main(int argc, char *argv[]) {
results.resize(evts.size());
stats.resize(evts.size());
std::fill(stats.begin(), stats.end(), 0); // unnecessary
for (int i = 0; i < repeat; i++) {
for (decltype(repeat) i = 0; i < repeat; i++) {
unified.start();
if (json_parse(p, pj) != simdjson::SUCCESS)
printf("bug\n");
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>());
}
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",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
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 (int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
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)
printf("bug\n");
std::printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("RapidJSON: cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
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 (int i = 0; i < repeat; i++) {
memcpy(buffer, p.data(), p.size());
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)
printf("bug\n");
std::printf("bug\n");
unified.end(results);
std::transform(stats.begin(), stats.end(), results.begin(), stats.begin(),
std::plus<unsigned long long>());
}
printf("sajson : cycles %10.0f instructions %10.0f branchmisses %10.0f "
"cacheref %10.0f cachemisses %10.0f bytespercachemiss %10.0f "
"inspercycle %10.1f insperbyte %10.1f\n",
stats[0] * 1.0 / repeat, stats[1] * 1.0 / repeat,
stats[2] * 1.0 / repeat, stats[3] * 1.0 / repeat,
stats[4] * 1.0 / repeat, volume * repeat * 1.0 / stats[2],
stats[1] * 1.0 / stats[0], stats[1] * 1.0 / (volume * repeat));
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__
free(ast_buffer);
free(buffer);
std::free(ast_buffer);
std::free(buffer);
return true;
}
int main(int argc, char *argv[]) {
bool verbose = false;
bool just_data = false;
double repeat_multiplier = 1;
int c;
while ((c = getopt(argc, argv, "r:vt")) != -1)
switch (c) {
case 'r':
repeat_multiplier = atof(optarg);
break;
case 't':
just_data = true;
break;
case 'v':
verbose = true;
break;
default:
abort();
}
if (optind >= argc) {
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
std::cerr << "The '-t' flag outputs a table." << std::endl;
std::cerr << "The '-r <N>' flag sets the repeat multiplier: set it above 1 "
"to do more iterations, and below 1 to do fewer."
<< std::endl;
exit(1);
}
int result = EXIT_SUCCESS;
for (int fileind = optind; fileind < argc; fileind++) {
if (!bench(argv[fileind], verbose, just_data, repeat_multiplier)) {
result = EXIT_FAILURE;
}
std::printf("\n\n");
}
return result;
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class Dom {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
dom::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline uint64_t nullable_int(dom::element element) {
if (element.is_null()) { return 0; }
return element;
}
};
simdjson_really_inline bool Dom::Run(const padded_string &json) {
tweets.clear();
for (dom::element tweet : parser.parse(json)["statuses"]) {
auto user = tweet["user"];
tweets.emplace_back(partial_tweets::tweet{
tweet["created_at"],
tweet["id"],
tweet["text"],
nullable_int(tweet["in_reply_to_status_id"]),
{ user["id"], user["screen_name"] },
tweet["retweet_count"],
tweet["favorite_count"]
});
}
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, Dom);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
class DomNoExcept {
public:
simdjson_really_inline bool Run(const simdjson::padded_string &json) noexcept;
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
dom::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline simdjson_result<uint64_t> nullable_int(simdjson_result<dom::element> result) noexcept {
dom::element element;
SIMDJSON_TRY( result.get(element) );
if (element.is_null()) { return 0; }
return element.get_uint64();
}
simdjson_really_inline error_code RunNoExcept(const simdjson::padded_string &json) noexcept;
};
simdjson_really_inline bool DomNoExcept::Run(const simdjson::padded_string &json) noexcept {
auto error = RunNoExcept(json);
if (error) { std::cerr << error << std::endl; return false; }
return true;
}
simdjson_really_inline error_code DomNoExcept::RunNoExcept(const simdjson::padded_string &json) noexcept {
tweets.clear();
dom::array tweet_array;
SIMDJSON_TRY( parser.parse(json)["statuses"].get_array().get(tweet_array) );
for (auto tweet_element : tweet_array) {
dom::object tweet;
SIMDJSON_TRY( tweet_element.get_object().get(tweet) );
dom::object user;
SIMDJSON_TRY( tweet["user"].get_object().get(user) );
partial_tweets::tweet t;
SIMDJSON_TRY( tweet["created_at"] .get_string().get(t.created_at) );
SIMDJSON_TRY( tweet["id"] .get_uint64().get(t.id) );
SIMDJSON_TRY( tweet["text"] .get_string().get(t.text) );
SIMDJSON_TRY( nullable_int(tweet["in_reply_to_status_id"]).get(t.in_reply_to_status_id) );
SIMDJSON_TRY( user["id"] .get_uint64().get(t.user.id) );
SIMDJSON_TRY( user["screen_name"] .get_string().get(t.user.screen_name) );
SIMDJSON_TRY( tweet["retweet_count"] .get_uint64().get(t.retweet_count) );
SIMDJSON_TRY( tweet["favorite_count"].get_uint64().get(t.favorite_count) );
tweets.push_back(t);
}
return SUCCESS;
}
} // namespace partial_tweets
+93
View File
@@ -0,0 +1,93 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
class Iter {
public:
simdjson_really_inline bool Run(const padded_string &json);
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
ondemand::parser parser{};
std::vector<tweet> tweets{};
simdjson_really_inline uint64_t nullable_int(ondemand::value && value) {
if (value.is_null()) { return 0; }
return std::move(value);
}
simdjson_really_inline twitter_user read_user(ondemand::object && user) {
// Move user into a local object so it gets destroyed (and moves the iterator)
ondemand::object u = std::move(user);
return { u["id"], u["screen_name"] };
}
};
simdjson_really_inline bool Iter::Run(const padded_string &json) {
tweets.clear();
// Walk the document, parsing the tweets as we go
// { "statuses":
auto iter = parser.iterate_raw(json).value();
if (!iter.start_object() || !iter.find_field_raw("statuses")) { return false; }
// { "statuses": [
if (!iter.start_array()) { return false; }
do {
tweet tweet;
if (!iter.start_object() || !iter.find_field_raw("created_at")) { return false; }
tweet.created_at = iter.consume_string();
if (!iter.has_next_field() || !iter.find_field_raw("id")) { return false; }
tweet.id = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("text")) { return false; }
tweet.text = iter.consume_string();
if (!iter.has_next_field() || !iter.find_field_raw("in_reply_to_status_id")) { return false; }
if (!iter.is_null()) {
tweet.in_reply_to_status_id = iter.consume_uint64();
}
if (!iter.has_next_field() || !iter.find_field_raw("user")) { return false; }
{
if (!iter.start_object() || !iter.find_field_raw("id")) { return false; }
tweet.user.id = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("screen_name")) { return false; }
tweet.user.screen_name = iter.consume_string();
if (iter.skip_container()) { return false; } // Skip the rest of the user object
}
if (!iter.has_next_field() || !iter.find_field_raw("retweet_count")) { return false; }
tweet.retweet_count = iter.consume_uint64();
if (!iter.has_next_field() || !iter.find_field_raw("favorite_count")) { return false; }
tweet.favorite_count = iter.consume_uint64();
tweets.push_back(tweet);
if (iter.skip_container()) { return false; } // Skip the rest of the tweet object
} while (iter.has_next_element());
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, Iter);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#if SIMDJSON_EXCEPTIONS
#include "partial_tweets.h"
namespace partial_tweets {
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<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"] };
}
static inline bool displayed_implementation = false;
};
simdjson_really_inline bool OnDemand::Run(const padded_string &json) {
tweets.clear();
// Walk the document, parsing the tweets as we go
auto doc = parser.iterate(json);
for (ondemand::object tweet : doc["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"]
});
}
return true;
}
BENCHMARK_TEMPLATE(PartialTweets, OnDemand);
} // namespace partial_tweets
#endif // SIMDJSON_EXCEPTIONS
+41
View File
@@ -0,0 +1,41 @@
#pragma once
//
// Interface
//
namespace partial_tweets {
template<typename T> static void PartialTweets(benchmark::State &state);
} // namespace partial_tweets
//
// Implementation
//
#include "tweet.h"
#include <vector>
#include "event_counter.h"
#include "domnoexcept.h"
#include "json_benchmark.h"
namespace partial_tweets {
using namespace simdjson;
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;
}
JsonBenchmark<T, DomNoExcept>(state, json);
}
} // namespace partial_tweets
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include "partial_tweets.h"
#include "sax_tweet_reader_visitor.h"
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
using namespace simdjson::builtin::stage2;
class Sax {
public:
simdjson_really_inline bool Run(const padded_string &json) noexcept;
simdjson_really_inline const std::vector<tweet> &Result() { return tweets; }
simdjson_really_inline size_t ItemCount() { return tweets.size(); }
private:
simdjson_really_inline error_code RunNoExcept(const padded_string &json) noexcept;
error_code Allocate(size_t new_capacity);
std::unique_ptr<uint8_t[]> string_buf{};
size_t capacity{};
dom_parser_implementation dom_parser{};
std::vector<tweet> tweets{};
};
// NOTE: this assumes the dom_parser is already allocated
bool Sax::Run(const padded_string &json) noexcept {
auto error = RunNoExcept(json);
if (error) { std::cerr << error << std::endl; return false; }
return true;
}
error_code Sax::RunNoExcept(const padded_string &json) noexcept {
tweets.clear();
// Allocate capacity if needed
if (capacity < json.size()) {
SIMDJSON_TRY( Allocate(json.size()) );
}
// Run stage 1 first.
SIMDJSON_TRY( dom_parser.stage1((uint8_t *)json.data(), json.size(), false) );
// Then walk the document, parsing the tweets as we go
json_iterator iter(dom_parser, 0);
sax_tweet_reader_visitor visitor(tweets, string_buf.get());
SIMDJSON_TRY( iter.walk_document<false>(visitor) );
return SUCCESS;
}
error_code Sax::Allocate(size_t new_capacity) {
// string_capacity copied from document::allocate
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
if (auto error = dom_parser.set_capacity(new_capacity)) { return error; }
if (capacity == 0) { // set max depth the first time only
if (auto error = dom_parser.set_max_depth(DEFAULT_MAX_DEPTH)) { return error; }
}
capacity = new_capacity;
return SUCCESS;
}
BENCHMARK_TEMPLATE(PartialTweets, Sax);
} // namespace partial_tweets
@@ -0,0 +1,514 @@
#pragma once
#include "simdjson.h"
#include "tweet.h"
#include <vector>
namespace partial_tweets {
using namespace simdjson;
using namespace simdjson::builtin;
using namespace simdjson::builtin::stage2;
struct sax_tweet_reader_visitor {
public:
simdjson_really_inline sax_tweet_reader_visitor(std::vector<tweet> &tweets, uint8_t *string_buf);
simdjson_really_inline error_code visit_document_start(json_iterator &iter);
simdjson_really_inline error_code visit_object_start(json_iterator &iter);
simdjson_really_inline error_code visit_key(json_iterator &iter, const uint8_t *key);
simdjson_really_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value);
simdjson_really_inline error_code visit_array_start(json_iterator &iter);
simdjson_really_inline error_code visit_array_end(json_iterator &iter);
simdjson_really_inline error_code visit_object_end(json_iterator &iter);
simdjson_really_inline error_code visit_document_end(json_iterator &iter);
simdjson_really_inline error_code visit_empty_array(json_iterator &iter);
simdjson_really_inline error_code visit_empty_object(json_iterator &iter);
simdjson_really_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value);
simdjson_really_inline error_code increment_count(json_iterator &iter);
private:
// Since we only care about one thing at each level, we just use depth as the marker for what
// object/array we're nested inside.
enum class containers {
document = 0, //
top_object = 1, // {
statuses = 2, // { "statuses": [
tweet = 3, // { "statuses": [ {
user = 4 // { "statuses": [ { "user": {
};
/**
* The largest depth we care about.
* There can be things at lower depths.
*/
static constexpr uint32_t MAX_SUPPORTED_DEPTH = uint32_t(containers::user);
static constexpr const char *STATE_NAMES[] = {
"document",
"top object",
"statuses",
"tweet",
"user"
};
enum class field_type {
any,
unsigned_integer,
string,
nullable_unsigned_integer,
object,
array
};
struct field {
const char * key{};
size_t len{0};
size_t offset;
containers container{containers::document};
field_type type{field_type::any};
};
std::vector<tweet> &tweets;
containers container{containers::document};
uint8_t *current_string_buf_loc;
const uint8_t *current_key{};
simdjson_really_inline bool in_container(json_iterator &iter);
simdjson_really_inline bool in_container_child(json_iterator &iter);
simdjson_really_inline void start_container(json_iterator &iter);
simdjson_really_inline void end_container(json_iterator &iter);
simdjson_really_inline error_code parse_nullable_unsigned(json_iterator &iter, const uint8_t *value, const field &f);
simdjson_really_inline error_code parse_unsigned(json_iterator &iter, const uint8_t *value, const field &f);
simdjson_really_inline error_code parse_string(json_iterator &iter, const uint8_t *value, const field &f);
struct field_lookup {
field entries[256]{};
field_lookup();
simdjson_really_inline field get(const uint8_t * key, containers container);
private:
simdjson_really_inline uint8_t hash(const char * key, uint32_t depth);
simdjson_really_inline void add(const char * key, size_t len, containers container, field_type type, size_t offset);
simdjson_really_inline void neg(const char * const key, uint32_t depth);
};
static field_lookup fields;
}; // sax_tweet_reader_visitor
simdjson_really_inline sax_tweet_reader_visitor::sax_tweet_reader_visitor(std::vector<tweet> &_tweets, uint8_t *_string_buf)
: tweets{_tweets},
current_string_buf_loc{_string_buf} {
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_document_start(json_iterator &iter) {
start_container(iter);
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_array_start(json_iterator &iter) {
// If we're not in a container we care about, don't bother with the rest
if (!in_container_child(iter)) { return SUCCESS; }
// Handle fields first
if (current_key) {
switch (fields.get(current_key, container).type) {
case field_type::array: // { "statuses": [
start_container(iter);
current_key = nullptr;
return SUCCESS;
case field_type::any:
return SUCCESS;
case field_type::object:
case field_type::unsigned_integer:
case field_type::nullable_unsigned_integer:
case field_type::string:
iter.log_error("unexpected array field");
return INCORRECT_TYPE;
}
}
// We're not in a field, so it must be a child of an array. We support any of those.
iter.log_error("unexpected array");
return INCORRECT_TYPE;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_object_start(json_iterator &iter) {
// If we're not in a container we care about, don't bother with the rest
if (!in_container_child(iter)) { return SUCCESS; }
// Handle known fields
if (current_key) {
auto f = fields.get(current_key, container);
switch (f.type) {
case field_type::object: // { "statuses": [ { "user": {
start_container(iter);
return SUCCESS;
case field_type::any:
return SUCCESS;
case field_type::array:
case field_type::unsigned_integer:
case field_type::nullable_unsigned_integer:
case field_type::string:
iter.log_error("unexpected object field");
return INCORRECT_TYPE;
}
}
// It's not a field, so it's a child of an array or document
switch (container) {
case containers::document: // top_object: {
case containers::statuses: // tweet: { "statuses": [ {
start_container(iter);
return SUCCESS;
case containers::top_object:
case containers::tweet:
case containers::user:
iter.log_error("unexpected object");
return INCORRECT_TYPE;
}
SIMDJSON_UNREACHABLE();
return UNINITIALIZED;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_key(json_iterator &, const uint8_t *key) {
current_key = key;
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_primitive(json_iterator &iter, const uint8_t *value) {
// Don't bother unless we're in a container we care about
if (!in_container(iter)) { return SUCCESS; }
// Handle fields first
if (current_key) {
auto f = fields.get(current_key, container);
switch (f.type) {
case field_type::unsigned_integer:
return parse_unsigned(iter, value, f);
case field_type::nullable_unsigned_integer:
return parse_nullable_unsigned(iter, value, f);
case field_type::string:
return parse_string(iter, value, f);
case field_type::any:
return SUCCESS;
case field_type::array:
case field_type::object:
iter.log_error("unexpected primitive");
return INCORRECT_TYPE;
}
current_key = nullptr;
}
// If it's not a field, it's a child of an array.
// The only array we support is statuses, which must contain objects.
iter.log_error("unexpected primitive");
return INCORRECT_TYPE;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_array_end(json_iterator &iter) {
if (in_container(iter)) { end_container(iter); }
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_object_end(json_iterator &iter) {
current_key = nullptr;
if (in_container(iter)) { end_container(iter); }
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_document_end(json_iterator &) {
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_empty_array(json_iterator &) {
current_key = nullptr;
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_empty_object(json_iterator &) {
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::visit_root_primitive(json_iterator &iter, const uint8_t *) {
iter.log_error("unexpected root primitive");
return INCORRECT_TYPE;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::increment_count(json_iterator &) { return SUCCESS; }
simdjson_really_inline bool sax_tweet_reader_visitor::in_container(json_iterator &iter) {
return iter.depth == uint32_t(container);
}
simdjson_really_inline bool sax_tweet_reader_visitor::in_container_child(json_iterator &iter) {
return iter.depth == uint32_t(container) + 1;
}
simdjson_really_inline void sax_tweet_reader_visitor::start_container(json_iterator &iter) {
SIMDJSON_ASSUME(iter.depth <= MAX_SUPPORTED_DEPTH); // Asserts in debug mode
container = containers(iter.depth);
if (logger::LOG_ENABLED) { iter.log_value(STATE_NAMES[iter.depth]); }
if (container == containers::tweet) { tweets.push_back({}); }
}
simdjson_really_inline void sax_tweet_reader_visitor::end_container(json_iterator &) {
container = containers(int(container) - 1);
}
simdjson_really_inline error_code sax_tweet_reader_visitor::parse_nullable_unsigned(json_iterator &iter, const uint8_t *value, const field &f) {
iter.log_value(f.key);
auto i = reinterpret_cast<uint64_t *>(reinterpret_cast<char *>(&tweets.back()) + f.offset);
if (auto error = numberparsing::parse_unsigned(value).get(*i)) {
// If number parsing failed, check if it's null before returning the error
if (!atomparsing::is_valid_null_atom(value)) { iter.log_error("expected number or null"); return error; }
i = 0;
}
return SUCCESS;
}
simdjson_really_inline error_code sax_tweet_reader_visitor::parse_unsigned(json_iterator &iter, const uint8_t *value, const field &f) {
iter.log_value(f.key);
auto i = reinterpret_cast<uint64_t *>(reinterpret_cast<char *>(&tweets.back()) + f.offset);
return numberparsing::parse_unsigned(value).get(*i);
}
simdjson_really_inline error_code sax_tweet_reader_visitor::parse_string(json_iterator &iter, const uint8_t *value, const field &f) {
iter.log_value(f.key);
auto s = reinterpret_cast<std::string_view *>(reinterpret_cast<char *>(&tweets.back()) + f.offset);
return stringparsing::parse_string_to_buffer(value, current_string_buf_loc, *s);
}
sax_tweet_reader_visitor::field_lookup sax_tweet_reader_visitor::fields{};
simdjson_really_inline uint8_t sax_tweet_reader_visitor::field_lookup::hash(const char * key, uint32_t depth) {
// These shift numbers were chosen specifically because this yields only 2 collisions between
// keys in twitter.json, leaves 0 as a distinct value, and has 0 collisions between keys we
// actually care about.
return uint8_t((key[0] << 0) ^ (key[1] << 3) ^ (key[2] << 3) ^ (key[3] << 1) ^ depth);
}
simdjson_really_inline sax_tweet_reader_visitor::field sax_tweet_reader_visitor::field_lookup::get(const uint8_t * key, containers c) {
auto index = hash((const char *)key, uint32_t(c));
auto entry = entries[index];
// TODO if any key is > SIMDJSON_PADDING, this will access inaccessible memory!
if (c != entry.container || memcmp(key, entry.key, entry.len)) { return entries[0]; }
return entry;
}
simdjson_really_inline void sax_tweet_reader_visitor::field_lookup::add(const char * key, size_t len, containers c, field_type type, size_t offset) {
auto index = hash(key, uint32_t(c));
if (index == 0) {
fprintf(stderr, "%s (depth %d) hashes to zero, which is used as 'missing value'\n", key, int(c));
assert(false);
}
if (entries[index].key) {
fprintf(stderr, "%s (depth %d) collides with %s (depth %d) !\n", key, int(c), entries[index].key, int(entries[index].container));
assert(false);
}
entries[index] = { key, len, offset, c, type };
}
simdjson_really_inline void sax_tweet_reader_visitor::field_lookup::neg(const char * const key, uint32_t depth) {
auto index = hash(key, depth);
if (entries[index].key) {
fprintf(stderr, "%s (depth %d) conflicts with %s (depth %d) !\n", key, depth, entries[index].key, int(entries[index].container));
}
}
sax_tweet_reader_visitor::field_lookup::field_lookup() {
add("\"statuses\"", std::strlen("\"statuses\""), containers::top_object, field_type::array, 0); // { "statuses": [...]
#define TWEET_FIELD(KEY, TYPE) add("\"" #KEY "\"", std::strlen("\"" #KEY "\""), containers::tweet, TYPE, offsetof(tweet, KEY));
TWEET_FIELD(id, field_type::unsigned_integer);
TWEET_FIELD(in_reply_to_status_id, field_type::nullable_unsigned_integer);
TWEET_FIELD(retweet_count, field_type::unsigned_integer);
TWEET_FIELD(favorite_count, field_type::unsigned_integer);
TWEET_FIELD(text, field_type::string);
TWEET_FIELD(created_at, field_type::string);
TWEET_FIELD(user, field_type::object)
#undef TWEET_FIELD
#define USER_FIELD(KEY, TYPE) add("\"" #KEY "\"", std::strlen("\"" #KEY "\""), containers::user, TYPE, offsetof(tweet, user)+offsetof(twitter_user, KEY));
USER_FIELD(id, field_type::unsigned_integer);
USER_FIELD(screen_name, field_type::string);
#undef USER_FIELD
// Check for collisions with other (unused) hash keys in typical twitter JSON
#define NEG(key, depth) neg("\"" #key "\"", depth);
NEG(display_url, 9);
NEG(expanded_url, 9);
neg("\"h\":", 9);
NEG(indices, 9);
NEG(resize, 9);
NEG(url, 9);
neg("\"w\":", 9);
NEG(display_url, 8);
NEG(expanded_url, 8);
neg("\"h\":", 8);
NEG(indices, 8);
NEG(large, 8);
NEG(medium, 8);
NEG(resize, 8);
NEG(small, 8);
NEG(thumb, 8);
NEG(url, 8);
neg("\"w\":", 8);
NEG(display_url, 7);
NEG(expanded_url, 7);
NEG(id_str, 7);
NEG(id, 7);
NEG(indices, 7);
NEG(large, 7);
NEG(media_url_https, 7);
NEG(media_url, 7);
NEG(medium, 7);
NEG(name, 7);
NEG(sizes, 7);
NEG(small, 7);
NEG(source_status_id_str, 7);
NEG(source_status_id, 7);
NEG(thumb, 7);
NEG(type, 7);
NEG(url, 7);
NEG(urls, 7);
NEG(description, 6);
NEG(display_url, 6);
NEG(expanded_url, 6);
NEG(id_str, 6);
NEG(id, 6);
NEG(indices, 6);
NEG(media_url_https, 6);
NEG(media_url, 6);
NEG(name, 6);
NEG(sizes, 6);
NEG(source_status_id_str, 6);
NEG(source_status_id, 6);
NEG(type, 6);
NEG(url, 6);
NEG(urls, 6);
NEG(contributors_enabled, 5);
NEG(default_profile_image, 5);
NEG(default_profile, 5);
NEG(description, 5);
NEG(entities, 5);
NEG(favourites_count, 5);
NEG(follow_request_sent, 5);
NEG(followers_count, 5);
NEG(following, 5);
NEG(friends_count, 5);
NEG(geo_enabled, 5);
NEG(hashtags, 5);
NEG(id_str, 5);
NEG(id, 5);
NEG(is_translation_enabled, 5);
NEG(is_translator, 5);
NEG(iso_language_code, 5);
NEG(lang, 5);
NEG(listed_count, 5);
NEG(location, 5);
NEG(media, 5);
NEG(name, 5);
NEG(notifications, 5);
NEG(profile_background_color, 5);
NEG(profile_background_image_url_https, 5);
NEG(profile_background_image_url, 5);
NEG(profile_background_tile, 5);
NEG(profile_banner_url, 5);
NEG(profile_image_url_https, 5);
NEG(profile_image_url, 5);
NEG(profile_link_color, 5);
NEG(profile_sidebar_border_color, 5);
NEG(profile_sidebar_fill_color, 5);
NEG(profile_text_color, 5);
NEG(profile_use_background_image, 5);
NEG(protected, 5);
NEG(result_type, 5);
NEG(statuses_count, 5);
NEG(symbols, 5);
NEG(time_zone, 5);
NEG(url, 5);
NEG(urls, 5);
NEG(user_mentions, 5);
NEG(utc_offset, 5);
NEG(verified, 5);
NEG(contributors_enabled, 4);
NEG(contributors, 4);
NEG(coordinates, 4);
NEG(default_profile_image, 4);
NEG(default_profile, 4);
NEG(description, 4);
NEG(entities, 4);
NEG(favorited, 4);
NEG(favourites_count, 4);
NEG(follow_request_sent, 4);
NEG(followers_count, 4);
NEG(following, 4);
NEG(friends_count, 4);
NEG(geo_enabled, 4);
NEG(geo, 4);
NEG(hashtags, 4);
NEG(id_str, 4);
NEG(in_reply_to_screen_name, 4);
NEG(in_reply_to_status_id_str, 4);
NEG(in_reply_to_user_id_str, 4);
NEG(in_reply_to_user_id, 4);
NEG(is_translation_enabled, 4);
NEG(is_translator, 4);
NEG(iso_language_code, 4);
NEG(lang, 4);
NEG(listed_count, 4);
NEG(location, 4);
NEG(media, 4);
NEG(metadata, 4);
NEG(name, 4);
NEG(notifications, 4);
NEG(place, 4);
NEG(possibly_sensitive, 4);
NEG(profile_background_color, 4);
NEG(profile_background_image_url_https, 4);
NEG(profile_background_image_url, 4);
NEG(profile_background_tile, 4);
NEG(profile_banner_url, 4);
NEG(profile_image_url_https, 4);
NEG(profile_image_url, 4);
NEG(profile_link_color, 4);
NEG(profile_sidebar_border_color, 4);
NEG(profile_sidebar_fill_color, 4);
NEG(profile_text_color, 4);
NEG(profile_use_background_image, 4);
NEG(protected, 4);
NEG(result_type, 4);
NEG(retweeted, 4);
NEG(source, 4);
NEG(statuses_count, 4);
NEG(symbols, 4);
NEG(time_zone, 4);
NEG(truncated, 4);
NEG(url, 4);
NEG(urls, 4);
NEG(user_mentions, 4);
NEG(utc_offset, 4);
NEG(verified, 4);
NEG(contributors, 3);
NEG(coordinates, 3);
NEG(entities, 3);
NEG(favorited, 3);
NEG(geo, 3);
NEG(id_str, 3);
NEG(in_reply_to_screen_name, 3);
NEG(in_reply_to_status_id_str, 3);
NEG(in_reply_to_user_id_str, 3);
NEG(in_reply_to_user_id, 3);
NEG(lang, 3);
NEG(metadata, 3);
NEG(place, 3);
NEG(possibly_sensitive, 3);
NEG(retweeted_status, 3);
NEG(retweeted, 3);
NEG(source, 3);
NEG(truncated, 3);
NEG(completed_in, 2);
NEG(count, 2);
NEG(max_id_str, 2);
NEG(max_id, 2);
NEG(next_results, 2);
NEG(query, 2);
NEG(refresh_url, 2);
NEG(since_id_str, 2);
NEG(since_id, 2);
NEG(search_metadata, 1);
#undef NEG
}
// sax_tweet_reader_visitor::field_lookup::find_min() {
// int min_count = 100000;
// for (int a=0;a<4;a++) {
// for (int b=0;b<4;b++) {
// for (int c=0;c<4;c++) {
// sax_tweet_reader_visitor::field_lookup fields(a,b,c);
// if (fields.collision_count) { continue; }
// if (fields.zero_emission) { continue; }
// if (fields.conflict_count < min_count) { printf("min=%d,%d,%d (%d)", a, b, c, fields.conflict_count); }
// }
// }
// }
// }
} // namespace partial_tweets
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include "simdjson.h"
#include "twitter_user.h"
namespace partial_tweets {
// {
// "statuses": [
// {
// "created_at": "Sun Aug 31 00:29:15 +0000 2014",
// "id": 505874924095815700,
// "text": "@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖",
// "in_reply_to_status_id": null,
// "user": {
// "id": 1186275104,
// "screen_name": "ayuu0123"
// },
// "retweet_count": 0,
// "favorite_count": 0
// }
// ]
// }
struct tweet {
std::string_view created_at{};
uint64_t id{};
std::string_view text{};
uint64_t in_reply_to_status_id{};
twitter_user user{};
uint64_t retweet_count{};
uint64_t favorite_count{};
simdjson_really_inline bool operator==(const tweet &other) const {
return created_at == other.created_at &&
id == other.id &&
text == other.text &&
in_reply_to_status_id == other.in_reply_to_status_id &&
user == other.user &&
retweet_count == other.retweet_count &&
favorite_count == other.favorite_count;
}
simdjson_really_inline bool operator!=(const tweet &other) const { return !(*this == other); }
};
simdjson_unused static std::ostream &operator<<(std::ostream &o, const tweet &t) {
o << "created_at: " << t.created_at << std::endl;
o << "id: " << t.id << std::endl;
o << "text: " << t.text << std::endl;
o << "in_reply_to_status_id: " << t.in_reply_to_status_id << std::endl;
o << "user.id: " << t.user.id << std::endl;
o << "user.screen_name: " << t.user.screen_name << std::endl;
o << "retweet_count: " << t.retweet_count << std::endl;
o << "favorite_count: " << t.favorite_count << std::endl;
return o;
}
} // namespace partial_tweets
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "simdjson.h"
namespace partial_tweets {
struct twitter_user {
uint64_t id{};
std::string_view screen_name{};
bool operator==(const twitter_user &other) const {
return id == other.id &&
screen_name == other.screen_name;
}
};
} // namespace partial_tweets
+114
View File
@@ -0,0 +1,114 @@
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <sstream>
#include <array>
#include <algorithm>
#include <vector>
#include <cmath>
#ifdef _WIN32
#define popen _popen
#define pclose _pclose
#endif
int closepipe(FILE *pipe) {
int exit_code = pclose(pipe);
if (exit_code != EXIT_SUCCESS) {
std::cerr << "Error " << exit_code << " running benchmark command!" << std::endl;
exit(EXIT_FAILURE);
};
return exit_code;
}
std::string exec(const char* cmd) {
std::cerr << cmd << std::endl;
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&closepipe)> pipe(popen(cmd, "r"), closepipe);
if (!pipe) {
std::cerr << "popen() failed!" << std::endl;
abort();
}
while (fgets(buffer.data(), int(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
double readThroughput(std::string parseOutput) {
std::istringstream output(parseOutput);
std::string line;
double result = 0;
int numResults = 0;
while (std::getline(output, line)) {
std::string::size_type pos = 0;
for (int i=0; i<5; i++) {
pos = line.find('\t', pos);
if (pos == std::string::npos) {
std::cerr << "Command printed out a line with less than 5 fields in it:\n" << line << std::endl;
}
pos++;
}
result += std::stod(line.substr(pos));
numResults++;
}
if (numResults == 0) {
std::cerr << "No results returned from benchmark command!" << std::endl;
exit(EXIT_FAILURE);
}
return result / numResults;
}
const double INTERLEAVED_ATTEMPTS = 7;
int main(int argc, const char *argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " <old parse exe> <new parse exe> [<parse arguments>]" << std::endl;
return 1;
}
std::string newCommand = argv[1];
std::string refCommand = argv[2];
for (int i=3; i<argc; i++) {
newCommand += " ";
newCommand += argv[i];
refCommand += " ";
refCommand += argv[i];
}
std::vector<double> ref;
std::vector<double> newcode;
for (int attempt=0; attempt < INTERLEAVED_ATTEMPTS; attempt++) {
std::cout << "Attempt #" << (attempt+1) << " of " << INTERLEAVED_ATTEMPTS << std::endl;
// Read new throughput
double newThroughput = readThroughput(exec(newCommand.c_str()));
std::cout << "New throughput: " << newThroughput << std::endl;
newcode.push_back(newThroughput);
// Read reference throughput
double referenceThroughput = readThroughput(exec(refCommand.c_str()));
std::cout << "Ref throughput: " << referenceThroughput << std::endl;
ref.push_back(referenceThroughput);
}
// we check if the maximum of newcode is lower than minimum of ref, if so we have a problem so fail!
double worseref = *std::min_element(ref.begin(), ref.end());
double bestnewcode = *std::max_element(newcode.begin(), newcode.end());
double bestref = *std::max_element(ref.begin(), ref.end());
double worsenewcode = *std::min_element(newcode.begin(), newcode.end());
std::cout << "The new code has a throughput in " << worsenewcode << " -- " << bestnewcode << std::endl;
std::cout << "The reference code has a throughput in " << worseref << " -- " << bestref << std::endl;
if(bestnewcode < worseref) {
std::cerr << "You probably have a performance degradation." << std::endl;
return EXIT_FAILURE;
}
if(bestnewcode < worseref) {
std::cout << "You probably have a performance gain." << std::endl;
return EXIT_SUCCESS;
}
std::cout << "There is no obvious performance difference. A manual check might be needed." << std::endl;
return EXIT_SUCCESS;
}
+75 -76
View File
@@ -1,9 +1,6 @@
#include <iostream>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "simdjson/jsonioutil.h"
#include "simdjson/jsonparser.h"
#include "simdjson.h"
#ifdef __linux__
#include "linux-perf-events.h"
#endif
@@ -42,72 +39,77 @@ struct stat_s {
using stat_t = struct stat_s;
simdjson_really_inline void simdjson_process_atom(stat_t &s,
simdjson::dom::element element) {
if (element.is<int64_t>()) {
s.integer_count++;
} else if(element.is<std::string_view>()) {
s.string_count++;
} else if(element.is<double>()) {
s.float_count++;
} else if (element.is<bool>()) {
simdjson::error_code err;
bool v;
err = element.get(v);
if (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) {
simdjson::error_code error;
if (element.is<simdjson::dom::array>()) {
s.array_count++;
simdjson::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++;
simdjson::dom::object object;
if ((error = element.get(object))) { std::cerr << error << std::endl; abort(); }
for (auto field : object) {
s.string_count++; // for key
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);
}
}
stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
stat_t answer;
simdjson::ParsedJson pj = simdjson::build_parsed_json(p);
answer.valid = pj.is_valid();
if (!answer.valid) {
stat_t answer{};
simdjson::dom::parser parser;
simdjson::dom::element doc;
auto error = parser.parse(p).get(doc);
if (error) {
answer.valid = false;
return answer;
}
answer.valid = true;
answer.backslash_count =
count_backslash(reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.non_ascii_byte_count = count_nonasciibytes(
reinterpret_cast<const uint8_t *>(p.data()), p.size());
answer.byte_count = p.size();
answer.integer_count = 0;
answer.float_count = 0;
answer.object_count = 0;
answer.array_count = 0;
answer.null_count = 0;
answer.true_count = 0;
answer.false_count = 0;
answer.string_count = 0;
answer.structural_indexes_count = pj.n_structural_indexes;
size_t tape_idx = 0;
uint64_t tape_val = pj.tape[tape_idx++];
uint8_t type = (tape_val >> 56);
size_t how_many = 0;
assert(type == 'r');
how_many = tape_val & JSON_VALUE_MASK;
for (; tape_idx < how_many; tape_idx++) {
tape_val = pj.tape[tape_idx];
// uint64_t payload = tape_val & JSON_VALUE_MASK;
type = (tape_val >> 56);
switch (type) {
case 'l': // we have a long int
answer.integer_count++;
tape_idx++; // skipping the integer
break;
case 'd': // we have a double
answer.float_count++;
tape_idx++; // skipping the double
break;
case 'n': // we have a null
answer.null_count++;
break;
case 't': // we have a true
answer.true_count++;
break;
case 'f': // we have a false
answer.false_count++;
break;
case '{': // we have an object
answer.object_count++;
break;
case '}': // we end an object
break;
case '[': // we start an array
answer.array_count++;
break;
case ']': // we end an array
break;
case '"': // we have a string
answer.string_count++;
break;
default:
break; // ignore
}
}
answer.structural_indexes_count = parser.implementation->n_structural_indexes;
simdjson_recurse(answer, doc);
return answer;
}
@@ -136,9 +138,8 @@ int main(int argc, char *argv[]) {
<< std::endl;
}
simdjson::padded_string p;
try {
simdjson::get_corpus(filename).swap(p);
} catch (const std::exception &) { // caught by reference to base
auto error = simdjson::padded_string::load(filename).get(p);
if (error) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
@@ -164,10 +165,10 @@ int main(int argc, char *argv[]) {
s.non_ascii_byte_count, s.object_count, s.array_count, s.null_count,
s.true_count, s.false_count, s.byte_count, s.structural_indexes_count);
#ifdef __linux__
simdjson::ParsedJson pj;
bool allocok = pj.allocate_capacity(p.size());
if (!allocok) {
std::cerr << "failed to allocate memory" << std::endl;
simdjson::dom::parser parser;
simdjson::error_code alloc_error = parser.allocate(p.size());
if (alloc_error) {
std::cerr << alloc_error << std::endl;
return EXIT_FAILURE;
}
const uint32_t iterations = p.size() < 1 * 1000 * 1000 ? 1000 : 50;
@@ -181,17 +182,15 @@ int main(int argc, char *argv[]) {
results.resize(evts.size());
for (uint32_t i = 0; i < iterations; i++) {
unified.start();
// The default template is simdjson::Architecture::NATIVE.
bool isok = (simdjson::find_structural_bits<>(p.data(), p.size(), pj) ==
simdjson::SUCCESS);
// The default template is simdjson::architecture::NATIVE.
bool isok = (parser.implementation->stage1((const uint8_t *)p.data(), p.size(), false) == simdjson::SUCCESS);
unified.end(results);
cy1 += results[0];
cl1 += results[1];
unified.start();
isok =
isok && (simdjson::SUCCESS == unified_machine(p.data(), p.size(), pj));
isok = isok && (parser.implementation->stage2(parser.doc) == simdjson::SUCCESS);
unified.end(results);
cy2 += results[0];
@@ -200,8 +199,8 @@ int main(int argc, char *argv[]) {
std::cerr << "failure?" << std::endl;
}
}
printf("%f %f %f %f ", cy1 * 1.0 / iterations, cl1 * 1.0 / iterations,
cy2 * 1.0 / iterations, cl2 * 1.0 / iterations);
printf("%f %f %f %f ", static_cast<double>(cy1) / static_cast<double>(iterations), static_cast<double>(cl1) / static_cast<double>(iterations),
static_cast<double>(cy2) / static_cast<double>(iterations), static_cast<double>(cl2) / static_cast<double>(iterations));
#endif // __linux__
printf("\n");
return EXIT_SUCCESS;
+49
View File
@@ -0,0 +1,49 @@
# Helper so we don't have to repeat ourselves so much
# Usage: add_cpp_test(testname [COMPILE_ONLY] [SOURCES a.cpp b.cpp ...] [LABELS acceptance per_implementation ...])
# SOURCES defaults to testname.cpp if not specified.
function(add_cpp_test TEST_NAME)
# Parse arguments
cmake_parse_arguments(PARSE_ARGV 1 ARGS "COMPILE_ONLY;LIBRARY;WILL_FAIL" "" "SOURCES;LABELS")
if (NOT ARGS_SOURCES)
list(APPEND ARGS_SOURCES ${TEST_NAME}.cpp)
endif()
if (ARGS_COMPILE_ONLY)
list(APPEND ${ARGS_LABELS} compile)
endif()
# Add the compile target
if (ARGS_LIBRARY)
add_library(${TEST_NAME} STATIC ${ARGS_SOURCES})
else(ARGS_LIBRARY)
add_executable(${TEST_NAME} ${ARGS_SOURCES})
endif(ARGS_LIBRARY)
# Add test
if (ARGS_COMPILE_ONLY OR ARGS_LIBRARY)
add_test(
NAME ${TEST_NAME}
COMMAND ${CMAKE_COMMAND} --build . --target ${TEST_NAME} --config $<CONFIGURATION>
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
set_target_properties(${TEST_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
else()
add_test(${TEST_NAME} ${TEST_NAME})
endif()
if (ARGS_LABELS)
set_property(TEST ${TEST_NAME} APPEND PROPERTY LABELS ${ARGS_LABELS})
endif()
if (ARGS_WILL_FAIL)
set_property(TEST ${TEST_NAME} PROPERTY WILL_FAIL TRUE)
endif()
endfunction()
function(add_compile_only_test TEST_NAME)
add_test(
NAME ${TEST_NAME}
COMMAND ${CMAKE_COMMAND} --build . --target ${TEST_NAME} --config $<CONFIGURATION>
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
set_target_properties(${TEST_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
endfunction()
+221
View File
@@ -0,0 +1,221 @@
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message (STATUS "The simdjson repository appears to be standalone.")
option(SIMDJSON_JUST_LIBRARY "Build just the library, omit tests, tools and benchmarks" OFF)
message (STATUS "By default, we attempt to build everything.")
else()
message (STATUS "The simdjson repository appears to be used as a subdirectory.")
option(SIMDJSON_JUST_LIBRARY "Build just the library, omit tests, tools and benchmarks" ON)
message (STATUS "By default, we just build the library.")
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
set(SIMDJSON_IS_UNDER_GIT ON CACHE BOOL "Whether cmake is under git control")
message( STATUS "The simdjson repository appears to be under git." )
else()
set(SIMDJSON_IS_UNDER_GIT OFF CACHE BOOL "Whether cmake is under git control")
message( STATUS "The simdjson repository does not appear to be under git." )
endif()
#
# Flags used by exes and by the simdjson library (project-wide flags)
#
add_library(simdjson-flags INTERFACE)
add_library(simdjson-internal-flags INTERFACE)
target_link_libraries(simdjson-internal-flags INTERFACE simdjson-flags)
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
if(SIMDJSON_SANITIZE)
target_compile_options(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(simdjson-flags INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
# Ubuntu bug for GCC 5.0+ (safe for all versions)
if (CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(simdjson-flags INTERFACE -fuse-ld=gold)
endif()
endif()
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
if(SIMDJSON_SANITIZE)
message(WARNING "No build type selected and you have enabled the sanitizer. Consider setting CMAKE_BUILD_TYPE to Debug to help identify the eventual problems.")
endif()
endif()
if(MSVC)
option(SIMDJSON_BUILD_STATIC "Build a static library" ON) # turning it on disables the production of a dynamic library
else()
option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disables the production of a dynamic library
option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF)
endif()
option(SIMDJSON_COMPETITION "Compile competitive benchmarks" ON)
option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark benchmarks" ON)
if(SIMDJSON_COMPETITION)
message(STATUS "Using SIMDJSON_GOOGLE_BENCHMARKS")
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a target.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
# LTO seems to create all sorts of fun problems. Let us
# disable temporarily.
#include(CheckIPOSupported)
#check_ipo_supported(RESULT ltoresult)
#if(ltoresult)
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
#endif()
option(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING "Under Visual Studio, add Zi to the compile flag and DEBUG to the link file to add debugging information to the release build for easier profiling inside tools like VTune" OFF)
if(MSVC)
if("${MSVC_TOOLSET_VERSION}" STREQUAL "140")
# Visual Studio 2015 issues warnings and we tolerate it, cmake -G"Visual Studio 14" ..
target_compile_options(simdjson-internal-flags INTERFACE /W0 /sdl)
else()
# Recent version of Visual Studio expected (2017, 2019...). Prior versions are unsupported.
target_compile_options(simdjson-internal-flags INTERFACE /WX /W3 /sdl /w34714) # https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019
endif()
if(SIMDJSON_VISUAL_STUDIO_BUILD_WITH_DEBUG_INFO_FOR_PROFILING)
target_link_options(simdjson-flags INTERFACE /DEBUG )
target_compile_options(simdjson-flags INTERFACE /Zi)
endif()
else()
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
target_compile_options(simdjson-internal-flags INTERFACE -Werror -Wall -Wextra -Weffc++)
target_compile_options(simdjson-internal-flags INTERFACE -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wno-sign-conversion)
endif()
#
# Optional flags
#
#
# Implementation selection
#
set(SIMDJSON_ALL_IMPLEMENTATIONS "fallback;westmere;haswell;arm64")
set(SIMDJSON_IMPLEMENTATION "" CACHE STRING "Semicolon-separated list of implementations to include (${SIMDJSON_ALL_IMPLEMENTATIONS}). If this is not set, any implementations that are supported at compile time and may be selected at runtime will be included.")
foreach(implementation ${SIMDJSON_IMPLEMENTATION})
if(NOT (implementation IN_LIST SIMDJSON_ALL_IMPLEMENTATIONS))
message(ERROR "Implementation ${implementation} not supported by simdjson. Possible implementations: ${SIMDJSON_ALL_IMPLEMENTATIONS}")
endif()
endforeach(implementation)
set(SIMDJSON_EXCLUDE_IMPLEMENTATION "" CACHE STRING "Semicolon-separated list of implementations to exclude (haswell/westmere/arm64/fallback). By default, excludes any implementations that are unsupported at compile time or cannot be selected at runtime.")
foreach(implementation ${SIMDJSON_EXCLUDE_IMPLEMENTATION})
if(NOT (implementation IN_LIST SIMDJSON_ALL_IMPLEMENTATIONS))
message(ERROR "Implementation ${implementation} not supported by simdjson. Possible implementations: ${SIMDJSON_ALL_IMPLEMENTATIONS}")
endif()
endforeach(implementation)
foreach(implementation ${SIMDJSON_ALL_IMPLEMENTATIONS})
string(TOUPPER ${implementation} implementation_upper)
if(implementation IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION)
message(STATUS "Excluding implementation ${implementation} due to SIMDJSON_EXCLUDE_IMPLEMENTATION=${SIMDJSON_EXCLUDE_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=0")
elseif(implementation IN_LIST SIMDJSON_IMPLEMENTATION)
message(STATUS "Including implementation ${implementation} due to SIMDJSON_IMPLEMENTATION=${SIMDJSON_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=1")
elseif(SIMDJSON_IMPLEMENTATION)
message(STATUS "Excluding implementation ${implementation} due to SIMDJSON_IMPLEMENTATION=${SIMDJSON_IMPLEMENTATION}")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_IMPLEMENTATION_${implementation_upper}=0")
endif()
endforeach(implementation)
# TODO make it so this generates the necessary compiler flags to select the given implementation as the builtin automatically!
option(SIMDJSON_BUILTIN_IMPLEMENTATION "Select the implementation that will be used for user code. Defaults to the most universal implementation in SIMDJSON_IMPLEMENTATION (in the order ${SIMDJSON_ALL_IMPLEMENTATIONS}) if specified; otherwise, by default the compiler will pick the best implementation that can always be selected given the compiler flags." "")
if(SIMDJSON_BUILTIN_IMPLEMENTATION)
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_BUILTIN_IMPLEMENTATION=${SIMDJSON_BUILTIN_IMPLEMENTATION}")
else()
# Pick the most universal implementation out of the selected implementations (if any)
foreach(implementation ${SIMDJSON_ALL_IMPLEMENTATIONS})
if(implementation IN_LIST SIMDJSON_IMPLEMENTATION AND NOT (implementation IN_LIST SIMDJSON_EXCLUDE_IMPLEMENTATION))
message(STATUS "Selected implementation ${implementation} as builtin implementation based on ${SIMDJSON_IMPLEMENTATION}.")
target_compile_definitions(simdjson-flags INTERFACE "SIMDJSON_BUILTIN_IMPLEMENTATION=${implementation}")
break()
endif()
endforeach(implementation)
endif(SIMDJSON_BUILTIN_IMPLEMENTATION)
option(SIMDJSON_IMPLEMENTATION_HASWELL "Include the haswell implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_HASWELL)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_HASWELL is deprecated. Use SIMDJSON_IMPLEMENTATION=-haswell instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_HASWELL=0)
endif()
option(SIMDJSON_IMPLEMENTATION_WESTMERE "Include the westmere implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_WESTMERE)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_WESTMERE is deprecated. SIMDJSON_IMPLEMENTATION=-westmere instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_WESTMERE=0)
endif()
option(SIMDJSON_IMPLEMENTATION_ARM64 "Include the arm64 implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_ARM64)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_ARM64 is deprecated. Use SIMDJSON_IMPLEMENTATION=-arm64 instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_ARM64=0)
endif()
option(SIMDJSON_IMPLEMENTATION_FALLBACK "Include the fallback implementation" ON)
if(NOT SIMDJSON_IMPLEMENTATION_FALLBACK)
message(DEPRECATION "SIMDJSON_IMPLEMENTATION_FALLBACK is deprecated. Use SIMDJSON_IMPLEMENTATION=-fallback instead.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_IMPLEMENTATION_FALLBACK=0)
endif()
#
# Other optional flags
#
option(SIMDJSON_ONDEMAND_SAFETY_RAILS "Validate ondemand user code at runtime to ensure it is being used correctly. Defaults to ON for debug builds, OFF for release builds." $<IF:$<CONFIG:DEBUG>,ON,OFF>)
if(SIMDJSON_ONDEMAND_SAFETY_RAILS)
message(STATUS "Ondemand safety rails enabled. Ondemand user code will be checked at runtime. This will be slower than normal!")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_ONDEMAND_SAFETY_RAILS)
endif(SIMDJSON_ONDEMAND_SAFETY_RAILS)
option(SIMDJSON_BASH "Allow usage of bash within CMake" ON)
option(SIMDJSON_GIT "Allow usage of git within CMake" ON)
option(SIMDJSON_EXCEPTIONS "Enable simdjson's exception-throwing interface" ON)
if(NOT SIMDJSON_EXCEPTIONS)
message(STATUS "simdjson exception interface turned off. Code that does not check error codes will not compile.")
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_EXCEPTIONS=0)
endif()
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
if(SIMDJSON_ENABLE_THREADS)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)
target_link_libraries(simdjson-flags INTERFACE Threads::Threads)
target_link_libraries(simdjson-flags INTERFACE ${CMAKE_THREAD_LIBS_INIT})
target_compile_options(simdjson-flags INTERFACE ${CMAKE_THREAD_LIBS_INIT})
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_THREADS_ENABLED=1) # This will be set in the code automatically.
endif()
option(SIMDJSON_VERBOSE_LOGGING, "Enable verbose logging for internal simdjson library development." OFF)
if (SIMDJSON_VERBOSE_LOGGING)
target_compile_definitions(simdjson-flags INTERFACE SIMDJSON_VERBOSE_LOGGING=1)
endif()
if(SIMDJSON_USE_LIBCPP)
target_link_libraries(simdjson-flags INTERFACE -stdlib=libc++ -lc++abi)
# instead of the above line, we could have used
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi")
# The next line is needed empirically.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
# we update CMAKE_SHARED_LINKER_FLAGS, this gets updated later as well
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi")
endif(SIMDJSON_USE_LIBCPP)
# prevent shared libraries from depending on Intel provided libraries
if(${CMAKE_C_COMPILER_ID} MATCHES "Intel") # icc / icpc
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-intel")
endif()
install(TARGETS simdjson-flags EXPORT simdjson-config)
install(TARGETS simdjson-internal-flags EXPORT simdjson-config)
+24
View File
@@ -0,0 +1,24 @@
#
# ${SIMDJSON_USER_CMAKECACHE} contains the *user-specified* simdjson options so you can call cmake on
# another branch or repository with the same options.
#
# Not supported on Windows at present, because the only thing that uses it is checkperf, which we
# don't run on Windows.
#
set(SIMDJSON_USER_CMAKECACHE ${CMAKE_CURRENT_BINARY_DIR}/.simdjson-user-CMakeCache.txt)
if (MSVC)
add_custom_command(
OUTPUT ${SIMDJSON_USER_CMAKECACHE}
COMMAND findstr SIMDJSON_ ${PROJECT_BINARY_DIR}/CMakeCache.txt > ${SIMDJSON_USER_CMAKECACHE}.tmp
COMMAND findstr /v SIMDJSON_LIB_ ${SIMDJSON_USER_CMAKECACHE}.tmp > ${SIMDJSON_USER_CMAKECACHE}
VERBATIM # Makes it not do weird escaping with the command
)
else()
add_custom_command(
OUTPUT ${SIMDJSON_USER_CMAKECACHE}
COMMAND grep SIMDJSON_ ${PROJECT_BINARY_DIR}/CMakeCache.txt > ${SIMDJSON_USER_CMAKECACHE}.tmp
COMMAND grep -v SIMDJSON_LIB_ ${SIMDJSON_USER_CMAKECACHE}.tmp > ${SIMDJSON_USER_CMAKECACHE}
VERBATIM # Makes it not do weird escaping with the command
)
endif()
add_custom_target(simdjson-user-cmakecache DEPENDS ${SIMDJSON_USER_CMAKECACHE})
+98
View File
@@ -0,0 +1,98 @@
# Initializes a git submodule if it hasn't been initialized before
find_package(Git QUIET) # We want the library to build even if git is missing
if ((Git_FOUND) AND SIMDJSON_GIT AND (SIMDJSON_IS_UNDER_GIT))
message(STATUS "Git is available.")
# Does NOT attempt to update or otherwise modify git submodules that are already initialized.
function(initialize_submodule DIRECTORY)
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}/.git)
message(STATUS "${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}/.git does not exist. Initializing ${DIRECTORY} submodule ...")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init ${CMAKE_CURRENT_SOURCE_DIR}/${DIRECTORY}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_EXIT_CODE)
if(NOT GIT_EXIT_CODE EQUAL "0")
message(FATAL_ERROR "${GIT_EXECUTABLE} submodule update --init dependencies/${DIRECTORY} failed with exit code ${GIT_EXIT_CODE}, please checkout submodules")
endif()
endif()
endfunction(initialize_submodule)
if (SIMDJSON_GOOGLE_BENCHMARKS)
message (STATUS "'SIMDJSON_GOOGLE_BENCHMARKS' is requested, configuring..." )
option(BENCHMARK_ENABLE_TESTING OFF)
set(BENCHMARK_ENABLE_TESTING OFF)
option(BENCHMARK_ENABLE_INSTALL OFF)
set(BENCHMARK_ENABLE_INSTALL OFF)
initialize_submodule(benchmark)
add_subdirectory(benchmark)
endif()
if (SIMDJSON_COMPETITION)
initialize_submodule(cJSON)
add_library(competition-cJSON INTERFACE)
target_include_directories(competition-cJSON INTERFACE cJSON)
initialize_submodule(fastjson)
add_library(competition-fastjson INTERFACE)
target_include_directories(competition-fastjson INTERFACE fastjson/src fastjson/include)
initialize_submodule(gason)
add_library(competition-gason INTERFACE)
target_include_directories(competition-gason INTERFACE gason/src)
initialize_submodule(jsmn)
add_library(competition-jsmn INTERFACE)
target_include_directories(competition-jsmn INTERFACE jsmn)
initialize_submodule(json)
add_library(competition-json INTERFACE)
target_include_directories(competition-json INTERFACE json/single_include)
initialize_submodule(json11)
add_library(competition-json11 INTERFACE)
target_include_directories(competition-json11 INTERFACE json11)
add_library(competition-jsoncppdist INTERFACE)
target_include_directories(competition-jsoncppdist INTERFACE jsoncppdist)
initialize_submodule(rapidjson)
add_library(competition-rapidjson INTERFACE)
target_include_directories(competition-rapidjson INTERFACE rapidjson/include)
initialize_submodule(sajson)
add_library(competition-sajson INTERFACE)
target_include_directories(competition-sajson INTERFACE sajson/include)
initialize_submodule(ujson4c)
add_library(competition-ujson4c ujson4c/src/ujdecode.c)
target_include_directories(competition-ujson4c PUBLIC ujson4c/3rdparty ujson4c/src)
initialize_submodule(boost.json)
add_library(boostjson boost.json/src/src.cpp)
target_compile_definitions(boostjson PUBLIC BOOST_JSON_STANDALONE)
target_include_directories(boostjson PUBLIC boost.json/include)
initialize_submodule(yyjson)
add_library(yyjson yyjson/src/yyjson.c)
target_include_directories(yyjson PUBLIC yyjson/src)
add_library(competition-core INTERFACE)
target_link_libraries(competition-core INTERFACE competition-json competition-rapidjson competition-sajson competition-cJSON competition-jsmn boostjson yyjson)
add_library(competition-all INTERFACE)
target_link_libraries(competition-all INTERFACE competition-core competition-jsoncppdist competition-json11 competition-fastjson competition-gason competition-ujson4c)
endif()
initialize_submodule(cxxopts)
message(STATUS "We acquired cxxopts and we are adding it as a library and target.")
add_library(cxxopts INTERFACE)
target_include_directories(cxxopts INTERFACE cxxopts/include)
else()
message(STATUS "Git is unavailable.")
if(SIMDJSON_COMPETITION)
message (STATUS "'SIMDJSON_COMPETITION' is requested, but we cannot download the remote repositories." )
endif()
if(SIMDJSON_GOOGLE_BENCHMARKS)
message (STATUS "'SIMDJSON_GOOGLE_BENCHMARKS' is requested, but we cannot download the remote repositories." )
endif()
endif()
Vendored Submodule
+1
Submodule dependencies/benchmark added at 8982e1ee6a
Vendored Submodule
+1
Submodule dependencies/boost.json added at a0983f788b
Vendored Submodule
+1
Submodule dependencies/cxxopts added at 794c975287
Vendored Submodule
+1
Submodule dependencies/json added at a015b78e81
+8
View File
@@ -2652,10 +2652,18 @@ char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
LogicError::LogicError(String const& msg) : Exception(msg) {}
JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
#if __cpp_exceptions
throw RuntimeError(msg);
#else
abort();
#endif
}
JSONCPP_NORETURN void throwLogicError(String const& msg) {
#if __cpp_exceptions
throw LogicError(msg);
#else
abort();
#endif
}
// //////////////////////////////////////////////////////////////////
Vendored Submodule
+1
Submodule dependencies/yyjson added at aa33ec5a47
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+675
View File
@@ -0,0 +1,675 @@
The Basics
==========
An overview of what you need to know to use simdjson, with examples.
* [Requirements](#requirements)
* [Including simdjson](#including-simdjson)
* [Using simdjson as a CMake dependency](#using-simdjson-as-a-cmake-dependency)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++11 Support and string_view](#c11-support-and-string_view)
* [C++17 Support](#c17-support)
* [Minifying JSON strings without parsing](#minifying-json-strings-without-parsing)
* [UTF-8 validation (alone)](#utf-8-validation-alone)
* [JSON Pointer](#json-pointer)
* [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions)
* [Tree Walking and JSON Element Types](#tree-walking-and-json-element-types)
* [Newline-Delimited JSON (ndjson) and JSON lines](#newline-delimited-json-ndjson-and-json-lines)
* [Thread Safety](#thread-safety)
* [Standard Compliance](#standard-compliance)
Requirements
------------------
- A recent compiler (LLVM clang6 or better, GNU GCC 7.4 or better) on a 64-bit (ARM or x64 Intel/AMD) POSIX systems such as macOS, freeBSD or Linux. We require that the compiler supports the C++11 standard or better.
- Visual Studio 2017 or better under 64-bit Windows. Users should target a 64-bit build (x64) instead of a 32-bit build (x86). We support the LLVM clang compiler under Visual Studio (clangcl) as well as as the regular Visual Studio compiler. We also support MinGW 64-bit under Windows.
Including simdjson
------------------
To include simdjson, copy [simdjson.h](/singleheader/simdjson.h) and [simdjson.cpp](/singleheader/simdjson.cpp)
into your project. Then include it in your project with:
```c++
#include "simdjson.h"
using namespace simdjson; // optional
```
You can compile with:
```
c++ myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms were default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++17 myproject.cpp simdjson.cpp`).
- Visual Studio users should compile with the `_CRT_SECURE_NO_WARNINGS` flag to avoid warnings with respect to our use of standard C functions such as `fopen`.
Using simdjson with package managers
------------------
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
Using simdjson as a CMake dependency
------------------
You can include the simdjson as a CMake dependency by including the following lines in your `CMakeLists.txt`:
```cmake
include(FetchContent)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v0.5.0
GIT_SHALLOW TRUE)
set(SIMDJSON_JUST_LIBRARY ON CACHE INTERNAL "")
set(SIMDJSON_BUILD_STATIC ON CACHE INTERNAL "")
FetchContent_MakeAvailable(simdjson)
```
You should replace `GIT_TAG v0.5.0` by the version you need. If you omit `GIT_TAG v0.5.0`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
Elsewhere in your project, you can declare dependencies on simdjson with lines such as these:
```cmake
add_executable(myprogram myprogram.cpp)
target_link_libraries(myprogram simdjson)
```
We recommend CMake version 3.15 or better.
See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file). It works under Linux, FreeBSD, macOS and Windows (including Visual Studio).
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```c++
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
```c++
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```c++
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to streams and strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
The following code illustrates all of the above:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```C++
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++11 Support and string_view
-------------
The simdjson library builds on compilers supporting the [C++11 standard](https://en.wikipedia.org/wiki/C%2B%2B11). It is also a strict requirement: we have no plan to support older C++ compilers.
We represent parsed strings in simdjson using the `std::string_view` class. It avoids
the need to copy the data, as would be necessary with the `std::string` class. It also
avoids the pitfalls of null-terminated C strings.
The `std::string_view` class has become standard as part of C++17 but it is not always available
on compilers which only supports C++11. When we detect that `string_view` is natively
available, we define the macro `SIMDJSON_HAS_STRING_VIEW`.
When we detect that it is unavailable,
we use [string-view-lite](https://github.com/martinmoene/string-view-lite) as a
substitute. In such cases, we use the type alias `using string_view = nonstd::string_view;` to
offer the same API, irrespective of the compiler and standard library. The macro
`SIMDJSON_HAS_STRING_VIEW` will be *undefined* to indicate that we emulate `string_view`.
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```c++
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```c++
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
Minifying JSON strings without parsing
----------------------
In some cases, you may have valid JSON strings that you do not wish to parse but that you wish to minify. That is, you wish to remove all unnecessary spaces. We have a fast function for this purpose (`simdjson::minify(const char * input, size_t length, const char * output, size_t& new_length)`). This function does not validate your content, and it does not parse it. It is much faster than parsing the string and re-serializing it in minified form (`simdjson::minify(parser.parse())`). Usage is relatively simple. You must pass an input pointer with a length parameter, as well as an output pointer and an output length parameter (by reference). The output length parameter is not read, but written to. The output pointer should point to a valid memory region that is as large as the original string length. The input pointer and input length are read, but not written to.
```C++
// Starts with a valid JSON document as a string.
// It does not have to be null-terminated.
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
// Create a buffer to receive the minified string. Make sure that there is enough room (length bytes).
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
```
Though it does not validate the JSON input, it will detect when the document ends with an unterminated string. E.g., it would refuse to minify the string `"this string is not terminated` because of the missing final quote.
UTF-8 validation (alone)
----------------------
The simdjson library has fast functions to validate UTF-8 strings. They are many times faster than most functions commonly found in libraries. You can use our fast functions, even if you do not care about JSON.
```C++
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
bool is_ok = simdjson::validate_utf8(some_string, length);
```
The UTF-8 validation function merely checks that the input is valid UTF-8: it works with strings in general, not just JSON strings.
Your input string does not need any padding. Any string will do. The `validate_utf8` function does not do any memory allocation on the heap, and it does not throw exceptions.
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
We also extend the JSON Pointer support to include *relative* paths.
You can apply a JSON path to any node and the path gets interpreted relatively, as if the currrent node were a whole JSON document.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
for (dom::element car_element : cars) {
dom::object car;
simdjson::error_code error;
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```c++
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse a file and access some data, without triggering exceptions:
```C++
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```C++
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```C++
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```C++
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```c++
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```c++
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Newline-Delimited JSON (ndjson) and JSON lines
----------------------------------------------
The simdjson library also support multithreaded JSON streaming through a large file containing many
smaller JSON documents in either [ndjson](http://ndjson.org) or [JSON lines](http://jsonlines.org)
format. If your JSON documents all contain arrays or objects, we even support direct file
concatenation without whitespace. The concatenated file has no size restrictions (including larger
than 4GB), though each individual document must be no larger than 4 GB.
Here is a simple example, given `x.json` with this content:
```json
{ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 }
```
```c++
dom::parser parser;
dom::document_stream docs = parser.load_many("x.json");
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
In-memory ndjson strings can be parsed as well, with `parser.parse_many(string)`:
```c++
dom::parser parser;
auto json = R"({ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 })"_padded;
dom::document_stream docs = parser.parse_many(json);
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
Unlike `parser.parse`, both `parser.load_many(filename)` and `parser.parse_many(string)` may parse
"on demand" (lazily). That is, no parsing may have been done before you enter the loop
`for (dom::element doc : docs) {` and you should expect the parser to only ever fully parse one JSON
document at a time.
1. When calling `parser.load_many(filename)`, the file's content is loaded up in a memory buffer owned by the `parser`'s instance. Thus the file can be safely deleted after calling `parser.load_many(filename)` as the parser instance owns all of the data.
2. When calling `parser.parse_many(string)`, no copy is made of the provided string input. The provided memory buffer may be accessed each time a JSON document is parsed. Calling `parser.parse_many(string)` on a temporary string buffer (e.g., `docs = parser.parse_many("[1,2,3]"_padded)`) is unsafe (and will not compile) because the `document_stream` instance needs access to the buffer to return the JSON documents. In constrast, calling `doc = parser.parse("[1,2,3]"_padded)` is safe because `parser.parse` eagerly parses the input.
Both `load_many` and `parse_many` take an optional parameter `size_t batch_size` which defines the window processing size. It is set by default to a large value (`1000000` corresponding to 1 MB). None of your JSON documents should exceed this window size, or else you will get the error `simdjson::CAPACITY`. You cannot set this window size larger than 4 GB: you will get the error `simdjson::CAPACITY`. The smaller the window size is, the less memory the function will use. Setting the window size too small (e.g., less than 100 kB) may also impact performance negatively. Leaving it to 1 MB is expected to be a good choice, unless you have some larger documents.
If your documents are large (e.g., larger than a megabyte), then the `load_many` and `parse_many` functions are maybe ill-suited. They are really meant to support reading efficiently streams of relatively small documents (e.g., a few kilobytes each). If you have larger documents, you should use other functions like `parse`.
See [parse_many.md](parse_many.md) for detailed information and design.
Thread Safety
-------------
We built simdjson with thread safety in mind.
The simdjson library is single-threaded except for [`parse_many`](parse_many.md) which may use secondary threads under its control when the library is compiled with thread support.
We recommend using one `dom::parser` object per thread in which case the library is thread-safe.
It is unsafe to reuse a `dom::parser` object between different threads.
The parsed results (`dom::document`, `dom::element`, `array`, `object`) depend on the `dom::parser`, etc. therefore it is also potentially unsafe to use the result of the parsing between different threads.
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
parser for your CPU, is transparent and thread-safe.
Standard Compliance
--------------------
The simdjson library is fully compliant with the [RFC 8259](https://www.tbray.org/ongoing/When/201x/2017/12/14/rfc8259.html) JSON specification.
- The only insignificant whitespace characters allowed are the space, the horizontal tab, the line feed and the carriage return. In particular, a JSON document may not contain an unespaced null character.
- A single string or a single number is considered to be a valid JSON document.
- We fully validate the numbers according to the JSON specification. For example, the string `01` is not valid JSON document since the specification states that *leading zeros are not allowed*.
- The specification allows implementations to set limits on the range and precision of numbers accepted. We support 64-bit floating-point numbers as well as integer values.
- We parse integers and floating-point numbers as separate types which allows us to support all signed (two complement's) 64-bit integers, like a Java `long` or a C/C++ `long long` and all 64-bit unsigned integers. When we cannot represent exactly an integer as a signed or unsigned 64-bit value, we reject the JSON document.
- We support the full range of 64-bit floating-point numbers (binary64). The values range from `std::numeric_limits<double>::lowest()` to `std::numeric_limits<double>::max()`, so from -1.7976e308 all the way to 1.7975e308. Extreme values (less or equal to -1e308, greater or equal to 1e308) are rejected: we refuse to parse the input document. Numbers are parsed with with a perfect accuracy (ULP 0): the nearest floating-point value is chosen, rounding to even when needed. If you serialized your floating-point numbers with 17 significant digits in a standard compliant manner, the simdjson library is guaranteed to recovere the example same numbers, exactly.
- The specification states that JSON text exchanged between systems that are not part of a closed ecosystem MUST be encoded using UTF-8. The simdjson library does full UTF-8 validation as part of the parsing. The specification states that implementations MUST NOT add a byte order mark: the simdjson library rejects documents starting with a byte order mark.
- The simdjson library validates string content for unescaped characters. Unescaped line breaks and tabs in strings are not allowed.
- The simdjson library accepts objects with repeated keys: all of the name/value pairs, including duplicates, are reported. We do not enforce key uniqueness.
- The specification states that an implementation may set limits on the size of texts that it accepts. The simdjson library limits single JSON documents to 4 GiB. It will refuse to parse a JSON document larger than 4294967295 bytes. (This limitation does not apply to streams of JSON documents, only to single JSON documents.)
- The specification states that an implementation may set limits on the maximum depth of nesting. By default, the simdjson will refuse to parse documents with a depth exceeding 1024.
Backwards Compatibility
-----------------------
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
number of other include files such as `document.h` or `ParsedJson.h` alongside `simdjson.h`; these headers
may be moved or removed in future versions.
+634
View File
@@ -0,0 +1,634 @@
The Basics
==========
An overview of what you need to know to use simdjson, with examples.
Requirements
------------------
- A recent compiler (LLVM clang6 or better, GNU GCC 7 or better) on a 64-bit (ARM or x64 Intel/AMD) POSIX systems such as macOS, freeBSD or Linux. We require that the compiler supports the C++11 standard or better.
- Visual Studio 2017 or better under 64-bit Windows. Users should target a 64-bit build (x64) instead of a 32-bit build (x86). We support the LLVM clang compiler under Visual Studio (clangcl) as well as as the regular Visual Studio compiler.
Including simdjson
------------------
To include simdjson, copy the simdjson.h and simdjson.cpp files from the singleheader directory
into your project. Then include the header file in your project with:
```
#include "simdjson.h"
using namespace simdjson; // optional
```
You can compile with:
```
c++ myproject.cpp simdjson.cpp
```
Note:
- Users on macOS and other platforms were default compilers do not provide C++11 compliant by default should request it with the appropriate flag (e.g., `c++ -std=c++17 myproject.cpp simdjson.cpp`).
- Visual Studio users should compile with the `_CRT_SECURE_NO_WARNINGS` flag to avoid warnings with respect to our use of standard C functions such as `fopen`.
Using simdjson with package managers
------------------
You can install the simdjson library on your system or in your project using multiple package managers such as MSYS2, the conan package manager, vcpkg, brew, the apt package manager (debian-based Linux systems), the FreeBSD package manager (FreeBSD), and so on. [Visit our wiki for more details](https://github.com/simdjson/simdjson/wiki/Installing-simdjson-with-a-package-manager).
Using simdjson as a CMake dependency
------------------
You can include the simdjson as a CMake dependency by including the following lines in your `CMakeLists.txt`:
```
include(FetchContent)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v0.4.7
GIT_SHALLOW TRUE)
set(SIMDJSON_JUST_LIBRARY ON CACHE INTERNAL "")
set(SIMDJSON_BUILD_STATIC ON CACHE INTERNAL "")
FetchContent_MakeAvailable(simdjson)
```
You should replace `GIT_TAG v0.5.0` by the version you need. If you omit `GIT_TAG v0.5.0`, you will work from the main branch of simdjson: we recommend that if you are working on production code,
Elsewhere in your project, you can declare dependencies on simdjson with lines such as these:
```
add_executable(myprogram myprogram.cpp)
target_link_libraries(myprogram simdjson)
```
We recommend CMake version 3.15 or better.
See [our CMake demonstration](https://github.com/simdjson/cmake_demo_single_file). It works under Linux, FreeBSD, macOS and Windows (including Visual Studio).
The CMake build in simdjson can be taylored with a few variables. You can see the available variables and their default values by entering the `cmake -LA` command.
The Basics: Loading and Parsing JSON Documents
----------------------------------------------
The simdjson library offers a simple DOM tree API, which you can access by creating a
`dom::parser` and calling the `load()` method:
```
dom::parser parser;
dom::element doc = parser.load(filename); // load and parse a file
```
Or by creating a padded string (for efficiency reasons, simdjson requires a string with
SIMDJSON_PADDING bytes at the end) and calling `parse()`:
```
dom::parser parser;
dom::element doc = parser.parse("[1,2,3]"_padded); // parse a string, the _padded suffix creates a simdjson::padded_string instance
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
During the`load` or `parse` calls, neither the input file nor the input string are ever modified. After calling `load` or `parse`, the source (either a file or a string) can be safely discarded. All of the JSON data is stored in the `parser` instance. The parsed document is also immutable in simdjson: you do not modify it by accessing it.
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson.
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
Using the Parsed JSON
---------------------
Once you have an element, you can navigate it with idiomatic C++ iterators, operators and casts.
* **Extracting Values (with exceptions):** You can cast a JSON element to a native type: `double(element)` or
`double x = json_element`. This works for double, uint64_t, int64_t, bool,
dom::object and dom::array. An exception is thrown if the cast is not possible.
* **Extracting Values (without exceptions):** You can use a variant usage of `get()` with error codes to avoid exceptions. You first declare the variable of the appropriate type (`double`, `uint64_t`, `int64_t`, `bool`,
`dom::object` and `dom::array`) and pass it by reference to `get()` which gives you back an error code: e.g.,
```
simdjson::error_code error;
simdjson::padded_string numberstring = "1.2"_padded; // our JSON input ("1.2")
simdjson::dom::parser parser;
double value; // variable where we store the value to be parsed
error = parser.parse(numberstring).get(value);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
* **Object Iteration:** You can iterate through an object's fields, too: `for (auto [key, value] : object)`
* **Array Index:** To get at an array value by index, use the at() method: `array.at(0)` gets the
first element.
> Note that array[0] does not compile, because implementing [] gives the impression indexing is a
> O(1) operation, which it is not presently in simdjson. Instead, you should iterate over the elements
> using a for-loop, as in our examples.
* **Array and Object size** Given an array or an object, you can get its size (number of elements or keys)
with the `size()` method.
* **Checking an Element Type:** You can check an element's type with `element.type()`. It
returns an `element_type` with values such as `simdjson::dom::element_type::ARRAY`, `simdjson::dom::element_type::OBJECT`, `simdjson::dom::element_type::INT64`, `simdjson::dom::element_type::UINT64`,`simdjson::dom::element_type::DOUBLE`, `simdjson::dom::element_type::BOOL` or, `simdjson::dom::element_type::NULL_VALUE`.
* **Output to Streams and Strings:** Given a document or an element (or node) out of a JSON document, you can output a minified string version using the C++ stream idiom (`out << element`). You can also request the construction of a minified string version (`simdjson::minify(element)`).
The following code illustrates all of the above:
```
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
// Iterating through an array of objects
for (dom::object car : parser.parse(cars_json)) {
// Accessing a field by name
cout << "Make/Model: " << car["make"] << "/" << car["model"] << endl;
// Casting a JSON element to an integer
uint64_t year = car["year"];
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
for (double tire_pressure : car["tire_pressure"]) {
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is a different example illustrating the same ideas:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
// Parse and iterate through an array of objects
for (dom::object obj : parser.parse(abstract_json)) {
for(const auto& key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v = parser.parse(abstract_json)["str"]["123"]["abc"];
cout << "number: " << v << endl;
```
C++11 Support and string_view
-------------
The simdjson library builds on compilers supporting the [C++11 standard](https://en.wikipedia.org/wiki/C%2B%2B11). It is also a strict requirement: we have no plan to support older C++ compilers.
We represent parsed strings in simdjson using the `std::string_view` class. It avoids
the need to copy the data, as would be necessary with the `std::string` class. It also
avoids the pitfalls of null-terminated C strings.
The `std::string_view` class has become standard as part of C++17 but it is not always available
on compilers which only supports C++11. When we detect that `string_view` is natively
available, we define the macro `SIMDJSON_HAS_STRING_VIEW`.
When we detect that it is unavailable,
we use [string-view-lite](https://github.com/martinmoene/string-view-lite) as a
substitute. In such cases, we use the type alias `using string_view = nonstd::string_view;` to
offer the same API, irrespective of the compiler and standard library. The macro
`SIMDJSON_HAS_STRING_VIEW` will be *undefined* to indicate that we emulate `string_view`.
C++17 Support
-------------
While the simdjson library can be used in any project using C++ 11 and above, field iteration has special support C++ 17's destructuring syntax. For example:
```
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (error) { cerr << error << endl; return; }
for (auto [key, value] : object) {
cout << key << " = " << value << endl;
}
```
For comparison, here is the C++ 11 version of the same code:
```
// C++ 11 version for comparison
padded_string json = R"( { "foo": 1, "bar": 2 } )"_padded;
dom::parser parser;
dom::object object;
auto error = parser.parse(json).get(object);
if (!error) { cerr << error << endl; return; }
for (dom::key_value_pair field : object) {
cout << field.key << " = " << field.value << endl;
}
```
Minifying JSON strings without parsing
----------------------
In some cases, you may have valid JSON strings that you do not wish to parse but that you wish to minify. That is, you wish to remove all unnecessary spaces. We have a fast function for this purpose (`simdjson::minify(const char * input, size_t length, const char * output, size_t& new_length)`). This function does not validate your content, and it does not parse it. It is much faster than parsing the string and re-serializing it in minified form (`simdjson::minify(parser.parse())`). Usage is relatively simple. You must pass an input pointer with a length parameter, as well as an output pointer and an output length parameter (by reference). The output length parameter is not read, but written to. The output pointer should point to a valid memory region that is as large as the original string length. The input pointer and input length are read, but not written to.
```
// Starts with a valid JSON document as a string.
// It does not have to be null-terminated.
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
// Create a buffer to receive the minified string. Make sure that there is enough room (length bytes).
std::unique_ptr<char[]> buffer{new char[length]};
size_t new_length{}; // It will receive the minified length.
auto error = simdjson::minify(some_string, length, buffer.get(), new_length);
// The buffer variable now has "[1,2,3,4]" and new_length has value 9.
```
Though it does not validate the JSON input, it will detect when the document ends with an unterminated string. E.g., it would refuse to minify the string `"this string is not terminated` because of the missing final quote.
UTF-8 validation (alone)
----------------------
The simdjson library has fast functions to validate UTF-8 strings. They are many times faster than most functions commonly found in libraries. You can use our fast functions, even if you do not care about JSON.
```
const char * some_string = "[ 1, 2, 3, 4] ";
size_t length = std::strlen(some_string);
bool is_ok = simdjson::validate_utf8(some_string, length);
```
The UTF-8 validation function merely checks that the input is valid UTF-8: it works with strings in general, not just JSON strings.
Your input string does not need any padding. Any string will do. The `validate_utf8` function does not do any memory allocation on the heap, and it does not throw exceptions.
JSON Pointer
------------
The simdjson library also supports [JSON pointer](https://tools.ietf.org/html/rfc6901) through the
`at_pointer()` method, letting you reach further down into the document in a single call:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
```
A JSON Path is a sequence of segments each starting with the '/' character. Within arrays, an integer
index allows you to select the indexed node. Within objects, the string value of the key allows you to
select the value. If your keys contain the characters '/' or '~', they must be escaped as '~1' and
'~0' respectively. An empty JSON Path refers to the whole document.
We also extend the JSON Pointer support to include *relative* paths.
You can apply a JSON path to any node and the path gets interpreted relatively, as if the currrent node were a whole JSON document.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element cars = parser.parse(cars_json);
cout << cars.at_pointer("/0/tire_pressure/1") << endl; // Prints 39.9
for (dom::element car_element : cars) {
dom::object car;
simdjson::error_code error;
if ((error = car_element.get(car))) { std::cerr << error << std::endl; return; }
double x = car.at_pointer("/tire_pressure/1");
cout << x << endl; // Prints 39.9, 31 and 30
}
```
Error Handling
--------------
All simdjson APIs that can fail return `simdjson_result<T>`, which is a &lt;value, error_code&gt;
pair. You can retrieve the value with .get(), like so:
```
dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) { cerr << error << endl; exit(1); }
```
When you use the code this way, it is your responsibility to check for error before using the
result: if there is an error, the result value will not be valid and using it will caused undefined
behavior.
We can write a "quick start" example where we attempt to parse a file and access some data, without triggering exceptions:
```
#include "simdjson.h"
int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets;
auto error = parser.load("twitter.json").get(tweets);
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::dom::element res;
if ((error = tweets["search_metadata"]["count"].get(res))) {
std::cerr << "could not access keys" << std::endl;
return EXIT_FAILURE;
}
std::cout << res << " results." << std::endl;
}
```
### Error Handling Example
This is how the example in "Using the Parsed JSON" could be written using only error code checking:
```
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::array cars;
auto error = parser.parse(cars_json).get(cars);
if (error) { cerr << error << endl; exit(1); }
// Iterating through an array of objects
for (dom::element car_element : cars) {
dom::object car;
if ((error = car_element.get(car))) { cerr << error << endl; exit(1); }
// Accessing a field by name
std::string_view make, model;
if ((error = car["make"].get(make))) { cerr << error << endl; exit(1); }
if ((error = car["model"].get(model))) { cerr << error << endl; exit(1); }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year;
if ((error = car["year"].get(year))) { cerr << error << endl; exit(1); }
cout << "- This car is " << 2020 - year << "years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
dom::array tire_pressure_array;
if ((error = car["tire_pressure"].get(tire_pressure_array))) { cerr << error << endl; exit(1); }
for (dom::element tire_pressure_element : tire_pressure_array) {
double tire_pressure;
if ((error = tire_pressure_element.get(tire_pressure))) { cerr << error << endl; exit(1); }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
// Writing out all the information about the car
for (auto field : car) {
cout << "- " << field.key << ": " << field.value << endl;
}
}
```
Here is another example:
```
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
dom::parser parser;
dom::array array;
auto error = parser.parse(abstract_json).get(array);
if (error) { cerr << error << endl; exit(1); }
// Iterate through an array of objects
for (dom::element elem : array) {
dom::object obj;
if ((error = elem.get(obj))) { cerr << error << endl; exit(1); }
for (auto & key_value : obj) {
cout << "key: " << key_value.key << " : ";
dom::object innerobj;
if ((error = key_value.value.get(innerobj))) { cerr << error << endl; exit(1); }
double va, vb;
if ((error = innerobj["a"].get(va))) { cerr << error << endl; exit(1); }
cout << "a: " << va << ", ";
if ((error = innerobj["b"].get(vc))) { cerr << error << endl; exit(1); }
cout << "b: " << vb << ", ";
int64_t vc;
if ((error = innerobj["c"].get(vc))) { cerr << error << endl; exit(1); }
cout << "c: " << vc << endl;
}
}
```
And another one:
```
auto abstract_json = R"(
{ "str" : { "123" : {"abc" : 3.14 } } } )"_padded;
dom::parser parser;
double v;
auto error = parser.parse(abstract_json)["str"]["123"]["abc"].get(v);
if (error) { cerr << error << endl; exit(1); }
cout << "number: " << v << endl;
```
Notice how we can string several operations (`parser.parse(abstract_json)["str"]["123"]["abc"].get(v)`) and only check for the error once, a strategy we call *error chaining*.
The next two functions will take as input a JSON document containing an array with a single element, either a string or a number. They return true upon success.
```
simdjson::dom::parser parser{};
bool parse_double(const char *j, double &d) {
auto error = parser.parse(j, std::strlen(j))
.at(0)
.get(d, error);
if (error) { return false; }
return true;
}
bool parse_string(const char *j, std::string &s) {
std::string_view answer;
auto error = parser.parse(j,strlen(j))
.at(0)
.get(answer, error);
if (error) { return false; }
s.assign(answer.data(), answer.size());
return true;
}
```
### Exceptions
Users more comfortable with an exception flow may choose to directly cast the `simdjson_result<T>` to the desired type:
```
dom::element doc = parser.parse(json); // Throws an exception if there was an error!
```
When used this way, a `simdjson_error` exception will be thrown if an error occurs, preventing the
program from continuing if there was an error.
Tree Walking and JSON Element Types
-----------------------------------
Sometimes you don't necessarily have a document with a known type, and are trying to generically
inspect or walk over JSON elements. To do that, you can use iterators and the type() method. For
example, here's a quick and dirty recursive function that verbosely prints the JSON document as JSON
(* ignoring nuances like trailing commas and escaping strings, for brevity's sake):
```
void print_json(dom::element element) {
switch (element.type()) {
case dom::element_type::ARRAY:
cout << "[";
for (dom::element child : dom::array(element)) {
print_json(child);
cout << ",";
}
cout << "]";
break;
case dom::element_type::OBJECT:
cout << "{";
for (dom::key_value_pair field : dom::object(element)) {
cout << "\"" << field.key << "\": ";
print_json(field.value);
}
cout << "}";
break;
case dom::element_type::INT64:
cout << int64_t(element) << endl;
break;
case dom::element_type::UINT64:
cout << uint64_t(element) << endl;
break;
case dom::element_type::DOUBLE:
cout << double(element) << endl;
break;
case dom::element_type::STRING:
cout << std::string_view(element) << endl;
break;
case dom::element_type::BOOL:
cout << bool(element) << endl;
break;
case dom::element_type::NULL_VALUE:
cout << "null" << endl;
break;
}
}
void basics_treewalk_1() {
dom::parser parser;
print_json(parser.load("twitter.json"));
}
```
Newline-Delimited JSON (ndjson) and JSON lines
----------------------------------------------
The simdjson library also support multithreaded JSON streaming through a large file containing many
smaller JSON documents in either [ndjson](http://ndjson.org) or [JSON lines](http://jsonlines.org)
format. If your JSON documents all contain arrays or objects, we even support direct file
concatenation without whitespace. The concatenated file has no size restrictions (including larger
than 4GB), though each individual document must be no larger than 4 GB.
Here is a simple example, given "x.json" with this content:
```
{ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 }
```
```
dom::parser parser;
dom::document_stream docs = parser.load_many("x.json");
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
In-memory ndjson strings can be parsed as well, with `parser.parse_many(string)`:
```
dom::parser parser;
auto json = R"({ "foo": 1 }
{ "foo": 2 }
{ "foo": 3 })"_padded;
dom::document_stream docs = parser.parse_many(json);
for (dom::element doc : docs) {
cout << doc["foo"] << endl;
}
// Prints 1 2 3
```
Unlike `parser.parse`, both `parser.load_many(filename)` and `parser.parse_many(string)` may parse
"on demand" (lazily). That is, no parsing may have been done before you enter the loop
`for (dom::element doc : docs) {` and you should expect the parser to only ever fully parse one JSON
document at a time.
1. When calling `parser.load_many(filename)`, the file's content is loaded up in a memory buffer owned by the `parser`'s instance. Thus the file can be safely deleted after calling `parser.load_many(filename)` as the parser instance owns all of the data.
2. When calling `parser.parse_many(string)`, no copy is made of the provided string input. The provided memory buffer may be accessed each time a JSON document is parsed. Calling `parser.parse_many(string)` on a temporary string buffer (e.g., `docs = parser.parse_many("[1,2,3]"_padded)`) is unsafe (and will not compile) because the `document_stream` instance needs access to the buffer to return the JSON documents. In constrast, calling `doc = parser.parse("[1,2,3]"_padded)` is safe because `parser.parse` eagerly parses the input.
Both `load_many` and `parse_many` take an optional parameter `size_t batch_size` which defines the window processing size. It is set by default to a large value (`1000000` corresponding to 1 MB). None of your JSON documents should exceed this window size, or else you will get the error `simdjson::CAPACITY`. You cannot set this window size larger than 4 GB: you will get the error `simdjson::CAPACITY`. The smaller the window size is, the less memory the function will use. Setting the window size too small (e.g., less than 100 kB) may also impact performance negatively. Leaving it to 1 MB is expected to be a good choice, unless you have some larger documents.
Thread Safety
-------------
We built simdjson with thread safety in mind.
The simdjson library is single-threaded except for `parse_many` which may use secondary threads under its control when the library is compiled with thread support.
We recommend using one `dom::parser` object per thread in which case the library is thread-safe.
It is unsafe to reuse a `dom::parser` object between different threads.
The parsed results (`dom::document`, `dom::element`, `array`, `object`) depend on the `dom::parser`, etc. therefore it is also potentially unsafe to use the result of the parsing between different threads.
The CPU detection, which runs the first time parsing is attempted and switches to the fastest
parser for your CPU, is transparent and thread-safe.
Backwards Compatibility
-----------------------
The only header file supported by simdjson is `simdjson.h`. Older versions of simdjson published a
number of other include files such as `document.h` or `ParsedJson.h` alongside `simdjson.h`; these headers
may be moved or removed in future versions.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 67 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

+119
View File
@@ -0,0 +1,119 @@
CPU Architecture-Specific Implementations
=========================================
* [Overview](#overview)
* [Runtime CPU Detection](#runtime-cpu-detection)
* [Inspecting the Detected Implementation](#inspecting-the-detected-implementation)
* [Querying Available Implementations](#querying-available-implementations)
* [Manually Selecting the Implementation](#manually-selecting-the-implementation)
* [Checking that an Implementation can Run on your System](#checking-that-an-implementation-can-run-on-your-system)
Overview
--------
The simdjson library takes advantage of SIMD instruction sets such as NEON, SSE and AVX to achieve
much of its speed. Because these instruction sets work differently, simdjson has to compile a
different version of the JSON parser for different CPU architectures, often with different
algorithms to take better advantage of a given CPU!
The current implementations are:
* haswell: AVX2 (2013 Intel Haswell or later)
* westmere: SSE4.2 (2010 Westmere or later).
* arm64: 64-bit ARMv8-A NEON
* fallback: A generic implementation that runs on any 64-bit processor.
In many cases, you don't know where your compiled binary is going to run, so simdjson automatically
compiles *all* the implementations into the executable. On Intel, it will include 3 implementations
(haswell, westmere and fallback), and on ARM it will include 2 (arm64 and fallback).
If you know more about where you're going to run and want to save the space, you can disable any of
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is HASWELL,
WESTMERE, ARM64 and FALLBACK).
The simdjson library automatically sets header flags for each implementation as it compiles; there
is no need to set architecture-specific flags yourself (e.g., `-mavx2`, `/AVX2` or
`-march=haswell`), and it may even break runtime dispatch and your binaries will fail to run on
older processors.
Runtime CPU Detection
---------------------
When you first use simdjson, it will detect the CPU you're running on, and swap over to the fastest
implementation for it. This is a small, one-time cost and for many people will be paid the first
time they call `parse()` or `load()`.
Inspecting the Detected Implementation
--------------------------------------
You can check what implementation is running with `active_implementation`:
```c++
cout << "simdjson v" << STRINGIFY(SIMDJSON_VERSION) << endl;
cout << "Detected the best implementation for your machine: " << simdjson::active_implementation->name();
cout << "(" << simdjson::active_implementation->description() << ")" << endl;
```
Implementation detection will happen in this case when you first call `name()`.
Querying Available Implementations
----------------------------------
You can list all available implementations, regardless of which one was selected:
```c++
for (auto implementation : simdjson::available_implementations) {
cout << implementation->name() << ": " << implementation->description() << endl;
}
```
And look them up by name:
```c++
cout << simdjson::available_implementations["fallback"]->description() << endl;
```
Though the fallback implementation should always be available, others might be missing. When
an implementation is not available, the bracket call `simdjson::available_implementations[name]`
will return the null pointer.
The available implementations have been compiled but may not necessarily be run safely on your system
see [Checking that an Implementation can Run on your System](#checking-that-an-implementation-can-run-on-your-system).
Manually Selecting the Implementation
-------------------------------------
If you're trying to do performance tests or see how different implementations of simdjson run, you
can select the CPU architecture yourself:
```c++
// Use the fallback implementation, even though my machine is fast enough for anything
simdjson::active_implementation = simdjson::available_implementations["fallback"];
```
You are responsible for ensuring that the requirements of the selected implementation match your current system.
Furthermore, you should check that the implementation is available before setting it to `simdjson::active_implementation`
by comparing it with the null pointer.
```c++
auto my_implementation = simdjson::available_implementations["haswell"];
if(! my_implementation) { exit(1); }
if(! my_implementation->supported_by_runtime_system()) { exit(1); }
simdjson::active_implementation = my_implementation;
```
Checking that an Implementation can Run on your System
-------------------------------------
You should call `supported_by_runtime_system()` to compare the processor's features with the need of the implementation.
```c++
for (auto implementation : simdjson::available_implementations) {
if(implementation->supported_by_runtime_system()) {
cout << implementation->name() << ": " << implementation->description() << endl;
}
}
```
The call to `supported_by_runtime_system()` maybe relatively expensive. Do not call `supported_by_runtime_system()` each
time you parse a JSON input (for example). It is meant to be called a handful of times at most in the life of a program.
+629
View File
@@ -0,0 +1,629 @@
A Better Way to Parse Documents?
====================
Whether we parse JSON or XML, or any other serialized format, there are relatively few common strategies:
- The most established approach is the construction of document-object-model (DOM).
- Another established approach is a event-based approach (like SAX, SAJ).
- Another popular approach is the schema-based deserialization model.
We propose an approach that is as easy to use and often as flexible as the DOM approach, yet as fast and
efficient as the schema-based or event-based approaches. We call this new approach "On Demand". The
simdjson On Demand API offers a familiar, friendly DOM API and
provides the performance of just-in-time parsing on top of the simdjson superior performance.
To achieve ease of use, we mimicked the *form* of a traditional DOM API: you can iterate over
arrays, look up fields in objects, and extract native values like `double`, `uint64_t`, `string` and `bool`.
To achieve performance, we introduced some key limitations that make the DOM API *streaming*:
array/object iteration cannot be restarted, and fields must be looked up in order, and string/number
values can only be parsed once. If these limitations are acceptable to you, the On Demand API could
help you write maintainable applications with a computation efficiency that is difficult to surpass.
A code example illustrates our API from a programmer's point of view:
```c++
ondemand::parser parser;
auto doc = parser.iterate(json);
for (auto tweet : doc["statuses"]) {
std::string_view text = tweet["text"];
std::string_view screen_name = tweet["user"]["screen_name"];
std::string_view screen_name;
{
ondemand::object user = tweet["user"];
screen_name = user["screen_name"];
}
uint64_t retweets = tweet["retweet_count"];
uint64_t favorites = tweet["favorite_count"];
cout << screen_name << " (" << retweets << " retweets / " << favorites << " favorites): " << text << endl;
}
```
Such code would be apply to a JSON document such as the following JSON mimicking a sample result from the Twitter API:
```json
{
"statuses": [{
"text": "@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖",
"user": {
"name": "AYUMI",
"screen_name": "ayuu0123",
"followers_count": 262,
"friends_count": 252
},
"retweet_count": 0,
"favorite_count": 0
},
{
"text": "RT @KATANA77: えっそれは・・・(一同) http://t.co/PkCJAcSuYK",
"user": {
"name": "RT&ファボ魔のむっつんさっm",
"screen_name": "yuttari1998",
"followers_count": 95,
"friends_count": 158
},
"retweet_count": 82,
"favorite_count": 42
}
]
}
```
This streaming approach means that unused fields and values are not parsed or
converted, thus saving space and time. In our example, the `"name"`, `"followers_count"`,
and `"friends_count"` keys and matching values are skipped.
Further, the On Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
`int`, `string`, or `bool`). In our example, when accessing the key-value pair `"retweet_count": 82`, the parser
may not convert the pair of characters `82` to the binary integer 82. Because the programmer specifies the data
type, we avoid branch mispredictions related to data type determination and improve the performance.
We expect users of an On Demand API to work in terms of a JSON dialect, which is a set of expectations and
specifications that come in addition to the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt).
The On Demand approach is designed around several principles:
* **Streaming (\*):** It avoids preparsing values, keeping the memory usage and the latency down.
* **Forward-Only:** To prevent reiteration of the same values and to keep the number of variables down (literally), only a single index is maintained and everything uses it (even if you have nested for loops). This means when you are going through an array of arrays, for example, that the inner array loop will advance the index to the next comma, and the array can just pick it up and look at it.
* **Natural Iteration:** A JSON array or object can be iterated with a normal C++ for loop. Nested arrays and objects are supported by nested for loops.
* **Use-Specific Parsing:** Parsing is always specific to the type required by the programmer. For example, if the programmer asks for an unsigned integer, we just start parsing digits. If there were no digits, we toss an error. There are even different parsers for `double`, `uint64_t` and `int64_t` values. This use-specific parsing avoids the branchiness of a generic "type switch," and makes the code more inlineable and compact.
* **Validate What You Use:** On Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
To understand why On Demand is different, it is helpful to review the major
approaches to parsing and parser APIs in use today.
### DOM Parsers
Many of the most usable, popular JSON APIs (including simdjson) deserialize into a **DOM**: an intermediate tree of
objects, arrays and values. In this model, we convert the input data all at once into a tree-like structure (the DOM).
The DOM is then accessed by the programmer like any other in-memory data structure. The resulting API let
you refer to each array or object separately, using familiar techniques like iteration (`for (auto value : array)`)
or indexing (`object["key"]`). In some cases, the values are even deserialized directly into familiar C++ constructs like vectors and
maps.
The DOM approach is conceptually simple and "programmer friendly". Using the
DOM tree is often easy enough that many users use the DOM as-is instead of creating their own
their own custom data structures.
The DOM approach was the only way to parse JSON documents up to version 0.6 of the simdjson library.
Our DOM API looks similar to our On Demand example, except
it calls `parse` instead of `iterate`:
```c++
dom::parser parser;
auto doc = parser.parse(json);
for (auto tweet : doc["statuses"]) {
std::string_view text = tweet["text"];
std::string_view screen_name = tweet["user"]["screen_name"];
uint64_t retweets = tweet["retweet_count"];
uint64_t favorites = tweet["favorite_count"];
cout << screen_name << " (" << retweets << " retweets / " << favorites << " favorites): " << text << endl;
}
```
Pros of the DOM approach:
* Straightforward, programmer-friendly interface (arrays and objects).
* Safe: all of the input data has been validated before it is accessed.
* All of the JSON document is available at once to the programmer.
Cons of the DOM approach:
* The memory usage scales linearly with the size of the input document.
* Parses and stores everything, using memory and CPU cycles even on unused values.
* Performance drain from [type blindness](#type-blindness).
What the simdjson library demonstrates is that a DOM API may be quite fast indeed: we can parse files at speeds
of several gigabytes per second. However, in some instances, it may be possible to achieve even higher speeds.
### Event-Based Parsers (SAX, SAJ, etc.)
The event-based model (originally from the "Streaming API for XML") uses streaming to eliminate the cost of
parsing and storing the entire JSON. In the event-based model, a core JSON engine parses the JSON document
piece by piece, but instead of stuffing values in a DOM tree, it passes each value to a callback function,
letting the user decide for themselves how to handle it. In such a model, the programmer may need to provide functions
for all possible events (a number, a string, a new object, a new array, the array ends, the object ends, and so on).
This allows programmers to work with much larger files without running out of memory.
The drawback is complexity: event-based APIs generally have you define a single callback for each type
(e.g. `string_field(std::string_view key, std::string_view value)`). Because of this, the programmer suffers
from context blindness: when they find a string they have to check where it is before they know what to
do with it. Is this string the text of the tweet, the screen name, or something else? Are we even in
a tweet right now, or is this from some other place in the document
entirely? Though an event-based approach may allow superior performance, it is demanding of the programmer
who must efficiently keep track of its current state within the JSON input.
The following is event-based example of the Twitter problem we have reviewed in the DOM and On Demand
examples. To make it short enough to use as an example at all, it has heavily redacted: it only solves
a part of the problem (does not get user.screen_name), it has bugs (it does not handle sub-objects
in a tweet at all), and it uses a theoretical, simple event-based API that minimizes ceremony.
```c++
struct twitter_callbacks {
bool in_statuses;
bool in_tweet;
std::string_view text;
uint64_t retweets;
uint64_t favorites;
void start_object_field(std::string_view key) {
if (key == "statuses") { in_statuses = true; }
}
void start_object() {
if (in_statuses) { in_tweet = true; }
}
void string_field(std::string_view key, std::string_view value) {
if (in_tweet && key == "text") { text = value; }
}
void number_field(std::string_view key, uint64_t value) {
if (in_tweet) {
if (key == "retweet_count") { retweets = value; }
if (key == "favorite_count") { favorites = value; }
}
}
void end_object() {
if (in_tweet) {
cout << "[redacted] (" << retweets << " retweets / " << favorites << " favorites): " << text << endl;
in_tweet = false;
} else if (in_statuses) {
in_statuses = false;
}
}
};
sax::parser parser;
parser.parse(twitter_callbacks());
```
This is a large amount of code, requiring mental gymnastics even to read. An actual implementation is harder to write
and to maintain.
Pros of the event-based approach:
* Speed and space benefits from low, predictable memory usage.
* Parsing can be done more lazily: the API can delegate work to the programmer for better performance.
* It is highly flexible: given enough effort, most tasks can be accomplished efficiently.
Cons of the event-based approach:
* Performance drain from context blindness (e.g., switch statements for "where am I in the document")
* Difficult to use (high code complexity, high maintenance, difficult to debug)
* Lacks the safety of DOM: malformed documents could be ingested.
Though an event-based approach might have its niche uses, we believe that it is rarely ideally suited. We suspect that it is mostly used when performance and memory is a concern, and no other option (except DOM) is readily available.
### Schema-Based Parser Generators
In a schema-based model, the programmer provides a description of a data structure, and the parser constructs the data structure in question during parsing. These parsers take a schema--a description of
your JSON, with field names, types, everything--and generate classes/structs in your language of
choice, as well as a parser to deserialize the JSON into those structs. Some such parsers let you
define your own data structures (`struct`) and they let a preprocessor inspects it and generates a custom JSON parser for it.
Though not all of these schema-based parser generators generate a parser or even optimize for
streaming, but they are *able* to in principle. Unlike the DOM and the event-based models, a schema-based approach assumes
that the structure of the document is known at compile-time.
Pros of the schema-based approach:
* Ease of Use is on par with DOM
* Parsers that generate iterators and lazy values in structs can keep memory pressure down to event-based levels.
* Type Blindness can be entirely solved with specific parsers for each type, saving many branches.
* Context Blindness can be solved, especially if object fields are required and in order, saving even more branches.
* Can be made a safe as DOM: the input can be entirely validated prior to ingestion.
Cons of the schema-based approach:
* It is less flexible than the DOM or event-based approaches, sometimes limited to a deserialization-to-objects scenario.
* The structure of the data must be fully known at compile-time.
### Type Blindness and Branch Misprediction
The DOM and event-based parsing model suffer from **type
blindness**: even when the programmer knows exactly what fields and what types are in the JSON document,
the parser does not. This means it has to look at each value blind with a big "switch"
statement, asking "is this a number? A string? A boolean? An array? An object?"
In modern processors, this kind of switch statement can make your program run slower
than it needs to because of the high cost of branch misprediction. Indeed, modern processor
cores rely on speculative execution for speed. They "read ahead" in your program, predicting
which instructions to run as soon as the data is available. A single-threaded program can
execute 2, 3 or even more instructions per cycle--largely because of speculative execution.
Unfortunately, when the processor mispredicts the instructions, typically due to a mispredicted
branch, all of the work done from the misprediction has be discarded and started anew. The
processor may have been executing 3 or 4 instructions per cycle, and consuming the corresponding
power, but all of the work may have been wasteful.
Type blindness means that the processor has to guess, for every JSON value, whether it will be an array,
an object, number, string or boolean since these correspond to distinct code paths.
Though some JSON files have predictable content, we find in practice that many JSON files
stress the branch prediction. Though branch predictors improve with each new generation of processors,
the cost of branch mispredictions also tends to increase as pipelines expand, and the processors become
able to schedule longer streams of instructions.
On Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
user declares their type by asking for a `double`, an `int`, a `string`, etc. It attempts to do so while
preserving most of the flexibility of DOM parsing.
Algorithm
---------
To help visualize the algorithm, we'll walk through the example C++ given at the top, for this JSON:
```json
{
"statuses": [
{ "id": 1, "text": "first!", "user": { "screen_name": "lemire", "name": "Daniel" }, "favorite_count": 100, "retweet_count": 40 },
{ "id": 2, "text": "second!", "user": { "screen_name": "jkeiser2", "name": "John" }, "favorite_count": 2, "retweet_count": 3 }
]
}
```
### Starting the iteration
1. First, we declare a parser object that keeps internal buffers necessary for parsing. This can be
reused to parse multiple JSON files, so you do not pay the high cost of allocating memory every
time (and so it can stay in cache!).
This declaration does not allocate any memory; that will happen in the next step.
```c++
ondemand::parser parser;
```
2. We then start iterating the JSON document by allocating internal parser buffers, preprocessing
the JSON, and initializing the iterator.
```c++
auto doc = parser.iterate(json);
```
Since this is the first time this parser has been used, `iterate()` first allocates internal
parser buffers if this is the first time through. When reusing an existing parser, allocation
only happens if the new document is bigger than internal buffers can handle. The On Demand
API only ever allocates memory in the `iterate()` function call.
The simdjson library then preprocesses the JSON text at high speed, finding all tokens (i.e. the starting
position of any JSON value, as well as any important operators like `,`, `:`, `]` or `}`).
Finally, a `document` iterator is created, initialized at the position of the first value in the
`json` text input. The document iterator is bumped forward by array / object iterators and
object[] lookup, and must be kept around until iteration is complete.
This operation can fail as this stage if the document in invalid! The result type is `simdjson_result<document>`.
The simdjson library uses `simdjson_result` when a value needs to be returned by a function that can fail given improper inputs.
The `simdjson_result` value contain an `error_code` and a `document`, and it was designed to allow you to use either error code
checking or C++ exceptions via a direct cast `document(parser.iterate(json))` you can use `get()`
to check the error and cast to a value, or cast directly to a value. However, the simdjson library
rely on error chaining, so it is possible to delay error checks: we shall shortly explain error
chaining more fully.
NOTE: You should always have such a `document` instance (here `doc`) and it should remain in scope for the duration
of your parsing function. E.g., you should not use the returned document as a temporary (e.g., `auto x = parser.iterate(json).get_object();`)
followed by other operations as the destruction of the `document` instance makes all of the derived instances
ill-defined.
3. We iterate over the "statuses" field using a typical C++ iterator, reading past the initial
`{ "statuses": [ {`.
```c++
for (ondemand::object tweet : doc["statuses"]) {
```
This shorthand does much, and it is helpful to see what it expands to.
Comments in front of each one explain what's going on:
```c++
// Validate that the top-level value is an object: check for {
ondemand::object top = doc.get_object();
// Find the field statuses by:
// 1. Check whether the object is empty (check for }). (We do not really need to do this unless the key lookup fails!)
// 2. Check if we're at the field by looking for the string "statuses" using byte-by-byte comparison.
// 3. Validate that there is a `:` after it.
auto tweets_field = top["statuses"];
// Validate that the field value is an array: check for [
// Also mark the array as finished if there is a ] next, which would cause the while () statement to exit immediately.
ondemand::array tweets = tweets_field.get_array();
// These three method calls do nothing substantial (the real checking happens in get_array() and ++)
// != checks whether the array is marked as finished (if we have found a ]).
ondemand::array_iterator tweets_iter = tweets.begin();
while (tweets_iter != tweets.end()) {
auto tweet_value = *tweets_iter;
// Validate that the array element is an object: check for {
ondemand::object tweet = tweet_value.get_object();
...
}
```
What is not explained in this code expansion is *error chaining*.
Generally, you can use `document` methods on a `simdjson_result<...>` value; any errors will
just be passed down the chain. Many method calls
can be chained in this manner. So `for (object tweet : doc["statuses"])`, which is the equivalent of
`object tweet = *(doc.get_object()["statuses"].get_array().begin()).get_object()`, could fail in any of
6 method calls, and the error will only be checked at the end,
when you attempt to cast the final `simdjson_result<object>` to object. Upon casting, an exception is
thrown if there was an error.
NOTE: while the document can be queried once for a key as if it were an object, it is not an actual object
instance. If you need to treat it as an object (e.g., to query more than one keys), you can cast it as
such `ondemand::object root_object = doc.get_object();`.
4. We get the `"text"` field as a string.
```c++
std::string_view text = tweet["text"];
```
First, `["text"]` skips the `"id"` field because it does not match: skips the key, `:` and
value (`1`). We then check whether there are more fields by looking for either `,`
or `}`.
The second field is matched (`"text"`), so we validate the `:` and move to the actual value.
NOTE: `["text"]` does a *raw match*, comparing the key directly against the raw JSON. This means
that keys with escapes in them may not be matched and the letter case must match exactly.
To convert to a string, we check for `"` and use simdjson's fast unescaping algorithm to copy
`first!` (plus a terminating `\0`) into a buffer managed by the `document`. This buffer stores
all strings from a single iteration. The next string will be written after the `\0`.
A `string_view` is returned which points to that buffer, and contains the length.
4. We get the `"screen_name"` from the `"user"` object.
```c++
ondemand::object user = tweet["user"];
screen_name = user["screen_name"];
```
First, `["user"]` checks whether there are any more object fields by looking for either `,` or
`}`. Then it matches `"user"` and validates the `:`.
`["screen_name"]` then converts to object, checking for `{`, and finds `"screen_name"`.
To convert the result to usable string (i.e., the screen name `lemire`), the characters are written to the document's
string buffer (after possibly escaping them), which now has *two* string_views pointing into it, and looks like `first!\0lemire\0`.
Finally, the temporary user object is destroyed, causing it to skip the remainder of the object
(`}`).
NOTE: You may only have one active array or object active at any given time. An array or an object becomes
active when the `ondemand::object` or `ondemand::array` is created, and it releases its 'focus' when
its destructor is called. If you create an array or an object located inside a parent object or array,
the child array or object becomes active while the parent becomes temporarily inactive. If you access
several sibling objects or arrays, you must ensure that the destructor is called by scoping each access
(see Iteration Safety section below for further details).
5. We get `"retweet_count"` and `"favorite_count"` as unsigned integers.
```c++
uint64_t retweets = tweet["retweet_count"];
uint64_t favorites = tweet["favorite_count"];
```
6. We loop to the next tweet.
```c++
for (ondemand::object tweet : doc["statuses"]) {
...
}
```
The relevant parts of the loop are:
```c++
while (iter != statuses.end()) {
ondemand::object tweet = *iter;
...
iter++;
}
```
First, the `tweet` destructor runs, skipping the remainder of the object which in this case is
just `}`.
Next, `iter++` checks whether there are more values and finds `,`. The loop continues.
Finally, `ondemand::object tweet = *iter` checks for `{` and returns the object.
This tweet is processed just like the previous one.
7. We finish the last tweet.
At the end of the loop, the `tweet` is first destroyed, skipping the remainder of the tweet
object (`}`).
The `iter++` instruction from `for (ondemand::object tweet : doc["statuses"])` then checks whether there are
more values and finds that there are none (`]`). It marks the array iteration as finished and the for
loop terminates.
Then the outer object is destroyed, skipping everything up to the `}`.
Design Features
---------------
### String Parsing
When the user requests strings, we unescape them to a single string buffer much like the DOM parser
so that users enjoy the same string performance as the core simdjson. We do not write the length to the
string buffer, however; that is stored in the `string_view` instance we return to the user.
```C++
ondemand::parser parser;
auto doc = parser.iterate(json);
std::set<std::string_view> default_users;
ondemand::array tweets = doc["statuses"].get_array();
for (auto tweet_value : tweets) {
auto tweet = tweet_value.get_object();
ondemand::object user = tweet["user"].get_object();
std::string_view screen_name = user["screen_name"].get_string();
bool default_profile = user["default_profile"].get_bool();
if (default_profile) { default_users.insert(screen_name); }
}
```
By using `string_view` instances, we avoid the high cost of allocating many small strings (as would be the
case with `std::string`) but be mindful that the life cycle of these `string_view` instances is tied to the
parser instance. If the parser instance is destroyed or reused for a new JSON document, these strings are no longer valid.
We iterate through object instances using `field` instances which represent key-value pairs. The value
is accessible by the `value()` method whereas the key is accessible by the `key()` method.
The keys are treated differently than values are made available as as special type `raw_json_string`
which is a lightweight type that is meant to be used on a temporary basis, amost solely for
direct raw ASCII comparisons (`field.key() == "mykey"`). If you occasionally need to access and store the
unescaped key values, you may use the `unescaped_key()` method. Once you have called `unescaped_key()` method,
neither the `key()` nor the `unescaped_key()` methods should be called: the current field instance
has no longer a key (that is by design). Like other strings, the resulting `std::string_view` generated
from the `unescaped_key()` method has a lifecycle tied to the `parser` instance: once the parser
is destroyed or reused with another document, the `std::string_view` instance becomes invalid.
```C++
auto doc = parser.iterate(json);
for(auto field : doc.get_object()) {
std::string_view keyv = field.unescaped_key();
}
```
### Iteration Safety
The On Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
in production systems:
- If the value fails to be parsed as one type, the program can try to parse it as something else until the program succeeds. Thus
the programmer can engineer fall back routines.
- If the value succeeds in being parsed or converted to a type, the program cannot try again. An attempt to parse the same node twice will
cause the program to abort. We put this safety measure in the API to prevent double iteration of an array which
would cause inconsistent iterator state or double-unescaping a string which may cause memory
overruns if done.
- Guaranteed Iteration: If you discard a value without using it--perhaps you just wanted to know
if it was `nullptr` but did not care what the actual value was--it will iterate. The destructor automates
the iteration.
Some care is needed when using the On Demand API in scenarios where you need to access several sibling arrays or objects because
only one object or array can be active at any one time. Let us consider the following example:
```C++
ondemand::parser parser;
const padded_string json = R"({ "parent": {"child1": {"name": "John"} , "child2": {"name": "Daniel"}} })"_padded;
auto doc = parser.iterate(json);
ondemand::object parent = doc["parent"];
// parent owns the focus
ondemand::object c1 = parent["child1"];
// c1 owns the focus
//
if(std::string_view(c1["name"]) != "John") { ... }
// c2 attempts to grab the focus from parent but fails
ondemand::object c2 = parent["child2"];
// c2 is now in an unsafe state and the following line would be unsafe
// if(std::string_view(c2["name"]) != "Daniel") { return false; }
```
A correct usage is given by the following example:
```C++
ondemand::parser parser;
const padded_string json = R"({ "parent": {"child1": {"name": "John"} , "child2": {"name": "Daniel"}} })"_padded;
auto doc = parser.iterate(json);
ondemand::object parent = doc["parent"];
// At this point, parent owns the focus
{
ondemand::object c1 = parent["child1"];
// c1 grabbed the focus from parent
if(std::string_view(c1["name"]) != "John") { return false; }
}
// c1 went out of scope, so its destructor was called and the focus
// was handed back to parent.
{
ondemand::object c2 = parent["child2"];
// c2 grabbed the focus from parent
// the following is safe:
if(std::string_view(c2["name"]) != "Daniel") { return false; }
}
```
### Benefits of the On Demand Approach
We expect that the On Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
* Faster than DOM in some cases. Reduced memory usage.
* Straightforward, programmer-friendly interface (arrays and objects).
* Highly expressive, beyond deserialization and pointer queries: many tasks can be accomplished with little code.
### Limitations of the On Demand Approach
The On Demand approach has some limitations:
* Because it operates in streaming mode, you only have access to the current element in the JSON document. Furthermore, the document is traversed in order so the code is sensitive to the order of the JSON nodes in the same manner as an event-based approach (e.g., SAX).
* The On Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
There are currently additional technical limitations which we expect to resolve in future releases of the simdjson library:
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On Demand API does not have runtime dispatch support at this time. To benefit from the On Demand API, you must compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On Demand code for different processors.
* There is an initial phase which scans the entire document quickly, irrespective of the size of the document. We plan to break this phase into distinct steps for large files in a future release as we have done with other components of our API (e.g., `parse_many`).
* The On Demand API does not support JSON Pointer. This capability is currently limited to our core API.
* You should be mindful that the though your software might write the keys in a consistent manner, the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt) states that "JSON parsing libraries have been observed to differ as to whether or not they make the ordering of object members visible". The On Demand API will help the programmer handle unexpected JSON dialects by throwing an exception when the unexpected occurs, but the programmer is responsible for handling such cases: e.g., by rejecting the JSON input that does not follow the expected JSON dialect. We intend to help users who wish to use the On Demand API but require support for order-insensitive semantics, but in our current implementation support for out-of-order keys (if needed) must be provided by the programmer. Currently, one might proceed in the following manner as a fallback measure if keys can appear in any order:
```C++
for (ondemand::object my_object : doc["mykey"]) {
for (auto field : my_object) {
if (field.key() == "key_value1") { process1(field.value()); }
else if (field.key() == "key_value2") { process2(field.value()); }
else if (field.key() == "key_value3") { process3(field.value()); }
}
}
```
### Applicability of the On Demand Approach
At this time we recommend the On Demand API in the following cases:
1. The 64-bit hardware (CPU) used to run the software is known at compile time. If you need runtime dispatching because you cannot be certain of the hardware used to run your software, you will be better served with the core simdjson API. (This only applies to x64 (AMD/Intel). On 64-bit ARM hardware, runtime dispatching is unnecessary.)
2. The used parts of JSON files do not need to be validated and the layout of the nodes follows a strict JSON dialect. If you are receiving JSON from other systems, you might be better served with core simdjson API as it fully validates the JSON inputs and allows you to navigate through the document at will.
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On Demand API is not necessary for high efficiency.
4. As a developer, you value a clean, flexible and maintainable API.
Good applications for the On Demand API might be:
* You are working from pre-existing large JSON files that have been vetted. You expect them to be well formed according to a known JSON dialect and to have a consistent layout. For example, you might be doing biomedical research or machine learning on top of static data dumps in JSON.
* You have a closed system on predetermined hardware. Both the generation and the consumption of JSON data is within your system. Your team controls both the software that produces the JSON and the software the parses it, your team knows and control the hardware. Thus you can fully test your system.
* You are working with stable JSON APIs which have a consistent layout and JSON dialect.
## Checking Your CPU Selection
Given that the On Demand API does not offer runtime dispatching, your code is compiled against a specific CPU target. You should
verify that the code is compiled against the target you expect: `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM,
most programmers will want to target `arm64`. The `fallback` is probably only good for testing purposes, not for deployment.
```C++
std::cout << simdjson::builtin_implementation()->name() << std::endl;
```
If you are using CMake for your C++ project, then you can pass compilation flags to your compiler by using
the `CMAKE_CXX_FLAGS` variable:
```
cmake -DCMAKE_CXX_FLAGS="-march=haswell" -B build_haswell
cmake --build build_haswell
```
+160
View File
@@ -0,0 +1,160 @@
parse_many
==========
An interface providing features to work with files or streams containing multiple small JSON documents.
As fast and convenient as possible.
Contents
--------
- [Motivations](#motivations)
- [Performance](#performance)
- [How it works](#how-it-works)
- [Support](#support)
- [API](#api)
- [Use cases](#use-cases)
Motivation
-----------
The main motivation for this piece of software is to achieve maximum speed and offer a
better quality of life in parsing files containing multiple small JSON documents.
The JavaScript Object Notation (JSON) [RFC7159](https://tools.ietf.org/html/rfc7159) is a handy
serialization format. However, when serializing a large sequence of
values as an array, or a possibly indeterminate-length or never-
ending sequence of values, JSON may be inconvenient.
Consider a sequence of one million values, each possibly one kilobyte
when encoded -- roughly one gigabyte. It is often desirable to process such a dataset incrementally
without having to first read all of it before beginning to produce results.
Performance
-----------
The following is a chart comparing the speed of the different alternatives to parse a multiline JSON.
The simdjson library provides a threaded and non-threaded `parse_many()` implementation. As the
figure below shows, if you can, use threads, but if you cannot, the unthreaded mode is still fast!
[![Chart.png](/doc/Multiline_JSON_Parse_Competition.png)](/doc/Multiline_JSON_Parse_Competition.png)
How it works
------------
### Context
The parsing in simdjson is divided into 2 stages. First, in stage 1, we parse the document and find
all the structural indexes (`{`, `}`, `]`, `[`, `,`, `"`, ...) and validate UTF8. Then, in stage 2,
we go through the document again and build the tape using structural indexes found during stage 1.
Although stage 1 finds the structural indexes, it has no knowledge of the structure of the document
nor does it know whether it parsed a valid document, multiple documents, or even if the document is
complete.
Prior to parse_many, most people who had to parse a multiline JSON file would proceed by reading the
file line by line, using a utility function like `std::getline` or equivalent, and would then use
the `parse` on each of those lines. From a performance point of view, this process is highly
inefficient, in that it requires a lot of unnecessary memory allocation and makes use of the
`getline` function, which is fundamentally slow, slower than the act of parsing with simdjson
[(more on this here)](https://lemire.me/blog/2019/06/18/how-fast-is-getline-in-c/).
Unlike the popular parser RapidJson, our DOM does not require the buffer once the parsing job is
completed, the DOM and the buffer are completely independent. The drawback of this architecture is
that we need to allocate some additional memory to store our ParsedJson data, for every document
inside a given file. Memory allocation can be slow and become a bottleneck, therefore, we want to
minimize it as much as possible.
### Design
To achieve a minimum amount of allocations, we opted for a design where we create only one
parser object and therefore allocate its memory once, and then recycle it for every document in a
given file. But, knowing that they often have largely varying size, we need to make sure that we
allocate enough memory so that all the documents can fit. This value is what we call the batch size.
As of right now, we need to manually specify a value for this batch size, it has to be at least as
big as the biggest document in your file, but not too big so that it submerges the cached memory.
The bigger the batch size, the fewer we need to make allocations. We found that 1MB is somewhat a
sweet spot.
1. When the user calls `parse_many`, we return a `document_stream` which the user can iterate over
to receive parsed documents.
2. We call stage 1 on the first batch_size bytes of JSON in the buffer, detecting structural
indexes for all documents in that batch.
3. We call stage 2 on the indexes, reading tokens until we reach the end of a valid document (i.e.
a single array, object, string, boolean, number or null).
4. Each time the user calls `++` to read the next document, we call stage 2 to parse the next
document where we left off.
5. When we reach the end of the batch, we call stage 1 on the next batch, starting from the end of
the last document, and go to step 3.
### Threads
But how can we make use of threads if they are available? We found a pretty cool algorithm that allows us to quickly
identify the position of the last JSON document in a given batch. Knowing exactly where the end of
the batch is, we no longer need for stage 2 to finish in order to load a new batch. We already know
where to start the next batch. Therefore, we can run stage 1 on the next batch concurrently while
the main thread is going through stage 2. Running stage 1 in a different thread can, in best
cases, remove almost entirely its cost and replaces it by the overhead of a thread, which is orders
of magnitude cheaper. Ain't that awesome!
Thread support is only active if thread supported is detected in which case the macro
SIMDJSON_THREADS_ENABLED is set. Otherwise the library runs in single-thread mode.
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
You should expect the main thread to be fully occupied while the worker thread is partially busy
(e.g., 80% of the time).
Support
-------
Since we want to offer flexibility and not restrict ourselves to a specific file
format, we support any file that contains any amount of valid JSON document, **separated by one
or more character that is considered whitespace** by the JSON spec. Anything that is
not whitespace will be parsed as a JSON document and could lead to failure.
Whitespace Characters:
- **Space**
- **Linefeed**
- **Carriage return**
- **Horizontal tab**
- **Nothing**
Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/)
- [JSON lines (JSONL)](http://jsonlines.org/)
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream!
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
API
---
See [basics.md](basics.md#newline-delimited-json-ndjson-and-json-lines) for an overview of the API.
## Use cases
From [jsonlines.org](http://jsonlines.org/examples/):
- **Better than CSV**
```json
["Name", "Session", "Score", "Completed"]
["Gilbert", "2013", 24, true]
["Alexa", "2013", 29, true]
["May", "2012B", 14, false]
["Deloise", "2012A", 19, true]
```
CSV seems so easy that many programmers have written code to generate it themselves, and almost every implementation is
different. Handling broken CSV files is a common and frustrating task. CSV has no standard encoding, no standard column
separator and multiple character escaping standards. String is the only type supported for cell values, so some programs
attempt to guess the correct types.
JSON Lines handles tabular data cleanly and without ambiguity. Cells may use the standard JSON types.
The biggest missing piece is an import/export filter for popular spreadsheet programs so that non-programmers can use
this format.
- **Easy Nested Data**
```json
{"name": "Gilbert", "wins": [["straight", "7♣"], ["one pair", "10♥"]]}
{"name": "Alexa", "wins": [["two pair", "4♠"], ["two pair", "9♠"]]}
{"name": "May", "wins": []}
{"name": "Deloise", "wins": [["three of a kind", "5♣"]]}
```
JSON Lines' biggest strength is in handling lots of similar nested data structures. One .jsonl file is easier to
work with than a directory full of XML files.
+200
View File
@@ -0,0 +1,200 @@
Performance Notes
=================
simdjson strives to be at its fastest *without tuning*, and generally achieves this. However, there
are still some scenarios where tuning can enhance performance.
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Keeping documents around for longer](#keeping-documents-around-for-longer)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
* [Large files and huge page support](#large-files-and-huge-page-support)
* [Number parsing](#number-parsing)
* [Visual Studio](#visual-studio)
* [Downclocking](#downclocking)
* [Best Use of the DOM API](#best-use-of-the-dom-api)
* [Padding and Temporary Copies](#padding-and-temporary-copies)
Reusing the parser for maximum efficiency
-----------------------------------------
If you're using simdjson to parse multiple documents, or in a loop, you should make a parser once
and reuse it. The simdjson library will allocate and retain internal buffers between parses, keeping
buffers hot in cache and keeping memory allocation and initialization to a minimum. In this manner,
you can parse terabytes of JSON data without doing any new allocation.
```c++
dom::parser parser;
// This initializes buffers and a document big enough to handle this JSON.
dom::element doc = parser.parse("[ true, false ]"_padded);
cout << doc << endl;
// This reuses the existing buffers, and reuses and *overwrites* the old document
doc = parser.parse("[1, 2, 3]"_padded);
cout << doc << endl;
// This also reuses the existing buffers, and reuses and *overwrites* the old document
dom::element doc2 = parser.parse("true"_padded);
// Even if you keep the old reference around, doc and doc2 refer to the same document.
cout << doc << endl;
cout << doc2 << endl;
```
It's not just internal buffers though. The simdjson library reuses the document itself. The dom::element, dom::object and dom::array instances are *references* to the internal document.
You are only *borrowing* the document from simdjson, which purposely reuses and overwrites it each
time you call parse. This prevent wasteful and unnecessary memory allocation in 99% of cases where
JSON is just read, used, and converted to native values or thrown away.
> **You are only borrowing the document from the simdjson parser. Don't keep it long term!**
This is key: don't keep the `document&`, `dom::element`, `dom::array`, `dom::object`
or `string_view` objects you get back from the API. Convert them to C++ native values, structs and
arrays that you own.
Server Loops: Long-Running Processes and Memory Capacity
--------------------------------------------------------
The simdjson library automatically expands its memory capacity when larger documents are parsed, so
that you don't unexpectedly fail. In a short process that reads a bunch of files and then exits,
this works pretty flawlessly.
Server loops, though, are long-running processes that will keep the parser around forever. This
means that if you encounter a really, really large document, simdjson will not resize back down.
The simdjson library lets you adjust your allocation strategy to prevent your server from growing
without bound:
* You can set a *max capacity* when constructing a parser:
```c++
dom::parser parser(1000*1000); // Never grow past documents > 1MB
for (web_request request : listen()) {
dom::element doc;
auto error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
This parser will grow normally as it encounters larger documents, but will never pass 1MB.
* You can set a *fixed capacity* that never grows, as well, which can be excellent for
predictability and reliability, since simdjson will never call malloc after startup!
```c++
dom::parser parser(0); // This parser will refuse to automatically grow capacity
auto error = parser.allocate(1000*1000); // This allocates enough capacity to handle documents <= 1MB
if (error) { cerr << error << endl; exit(1); }
for (web_request request : listen()) {
dom::element doc;
error = parser.parse(request.body).get(doc);
// If the document was above our limit, emit 413 = payload too large
if (error == CAPACITY) { request.respond(413); continue; }
// ...
}
```
Large files and huge page support
---------------------------------
There is a memory allocation performance cost the first time you process a large file (e.g. 100MB).
Between the cost of allocation, the fact that the memory is not in cache, and the initial zeroing of
memory, [on some systems, allocation runs far slower than parsing (e.g., 1.4GB/s)](https://lemire.me/blog/2020/01/14/how-fast-can-you-allocate-a-large-block-of-memory-in-c/). Reusing the parser mitigates this by
paying the cost once, but does not eliminate it.
In large file use cases, enabling transparent huge page allocation on the OS can help a lot. We
haven't found the right way to do this on Windows or OS/X, but on Linux, you can enable transparent
huge page allocation with a command like:
```bash
echo always > /sys/kernel/mm/transparent_hugepage/enabled
```
In general, when running benchmarks over large files, we recommend that you report performance
numbers with and without huge pages if possible. Furthermore, you should amortize the parsing (e.g.,
by parsing several large files) to distinguish the time spent parsing from the time spent allocating
memory. If you are using the `parse` benchmarking tool provided with the simdjson library, you can
use the `-H` flag to omit the memory allocation cost from the benchmark results.
```
./parse largefile # includes memory allocation cost
./parse -H largefile # without memory allocation
```
Number parsing
--------------
Some JSON files contain many floating-point values. It is the case with many GeoJSON files. Accurately
parsing decimal strings into binary floating-point values with proper rounding is challenging. To
our knowledge, it is not possible, in general, to parse streams of numbers at gigabytes per second
using a single core. While using the simdjson library, it is possible that you might be limited to a
few hundred megabytes per second if your JSON documents are densely packed with floating-point values.
- When possible, you should favor integer values written without a decimal point, as it simpler and faster to parse decimal integer values.
- When serializing numbers, you should not use more digits than necessary: 17 digits is all that is needed to exactly represent double-precision floating-point numbers. Using many more digits than necessary will make your files larger and slower to parse.
- When benchmarking parsing speeds, always report whether your JSON documents are made mostly of floating-point numbers when it is the case, since number parsing can then dominate the parsing time.
Visual Studio
--------------
On Intel and AMD Windows platforms, Microsoft Visual Studio enables programmers to build either 32-bit (x86) or 64-bit (x64) binaries. We urge you to always use 64-bit mode. Visual Studio 2019 should default on 64-bit builds when you have a 64-bit version of Windows, which we recommend.
When compiling with Visual Studio, we recommend the flags `/Ob2 /O2` or better. We do not recommend that you compile simdjson with architecture-specific flags such as `arch:AVX2`. The simdjson library automatically selects the best execution kernel at runtime.
Recent versions of Microsoft Visual Studio on Windows provides support for the LLVM Clang compiler. You only need to install the "Clang compiler" optional component (ClangCL). You may also get a copy of the 64-bit LLVM CLang compiler for [Windows directly from LLVM](https://releases.llvm.org/download.html). The simdjson library fully supports the LLVM Clang compiler under Windows. In fact, you may get better performance out of simdjson with the LLVM Clang compiler than with the regular Visual Studio compiler. Meanwhile the [LLVM CLang compiler is binary compatible with Visual Studio](https://clang.llvm.org/docs/MSVCCompatibility.html) which means that you can combine their binaries (executables and libraries).
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows excellent (on par with Linux).
Downclocking
--------------
SIMD instructions are the public transportation of computing. Instead of using 4 distinct instructions to add numbers, you can replace them with a single instruction that does the same work. Though the one instruction is slightly more expensive, the energy used per unit of work is much less with SIMD. If you can increase your speed using SIMD instructions (NEON, SSE, AVX), you should expect to reduce your power usage.
The SIMD instructions that simdjson relies upon (SSE and AVX under x64, NEON under ARM) are routinely part of runtime libraries (e.g., [Go](https://golang.org/src/runtime/memmove_amd64.s), [Glibc](https://github.com/ihtsae/glibc/commit/5f3d0b78e011d2a72f9e88b0e9ef5bc081d18f97), [LLVM](https://github.com/llvm/llvm-project/blob/96f3ea0d21b48ca088355db10d4d1a2e9bc9f884/lldb/tools/debugserver/source/MacOSX/i386/DNBArchImplI386.cpp), [Rust](https://github.com/rust-lang/rust/commit/070fad1701fb36b112853b0a6a9787a7bb7ff34c), [Java](http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/c1374141598c/src/cpu/x86/vm/stubGenerator_x86_64.cpp#l1297), [PHP](https://github.com/php/php-src/blob/e5cb53ec68603d4dbdd780fd3ecfca943b4fd383/ext/standard/string.c)). What distinguishes the simdjson library is that it is built from the ground up to benefit from these instructions.
You should not expect the simdjson library to cause *downclocking* of your recent Intel CPU cores.
On some Intel processors, using SIMD instructions in a sustained manner on the same CPU core may result in a phenomenon called downclocking whereas the processor initially runs these instructions at a slow speed before reducing the frequency of the core for a short time (milliseconds). Intel refers to these states as licenses. On some current Intel processors, it occurs under two scenarios:
- [Whenever 512-bit AVX-512 instructions are used](https://lemire.me/blog/2018/09/07/avx-512-when-and-how-to-use-these-new-instructions/).
- Whenever heavy 256-bit or wider instructions are used. Heavy instructions are those involving floating point operations or integer multiplications (since these execute on the floating point unit).
The simdjson library does not currently support AVX-512 instructions and it does not make use of heavy 256-bit instructions. We do use vectorized multiplications, but only using 128-bit registers. Thus there should be no downclocking due to simdjson on recent processors.
You may still be worried about which SIMD instruction set is used by simdjson. Thankfully, [you can always determine and change which architecture-specific implementation is used](implementation-selection.md) by simdjson. Thus even if your CPU supports AVX2, you do not need to use AVX2. You are in control.
Best Use of the DOM API
-------------------------
The simdjson API provides access to the JSON DOM (document-object-model) content as a tree of `dom::element` instances, each representing an object, an array or an atomic type (null, true, false, number). These `dom::element` instances are lightweight objects (e.g., spanning 16 bytes) and it might be advantageous to pass them by value, as opposed to passing them by reference or by pointer.
Padding and Temporary Copies
--------------
The simdjson function `parser.parse` reads data from a padded buffer, containing SIMDJSON_PADDING extra bytes added at the end.
If you are passing a `padded_string` to `parser.parse` or loading the JSON directly from
disk (`parser.load`), padding is automatically handled.
When calling `parser.parse` on a pointer (e.g., `parser.parse(my_char_pointer, my_length_in_bytes)`) a temporary copy is made by default with adequate padding and you, again, do not need to be concerned with padding.
Some users may not be able use our `padded_string` class or to load the data directly from disk (`parser.load`). They may need to pass data pointers to the library. If these users wish to avoid temporary copies and corresponding temporary memory allocations, they may want to call `parser.parse` with the `realloc_if_needed` parameter set to false (e.g., `parser.parse(my_char_pointer, my_length_in_bytes, false)`). In such cases, they need to ensure that there are at least SIMDJSON_PADDING extra bytes at the end that can be safely accessed and read. They do not need to initialize the padded bytes to any value in particular. The following example is safe:
```C++
const char *json = R"({"key":"value"})";
const size_t json_len = std::strlen(json);
std::unique_ptr<char[]> padded_json_copy{new char[json_len + SIMDJSON_PADDING]};
memcpy(padded_json_copy.get(), json, json_len);
memset(padded_json_copy.get() + json_len, 0, SIMDJSON_PADDING);
simdjson::dom::parser parser;
simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false);
````
Setting the `realloc_if_needed` parameter `false` in this manner may lead to better performance since copies are avoided, but it requires that the user takes more responsibilities: the simdjson library cannot verify that the input buffer was padded with SIMDJSON_PADDING extra bytes.
+40 -38
View File
@@ -28,45 +28,46 @@ It is sometimes useful to start with an example. Consider the following JSON doc
The following is a dump of the content of the tape, with the first number of each line representing the index of a tape element.
```bash
$ ./json2json -d jsonexamples/small/demo.json
0 : r // pointing to 38 (right after last node)
1 : { // pointing to next tape location 38 (first node after the scope)
2 : string "Image"
3 : { // pointing to next tape location 37 (first node after the scope)
4 : string "Width"
5 : integer 800
7 : string "Height"
8 : integer 600
10 : string "Title"
11 : string "View from 15th Floor"
12 : string "Thumbnail"
13 : { // pointing to next tape location 23 (first node after the scope)
14 : string "Url"
15 : string "http://www.example.com/image/481989943"
16 : string "Height"
17 : integer 125
19 : string "Width"
20 : integer 100
22 : } // pointing to previous tape location 13 (start of the scope)
23 : string "Animated"
24 : false
25 : string "IDs"
26 : [ // pointing to next tape location 36 (first node after the scope)
27 : integer 116
29 : integer 943
31 : integer 234
33 : integer 38793
35 : ] // pointing to previous tape location 26 (start of the scope)
36 : } // pointing to previous tape location 3 (start of the scope)
37 : } // pointing to previous tape location 1 (start of the scope)
38 : r // pointing to 0 (start root)
### The Tape
| index | element (64 bit word) |
| ----- | ------------------------------------------------------------------- |
| 0 | r // pointing to 38 (right after last node) |
| 1 | { // pointing to next tape location 38 (first node after the scope) |
| 2 | string "Image" |
| 3 | { // pointing to next tape location 37 (first node after the scope) |
| 4 | string "Width" |
| 5 | integer 800 |
| 7 | string "Height" |
| 8 | integer 600 |
| 10 | string "Title" |
| 11 | string "View from 15th Floor" |
| 12 | string "Thumbnail" |
| 13 | { // pointing to next tape location 23 (first node after the scope) |
| 14 | string "Url" |
| 15 | string "http://www.example.com/image/481989943" |
| 16 | string "Height" |
| 17 | integer 125 |
| 19 | string "Width" |
| 20 | integer 100 |
| 22 | } // pointing to previous tape location 13 (start of the scope) |
| 23 | string "Animated" |
| 24 | false |
| 25 | string "IDs" |
| 26 | [ // pointing to next tape location 36 (first node after the scope) |
| 27 | integer 116 |
| 29 | integer 943 |
| 31 | integer 234 |
| 33 | integer 38793 |
| 35 | ] // pointing to previous tape location 26 (start of the scope) |
| 36 | } // pointing to previous tape location 3 (start of the scope) |
| 37 | } // pointing to previous tape location 1 (start of the scope) |
| 38 | r // pointing to 0 (start root) |
```
## General formal of the tape elements
Most tape elements are written as `('c' << 56) + x` where `'c'` is some ASCII character determining the type of the element (out of 't', 'f', 'n', 'l', 'd', '"', '{', '}', '[', ']' ,'r') and where `x` is a 56-bit value called the payload. The payload is normally interpreted as an unsigned 56-bit integer. Note that 56-bit integers can be quite large.
Most tape elements are written as `('c' << 56) + x` where `'c'` is some ASCII character determining the type of the element (out of 't', 'f', 'n', 'l', 'u', 'd', '"', '{', '}', '[', ']' ,'r') and where `x` is a 56-bit value called the payload. The payload is normally interpreted as an unsigned 56-bit integer. Note that 56-bit integers can be quite large.
Performance consideration: We believe that accessing the tape in regular units of 64 bits is more important for performance than saving memory.
@@ -83,10 +84,12 @@ Simple JSON nodes are represented with one tape element:
## Integer and Double values
Integer values are represented as two 64-bit tape elements:
- The 64-bit value `('l' << 56)` followed by the 64-bit integer value litterally. Integer values are assumed to be signed 64-bit values, using two's complement notation.
- The 64-bit value `('l' << 56)` followed by the 64-bit integer value literally. Integer values are assumed to be signed 64-bit values, using two's complement notation.
- The 64-bit value `('u' << 56)` followed by the 64-bit integer value literally. Integer values are assumed to be unsigned 64-bit values.
Float values are represented as two 64-bit tape elements:
- The 64-bit value `('d' << 56)` followed by the 64-bit double value litterally in standard IEEE 754 notation.
- The 64-bit value `('d' << 56)` followed by the 64-bit double value literally in standard IEEE 754 notation.
Performance consideration: We store numbers of the main tape because we believe that locality of reference is helpful for performance.
@@ -131,4 +134,3 @@ In-between these two tape elements, we alternate between key (which must be stri
All the content of the object is located between these two tape elements, including arrays and objects.
Performance consideration: We can skip the content of an object entirely by accessing the first 64-bit tape element, reading the payload and moving to the corresponding index on the tape.
+1
View File
@@ -0,0 +1 @@
add_subdirectory(quickstart)
+60
View File
@@ -0,0 +1,60 @@
#
# Quickstart compile tests don't require any flags
#
# TODO haven't quite decided the right way to run quickstart on Windows. Needs README update.
#
# Note: on macOS and other platforms, the 'command' described below may not work even if the cmake builds.
# For example, it may be necessary to specify the sysroot, which CMake does, but the 'command' does not
# handle such niceties. On a case-by-case basis it is fixable but it requires work that CMake knows how
# to do but that is not trivial.
#
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# TODO run amalgamate first!
function(add_quickstart_test TEST_NAME SOURCE_FILE)
# Second argument is C++ standard name
if (MSVC)
if (ARGV2)
set(QUICKSTART_FLAGS /std:${ARGV2})
else()
set(QUICKSTART_FLAGS /WX)
endif()
set(QUICKSTART_INCLUDE /I${PROJECT_SOURCE_DIR}/include /I${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/simdjson.cpp)
else()
if (ARGV2)
set(QUICKSTART_FLAGS -Werror -std=${ARGV2})
else()
set(QUICKSTART_FLAGS -Werror)
endif()
set(QUICKSTART_INCLUDE -I${PROJECT_SOURCE_DIR}/include -I${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/simdjson.cpp)
endif()
# Third argument tells whether to compile with -fno-exceptions
if (ARGV3)
if (NOT MSVC)
set(QUICKSTART_FLAGS ${QUICKSTART_FLAGS} -fno-exceptions)
endif()
endif()
add_test(
NAME ${TEST_NAME}
COMMAND ${CMAKE_CXX_COMPILER} ${QUICKSTART_FLAGS} -I${PROJECT_SOURCE_DIR}/include -I${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/simdjson.cpp ${SOURCE_FILE}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/examples/quickstart
)
set_property(
TEST ${TEST_NAME}
APPEND PROPERTY DEPENDS simdjson-source ${PROJECT_SOURCE_DIR}/examples/quickstart/${SOURCE_FILE}
)
endfunction(add_quickstart_test)
if (SIMDJSON_EXCEPTIONS)
add_quickstart_test(quickstart quickstart.cpp)
add_quickstart_test(quickstart11 quickstart.cpp c++11)
add_quickstart_test(quickstart14 quickstart.cpp c++14)
set_property( TEST quickstart quickstart11 APPEND PROPERTY LABELS acceptance compiletests )
endif()
add_quickstart_test(quickstart_noexceptions quickstart_noexceptions.cpp "" true)
add_quickstart_test(quickstart_noexceptions11 quickstart_noexceptions.cpp c++11 true)
set_property( TEST quickstart_noexceptions APPEND PROPERTY LABELS acceptance compile )
endif()

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