Compare commits

..

22 Commits

Author SHA1 Message Date
John Keiser d1de135ebd Updates 2023-09-13 11:57:43 -04:00
John Keiser 0edf1008c9 Updates 2023-09-13 11:21:58 -04:00
John Keiser 6a0d7bcb55 Right-justify int columns in markdown tables 2023-09-13 11:10:01 -04:00
John Keiser 6f196d0af0 Add results 2023-09-12 15:02:17 -04:00
John Keiser c3414a1234 Show more transitions 2023-09-12 14:58:33 -04:00
John Keiser 5d2107b470 Add jsonexamples generator and miss reduction script 2023-09-12 14:36:11 -04:00
John Keiser 90aa198430 Moar jsonexamples 2023-09-12 14:36:11 -04:00
John Keiser 02b488b88d All branch tests for twitter.json 2023-09-12 14:36:10 -04:00
John Keiser 47e3e95867 Branch tests with twitter.json. 2023-09-12 14:36:10 -04:00
Daniel Lemire 588c0677f5 Redesigning visit_primitive so that it is optimized for strings and
numbers.
2023-09-05 11:05:15 -04:00
Daniel Lemire 68ba9a1b2a Adding a few spaces (minor formatting) (#2059) 2023-08-30 20:23:58 -04:00
Daniel Lemire 6fed6bd29b Update README.md 2023-08-30 19:26:34 -04:00
Piotr Rżysko cc24bb4114 Removed number parsing fallback (#2056) 2023-08-29 18:28:21 -04:00
John Keiser 14ec0ca8f4 Merge pull request #2052 from simdjson/jkeiser/structural_indexer_step
Write out 4 structural indexes at a time instead of 8
2023-08-29 15:24:54 -07:00
John Keiser 8dabd02c3a Default to step = 4 2023-08-29 15:48:52 -04:00
Daniel Lemire c9692005fd This allows us to pass SIMDJSON_STRUCTURAL_INDEXER_STEP as a command-line option. (#2055) 2023-08-25 13:47:20 -04:00
Daniel Lemire 2e0a9397c9 Merge branch 'master' into jkeiser/structural_indexer_step 2023-08-25 11:34:13 -04:00
Daniel Lemire f7922075c3 Adding support for Apple event counters (#2054) 2023-08-25 11:33:24 -04:00
Daniel Lemire b2e20e04c9 Standard compatibility fixes (#2053)
* Standard compatibility fixes

* missing commit

* Should work.

* Fix.

* Fix.

* Should work now.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-08-25 10:18:02 -04:00
John Keiser adc9d18efd Change step for structural_indexes to 2 2023-08-23 19:02:05 -07:00
Daniel Lemire bf849e3619 Preparing release 2023-08-22 14:05:14 -04:00
Daniel Lemire c09e8dc225 Icelake processes 64 bytes at a time when processing strings. (#2050)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2023-08-18 14:37:10 -04:00
66 changed files with 1072985 additions and 177 deletions
+3 -2
View File
@@ -3,7 +3,7 @@
{"column": 95 },
{"column": 120 }
],
"files.trimTrailingWhitespace": true,
"files.trimTrailingWhitespace": false,
"files.associations": {
".clangd": "yaml",
"array": "cpp",
@@ -99,5 +99,6 @@
"shared_mutex": "cpp",
"ranges": "cpp",
"span": "cpp"
}
},
"editor.trimAutoWhitespace": true
}
+1 -1
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.2.2
VERSION 3.2.3
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.2.2"
PROJECT_NUMBER = "3.2.3"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
-2
View File
@@ -1,8 +1,6 @@
[![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)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
![MinGW64-CI](https://github.com/simdjson/simdjson/workflows/MinGW64-CI/badge.svg)
[![][license img]][license]
[![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/)
File diff suppressed because it is too large Load Diff
+13 -9
View File
@@ -445,7 +445,7 @@ struct benchmarker {
return 100.0 * a / b;
}
void print(bool tabbed_output) const {
void print(bool tabbed_output, bool stage1_only) const {
if (tabbed_output) {
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
SIMDJSON_PUSH_DISABLE_WARNINGS
@@ -503,17 +503,21 @@ struct benchmarker {
stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
}
printf("\n");
printf("All Stages (excluding allocation)\n");
print_aggregate("| " , all_stages_without_allocation.best);
// 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);
if(!stage1_only) {
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(!stage1_only) {
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;
+1 -1
View File
@@ -218,7 +218,7 @@ int main(int argc, char *argv[]) {
if (!options.verbose) { progress.erase(); }
for (size_t i=0; i<options.files.size(); i++) {
benchmarkers[i]->print(options.tabbed_output);
benchmarkers[i]->print(options.tabbed_output, options.stage1_only);
delete benchmarkers[i];
}
+28 -3
View File
@@ -34,11 +34,15 @@
#include <string>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include "linux-perf-events.h"
#include <libgen.h>
#endif
#if __APPLE__ && __aarch64__
#include "apple/apple_arm_events.h"
#endif
#include "simdjson.h"
using std::string;
@@ -134,7 +138,7 @@ struct event_collector {
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector(simdjson_unused bool quiet = false) : linux_events(vector<int>{
event_collector() : linux_events(vector<int>{
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
PERF_COUNT_HW_CPU_CYCLES,
PERF_COUNT_HW_INSTRUCTIONS,
@@ -149,8 +153,17 @@ struct event_collector {
bool has_events() {
return linux_events.is_working();
}
#elif __APPLE__ && __aarch64__
AppleEvents apple_events;
performance_counters diff;
event_collector() : diff(0) {
apple_events.setup_performance_counters();
}
bool has_events() {
return apple_events.setup_performance_counters();
}
#else
event_collector(simdjson_unused bool _quiet = false) {}
event_collector() {}
bool has_events() {
return false;
}
@@ -159,6 +172,8 @@ struct event_collector {
simdjson_inline void start() {
#if defined(__linux)
linux_events.start();
#elif __APPLE__ && __aarch64__
if(has_events()) { diff = apple_events.get_counters(); }
#endif
start_clock = steady_clock::now();
}
@@ -166,6 +181,16 @@ struct event_collector {
time_point<steady_clock> end_clock = steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
#elif __APPLE__ && __aarch64__
if(has_events()) {
performance_counters end = apple_events.get_counters();
diff = end - diff;
}
count.event_counts[0] = diff.cycles;
count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.missed_branches;
count.event_counts[3] = 0;
count.event_counts[4] = 0;
#endif
count.elapsed = end_clock - start_clock;
return count;
@@ -19,7 +19,7 @@ void maybe_display_implementation() {
template<typename B, typename R> static void run_json_benchmark(benchmark::State &state) {
maybe_display_implementation();
event_collector collector(true);
event_collector collector;
event_aggregate events;
// Warmup and equality check (make sure the data is right!)
+5
View File
@@ -117,7 +117,12 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_MACOSX_RPATH OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(SIMDJSON_STRUCTURAL_INDEXER_STEP CACHE STRING "the SIMDJSON_STRUCTURAL_INDEXER_STEP variable")
if(SIMDJSON_STRUCTURAL_INDEXER_STEP)
message(STATUS "Setting SIMDJSON_STRUCTURAL_INDEXER_STEP to ${SIMDJSON_STRUCTURAL_INDEXER_STEP}.")
add_compile_definitions(SIMDJSON_STRUCTURAL_INDEXER_STEP=${SIMDJSON_STRUCTURAL_INDEXER_STEP})
endif()
# LTO seems to create all sorts of fun problems. Let us
# disable temporarily.
#include(CheckIPOSupported)
+31 -31
View File
@@ -355,7 +355,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`. This will
scan through the object looking for the field with the matching string, doing a character-by-character
comparison. It may generate the error `simdjson::NO_SUCH_FIELD` if there is no such key in the object, it may throw an exception (see [Error Handling](#error-handling)). For efficiency reason, you should avoid looking up the same field repeatedly: e.g., do
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if(!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
not do `object["foo"]` followed by `object["foo"]` with the same `object` instance. Keep in mind that On Demand does not buffer or save the result of the parsing: if you repeatedly access `object["foo"]`, then it must repeatedly seek the key and parse the content. The library does not provide a distinct function to check if a key is present, instead we recommend you attempt to access the key: e.g., by doing `ondemand::value val{}; if (!object["foo"].get(val)) {...}`, you have that `val` contains the requested value inside the if clause. It is your responsibility as a user to temporarily keep a reference to the value (`auto v = object["foo"]`), or to consume the content and store it in your own data structures. If you consume an
object twice: `std::string_view(object["foo"]` followed by `std::string_view(object["foo"]` then your code
is in error. Furthermore, you can only consume one field at a time, on the same object. The
value instance you get from `content["bids"]` becomes invalid when you call `content["asks"]`.
@@ -381,7 +381,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> // parses and writes out the key, after unescaping it,
> // to a string buffer. It causes a performance penalty.
> std::string_view keyv = field.unescaped_key();
> if(keyv == "key") { std::cout << uint64_t(field.value()); }
> if (keyv == "key") { std::cout << uint64_t(field.value()); }
> }
> ```
>
@@ -445,7 +445,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> {
> ondemand::parser parser;
> for (ondemand::object car : parser.iterate(cars_json)) {
> if(uint64_t(car["year"]) > 2000) {
> if (uint64_t(car["year"]) > 2000) {
> arrays.push_back(simdjson::to_json_string(car["tire_pressure"]));
> }
> }
@@ -454,7 +454,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
> std::ostringstream oss;
> oss << "[";
> for(size_t i = 0; i < arrays.size(); i++) {
> if(i>0) { oss << ","; }
> if (i>0) { oss << ","; }
> oss << arrays[i];
> }
> oss << "]";
@@ -597,7 +597,7 @@ support for users who avoid exceptions. See [the simdjson error handling documen
case ondemand::json_type::null:
// We check that the value is indeed null
// otherwise: an error is thrown.
if(element.is_null()) {
if (element.is_null()) {
cout << "null";
}
break;
@@ -910,11 +910,11 @@ bool simple_error_example() {
ondemand::parser parser;
auto json = R"({"bad number":3.14.1 })"_padded;
ondemand::document doc;
if( parser.iterate(json).get(doc) != SUCCESS ) { return false; }
if (parser.iterate(json).get(doc) != SUCCESS) { return false; }
double x;
auto error = doc["bad number"].get_double().get(x);
// returns "simdjson::NUMBER_ERROR"
if(error != SUCCESS) {
if (error != SUCCESS) {
std::cout << error << std::endl;
return false;
}
@@ -976,10 +976,10 @@ it selects the key `"count"` within that object.
int main(void) {
simdjson::ondemand::parser parser;
auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::document tweets;
error = parser.iterate(json).get(tweets);
if( error ) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
simdjson::ondemand::value res;
error = tweets["search_metadata"]["count"].get(res);
if (error != SUCCESS) {
@@ -1010,12 +1010,12 @@ int main(void) {
simdjson::ondemand::document tweets;
padded_string json;
auto error = padded_string::load("twitter.json").get(json);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
error = parser.iterate(json).get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
}
```
@@ -1039,40 +1039,40 @@ bool parse() {
// Iterating through an array of objects
auto error = parser.iterate(cars_json).get(doc);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
ondemand::array cars; // invalid until the get() succeeds
error = doc.get_array().get(cars);
for (auto car_value : cars) {
ondemand::object car; // invalid until the get() succeeds
error = car_value.get_object().get(car);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
// Accessing a field by name
std::string_view make;
std::string_view model;
error = car["make"].get(make);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
error = car["model"].get(model);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
cout << "Make/Model: " << make << "/" << model << endl;
// Casting a JSON element to an integer
uint64_t year{};
error = car["year"].get(year);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
cout << "- This car is " << 2020 - year << " years old." << endl;
// Iterating through an array of floats
double total_tire_pressure = 0;
ondemand::array pressures;
error = car["tire_pressure"].get_array().get(pressures);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
for (auto tire_pressure_value : pressures) {
double tire_pressure;
error = tire_pressure_value.get_double().get(tire_pressure);
if(error) { std::cerr << error << std::endl; return false; }
if (error) { std::cerr << error << std::endl; return false; }
total_tire_pressure += tire_pressure;
}
cout << "- Average tire pressure: " << (total_tire_pressure / 4) << endl;
@@ -1088,7 +1088,7 @@ after you have initialized them and checked that there is no error:
ondemand::object car; // invalid until the get() succeeds
// the `car` instance should not use used before it is initialized
error = car_value.get_object().get(car);
if(error) {
if (error) {
// the `car` instance should not use used
} else {
// the `car` instance can be safely used
@@ -1102,20 +1102,20 @@ having to handle exceptions.
ondemand::parser parser;
ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if(error) { return false; }
if (error) { return false; }
ondemand::object object; // invalid until the get() succeeds
error = doc.get_object().get(object);
if(error) { return false; }
if (error) { return false; }
for(auto field : object) {
// We could replace 'field.key() with field.unescaped_key(),
// and ondemand::raw_json_string by std::string_view.
ondemand::raw_json_string keyv;
error = field.key().get(keyv);
if(error) { return false; }
if(keyv == "key") {
if (error) { return false; }
if (keyv == "key") {
uint64_t intvalue;
error = field.value().get(intvalue);
if(error) { return false; }
if (error) { return false; }
std::cout << intvalue;
}
}
@@ -1294,7 +1294,7 @@ content.
for (uint64_t values : array) {
std::cout << values << std::endl;
}
if(!doc.at_end()) {
if (!doc.at_end()) {
// In this instance, we will be left pointing at 'foo' since we have consumed the array [1,2].
std::cerr << "trailing content at byte index " << doc.current_location() - json.data() << std::endl;
}
@@ -1324,7 +1324,7 @@ before printout the data.
auto doc = parser.iterate(cars_json);
for (simdjson_unused ondemand::object car : doc) {
if(car["make"] == "Toyota") { count++; }
if (car["make"] == "Toyota") { count++; }
}
std::cout << "We have " << count << " Toyota cars.\n";
doc.rewind(); // requires simdjson 1.0 or better
@@ -1392,18 +1392,18 @@ ondemand::parser parser;
ondemand::document_stream stream;
size_t counter{0};
auto error = parser.iterate_many(json, 50).get(stream);
if( error ) { /* handle the error */ }
if (error) { /* handle the error */ }
for (auto doc: stream) {
if(counter < 6) {
if (counter < 6) {
int64_t val;
error = doc.at_pointer("/4").get(val);
if( error ) { /* handle the error */ }
if (error) { /* handle the error */ }
std::cout << "5 = " << val << std::endl;
} else {
ondemand::value val;
error = doc.at_pointer("/4").get(val);
// error == simdjson::CAPACITY
if(error) {
if (error) {
std::cerr << error << std::endl;
// We left 293 bytes unprocessed at the tail end of the input.
std::cout << " unprocessed bytes at the end: " << stream.truncated_bytes() << std::endl;
+2 -2
View File
@@ -329,10 +329,10 @@ int main(void) {
simdjson::dom::parser parser;
simdjson::dom::element tweets; // invalid until the get() succeeds
auto error = parser.load("twitter.json").get(tweets);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
uint64_t identifier;
error = tweets["statuses"].at(0)["id"].get(identifier);
if(error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
if (error) { std::cerr << error << std::endl; return EXIT_FAILURE; }
std::cout << identifier << std::endl;
return EXIT_SUCCESS;
}
+3 -3
View File
@@ -101,8 +101,8 @@ by comparing it with the null pointer.
```c++
auto my_implementation = simdjson::get_available_implementations()["haswell"];
if(! my_implementation) { exit(1); }
if(! my_implementation->supported_by_runtime_system()) { exit(1); }
if (! my_implementation) { exit(1); }
if (! my_implementation->supported_by_runtime_system()) { exit(1); }
simdjson::get_active_implementation() = my_implementation;
```
@@ -113,7 +113,7 @@ You should call `supported_by_runtime_system()` to compare the processor's featu
```c++
for (auto implementation : simdjson::get_available_implementations()) {
if(implementation->supported_by_runtime_system()) {
if (implementation->supported_by_runtime_system()) {
cout << implementation->name() << ": " << implementation->description() << endl;
}
}
+4 -4
View File
@@ -196,12 +196,12 @@ Let us illustrate the idea with code:
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json).get(stream);
if( error ) { /* do something */ }
if (error) { /* do something */ }
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto doc = *i;
if(!i.error()) {
if (!i.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
@@ -237,7 +237,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::ondemand::parser parser;
simdjson::ondemand::document_stream stream;
auto error = parser.iterate_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for(auto i = stream.begin(); i != stream.end(); ++i) {
std::cout << i.source() << std::endl;
}
@@ -269,7 +269,7 @@ Example:
// we pass 'true' to the allow_comma parameter, the batch size will be set to at least
// the document size.
auto error = parser.iterate_many(json, 32, true).get(doc_stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for (auto doc : doc_stream) {
std::cout << doc.type() << std::endl;
}
+4 -4
View File
@@ -679,11 +679,11 @@ in production systems:
ondemand::object c1 = parent["child1"];
// c1 owns the focus
//
if(std::string_view(c1["name"]) != "John") { ... }
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; }
// if (std::string_view(c2["name"]) != "Daniel") { return false; }
```
A correct usage is given by the following example:
@@ -697,7 +697,7 @@ in production systems:
{
ondemand::object c1 = parent["child1"];
// c1 grabbed the focus from parent
if(std::string_view(c1["name"]) != "John") { return false; }
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.
@@ -705,7 +705,7 @@ in production systems:
ondemand::object c2 = parent["child2"];
// c2 grabbed the focus from parent
// the following is safe:
if(std::string_view(c2["name"]) != "Daniel") { return false; }
if (std::string_view(c2["name"]) != "Daniel") { return false; }
}
```
+3 -3
View File
@@ -184,12 +184,12 @@ Let us illustrate the idea with code:
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json).get(stream);
if( error ) { /* do something */ }
if (error) { /* do something */ }
auto i = stream.begin();
size_t count{0};
for(; i != stream.end(); ++i) {
auto doc = *i;
if(!doc.error()) {
if (!doc.error()) {
std::cout << "got full document at " << i.current_index() << std::endl;
std::cout << i.source() << std::endl;
count++;
@@ -225,7 +225,7 @@ Consider the following example where a truncated document (`{"key":"intentionall
simdjson::dom::parser parser;
simdjson::dom::document_stream stream;
auto error = parser.parse_many(json,json.size()).get(stream);
if(error) { std::cerr << error << std::endl; return; }
if (error) { std::cerr << error << std::endl; return; }
for(auto doc : stream) {
std::cout << doc << std::endl;
}
+277
View File
@@ -0,0 +1,277 @@
number of iterations 3000
| Original (jsonexamples/twitter-padded-numbers.json) 773
|- 18-digit numbers (jsonexamples/twitter18.json) 757
|- 8-digit numbers (jsonexamples/twitter8.json) 766
|- 1-digit numbers (jsonexamples/twitter1.json) 544
| |- strings -> no backslashes (jsonexamples/twitter1-nobackslash.json) 499
| |- strings -> no utf8 (jsonexamples/twitter1-nobackslash-noutf8.json) 489
| |- strings -> empty strings (jsonexamples/twitter1-emptystring.json) 351
| |- no number/bool/null -> all strings (jsonexamples/twitter-allstrings.json) 216
| |- no objects -> all arrays (jsonexamples/twitter-allstrings-arrays.json) 427
| |- arrays -> no nested arrays (jsonexamples/twitter-allstrings-flatarray.json) 3
jsonexamples/twitter-padded-numbers.json
========================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2576 ( 25.0 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2843 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 20.7212 ns per block ( 97.66%) - 0.3238 ns per byte - 3.8647 ns per structural - 3.0885 GB/s
| Cycles : 64.1524 per block ( 97.86%) - 1.0024 per byte - 11.9652 per structural - 3.096 GHz est. frequency
| Instructions : 200.2565 per block (100.00%) - 3.1291 per byte - 37.3502 per structural - 3.122 per cycle
| Misses : 873 branch misses ( 93.08%) - 0 cache misses ( 0.00%) - 28032.00 cache references
|- Stage 1
| Speed : 6.0073 ns per block ( 28.31%) - 0.0939 ns per byte - 1.1204 ns per structural - 10.6534 GB/s
| Cycles : 18.6130 per block ( 28.39%) - 0.2908 per byte - 3.4715 per structural - 3.098 GHz est. frequency
| Instructions : 61.8107 per block ( 30.87%) - 0.9658 per byte - 11.5284 per structural - 3.321 per cycle
| Misses : 93 branch misses ( 9.92%) - 0 cache misses ( 0.00%) - 12229.00 cache references
|- Stage 2
| Speed : 14.6555 ns per block ( 69.07%) - 0.2290 ns per byte - 2.7334 ns per structural - 4.3668 GB/s
| Cycles : 45.3587 per block ( 69.19%) - 0.7088 per byte - 8.4599 per structural - 3.095 GHz est. frequency
| Instructions : 138.4458 per block ( 69.13%) - 2.1633 per byte - 25.8217 per structural - 3.052 per cycle
| Misses : 773 branch misses ( 82.42%) - 0 cache misses ( 0.00%) - 15925.00 cache references
4682.2 documents parsed per second (best)
jsonexamples/twitter18.json
===========================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2585 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2847 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 21.1739 ns per block ( 93.63%) - 0.3309 ns per byte - 3.9492 ns per structural - 3.0225 GB/s
| Cycles : 65.5570 per block ( 97.40%) - 1.0244 per byte - 12.2271 per structural - 3.096 GHz est. frequency
| Instructions : 205.6160 per block (100.00%) - 3.2129 per byte - 38.3498 per structural - 3.136 per cycle
| Misses : 856 branch misses ( 97.20%) - 0 cache misses ( 0.00%) - 28297.00 cache references
|- Stage 1
| Speed : 5.9968 ns per block ( 26.52%) - 0.0937 ns per byte - 1.1185 ns per structural - 10.6720 GB/s
| Cycles : 18.5808 per block ( 27.61%) - 0.2903 per byte - 3.4655 per structural - 3.098 GHz est. frequency
| Instructions : 61.8107 per block ( 30.06%) - 0.9658 per byte - 11.5284 per structural - 3.327 per cycle
| Misses : 103 branch misses ( 11.70%) - 0 cache misses ( 0.00%) - 12271.00 cache references
|- Stage 2
| Speed : 15.0964 ns per block ( 66.75%) - 0.2359 ns per byte - 2.8157 ns per structural - 4.2393 GB/s
| Cycles : 46.7271 per block ( 69.43%) - 0.7301 per byte - 8.7151 per structural - 3.095 GHz est. frequency
| Instructions : 143.8053 per block ( 69.94%) - 2.2470 per byte - 26.8213 per structural - 3.078 per cycle
| Misses : 757 branch misses ( 85.96%) - 0 cache misses ( 0.00%) - 16107.00 cache references
4582.1 documents parsed per second (best)
jsonexamples/twitter8.json
==========================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2576 ( 25.0 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2843 ( 27.6 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 20.6976 ns per block ( 97.48%) - 0.3234 ns per byte - 3.8603 ns per structural - 3.0920 GB/s
| Cycles : 64.0733 per block ( 97.72%) - 1.0012 per byte - 11.9504 per structural - 3.096 GHz est. frequency
| Instructions : 200.2565 per block (100.00%) - 3.1291 per byte - 37.3502 per structural - 3.125 per cycle
| Misses : 860 branch misses ( 91.48%) - 0 cache misses ( 0.00%) - 28700.00 cache references
|- Stage 1
| Speed : 6.0103 ns per block ( 28.31%) - 0.0939 ns per byte - 1.1210 ns per structural - 10.6481 GB/s
| Cycles : 18.6231 per block ( 28.40%) - 0.2910 per byte - 3.4734 per structural - 3.099 GHz est. frequency
| Instructions : 61.8107 per block ( 30.87%) - 0.9658 per byte - 11.5284 per structural - 3.319 per cycle
| Misses : 102 branch misses ( 10.85%) - 0 cache misses ( 0.00%) - 11933.00 cache references
|- Stage 2
| Speed : 14.6407 ns per block ( 68.95%) - 0.2288 ns per byte - 2.7307 ns per structural - 4.3712 GB/s
| Cycles : 45.3059 per block ( 69.10%) - 0.7079 per byte - 8.4501 per structural - 3.095 GHz est. frequency
| Instructions : 138.4458 per block ( 69.13%) - 2.1633 per byte - 25.8217 per structural - 3.056 per cycle
| Misses : 766 branch misses ( 81.48%) - 0 cache misses ( 0.00%) - 16566.00 cache references
4687.6 documents parsed per second (best)
jsonexamples/twitter1.json
==========================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 2289 ( 22.2 %) - escape 604 ( 5.9 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 1096 ( 10.6 %) - escape 646 ( 6.3 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 20.1200 ns per block ( 93.76%) - 0.3144 ns per byte - 3.7526 ns per structural - 3.1808 GB/s
| Cycles : 62.2865 per block ( 97.52%) - 0.9733 per byte - 11.6172 per structural - 3.096 GHz est. frequency
| Instructions : 194.5666 per block (100.00%) - 3.0402 per byte - 36.2889 per structural - 3.124 per cycle
| Misses : 673 branch misses ( 97.88%) - 0 cache misses ( 0.00%) - 28320.00 cache references
|- Stage 1
| Speed : 6.0046 ns per block ( 27.98%) - 0.0938 ns per byte - 1.1199 ns per structural - 10.6582 GB/s
| Cycles : 18.6140 per block ( 29.14%) - 0.2909 per byte - 3.4717 per structural - 3.100 GHz est. frequency
| Instructions : 61.8107 per block ( 31.77%) - 0.9658 per byte - 11.5284 per structural - 3.321 per cycle
| Misses : 113 branch misses ( 16.43%) - 0 cache misses ( 0.00%) - 12175.00 cache references
|- Stage 2
| Speed : 14.0716 ns per block ( 65.57%) - 0.2199 ns per byte - 2.6245 ns per structural - 4.5480 GB/s
| Cycles : 43.5464 per block ( 68.18%) - 0.6804 per byte - 8.1219 per structural - 3.095 GHz est. frequency
| Instructions : 132.7559 per block ( 68.23%) - 2.0744 per byte - 24.7605 per structural - 3.049 per cycle
| Misses : 544 branch misses ( 79.11%) - 0 cache misses ( 0.00%) - 16212.00 cache references
4822.1 documents parsed per second (best)
jsonexamples/twitter1-nobackslash.json
======================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 2289 ( 22.2 %) - escape 0 ( 0.0 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 1096 ( 10.6 %) - escape 0 ( 0.0 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 19.3473 ns per block ( 94.75%) - 0.3023 ns per byte - 3.6085 ns per structural - 3.3078 GB/s
| Cycles : 59.8930 per block ( 97.21%) - 0.9359 per byte - 11.1707 per structural - 3.096 GHz est. frequency
| Instructions : 191.2882 per block (100.00%) - 2.9890 per byte - 35.6774 per structural - 3.194 per cycle
| Misses : 624 branch misses ( 99.02%) - 2 cache misses ( 30.96%) - 28632.00 cache references
|- Stage 1
| Speed : 5.9519 ns per block ( 29.15%) - 0.0930 ns per byte - 1.1101 ns per structural - 10.7526 GB/s
| Cycles : 18.4460 per block ( 29.94%) - 0.2882 per byte - 3.4404 per structural - 3.099 GHz est. frequency
| Instructions : 61.1661 per block ( 31.98%) - 0.9558 per byte - 11.4082 per structural - 3.316 per cycle
| Misses : 92 branch misses ( 14.60%) - 1 cache misses ( 15.48%) - 12277.00 cache references
|- Stage 2
| Speed : 13.3283 ns per block ( 65.28%) - 0.2083 ns per byte - 2.4859 ns per structural - 4.8016 GB/s
| Cycles : 41.2453 per block ( 66.95%) - 0.6445 per byte - 7.6927 per structural - 3.095 GHz est. frequency
| Instructions : 130.1221 per block ( 68.02%) - 2.0332 per byte - 24.2693 per structural - 3.155 per cycle
| Misses : 499 branch misses ( 79.19%) - 1 cache misses ( 15.48%) - 16358.00 cache references
5014.7 documents parsed per second (best)
jsonexamples/twitter1-nobackslash-noutf8.json
=============================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1270 ( 12.3 %) - 1+ structurals 9037 ( 87.7 %) - 8+ structurals 2584 ( 25.1 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 932 ( 9.0 %) - 1+ structurals 932 ( 9.0 %) - 8+ structurals 2859 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 18.9506 ns per block ( 97.78%) - 0.2961 ns per byte - 3.5345 ns per structural - 3.3771 GB/s
| Cycles : 58.6756 per block ( 97.97%) - 0.9168 per byte - 10.9437 per structural - 3.096 GHz est. frequency
| Instructions : 186.6244 per block (100.00%) - 2.9161 per byte - 34.8076 per structural - 3.181 per cycle
| Misses : 634 branch misses ( 99.08%) - 0 cache misses ( 0.00%) - 28596.00 cache references
|- Stage 1
| Speed : 5.4613 ns per block ( 28.18%) - 0.0853 ns per byte - 1.0186 ns per structural - 11.7184 GB/s
| Cycles : 16.9236 per block ( 28.26%) - 0.2644 per byte - 3.1565 per structural - 3.099 GHz est. frequency
| Instructions : 56.5024 per block ( 30.28%) - 0.8829 per byte - 10.5383 per structural - 3.339 per cycle
| Misses : 130 branch misses ( 20.32%) - 0 cache misses ( 0.00%) - 12365.00 cache references
|- Stage 2
| Speed : 13.4108 ns per block ( 69.19%) - 0.2096 ns per byte - 2.5013 ns per structural - 4.7721 GB/s
| Cycles : 41.5073 per block ( 69.31%) - 0.6486 per byte - 7.7416 per structural - 3.095 GHz est. frequency
| Instructions : 130.1221 per block ( 69.72%) - 2.0332 per byte - 24.2693 per structural - 3.135 per cycle
| Misses : 489 branch misses ( 76.42%) - 0 cache misses ( 0.00%) - 16279.00 cache references
5119.7 documents parsed per second (best)
jsonexamples/twitter1-emptystring.json
======================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2856 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3123 ( 30.3 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 18.0587 ns per block ( 93.58%) - 0.2822 ns per byte - 3.3682 ns per structural - 3.5439 GB/s
| Cycles : 55.9116 per block ( 97.35%) - 0.8736 per byte - 10.4282 per structural - 3.096 GHz est. frequency
| Instructions : 183.3181 per block (100.00%) - 2.8644 per byte - 34.1909 per structural - 3.279 per cycle
| Misses : 473 branch misses (101.19%) - 1 cache misses ( 15.11%) - 18833.00 cache references
|- Stage 1
| Speed : 5.4026 ns per block ( 28.00%) - 0.0844 ns per byte - 1.0077 ns per structural - 11.8457 GB/s
| Cycles : 16.7461 per block ( 29.16%) - 0.2617 per byte - 3.1233 per structural - 3.100 GHz est. frequency
| Instructions : 56.5028 per block ( 30.82%) - 0.8829 per byte - 10.5384 per structural - 3.374 per cycle
| Misses : 112 branch misses ( 23.96%) - 0 cache misses ( 0.00%) - 10717.00 cache references
|- Stage 2
| Speed : 12.6022 ns per block ( 65.30%) - 0.1969 ns per byte - 2.3505 ns per structural - 5.0783 GB/s
| Cycles : 39.0031 per block ( 67.91%) - 0.6094 per byte - 7.2745 per structural - 3.095 GHz est. frequency
| Instructions : 126.8154 per block ( 69.18%) - 1.9816 per byte - 23.6525 per structural - 3.251 per cycle
| Misses : 351 branch misses ( 75.09%) - 1 cache misses ( 15.11%) - 8120.00 cache references
5372.6 documents parsed per second (best)
jsonexamples/twitter-allstrings.json
====================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2853 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3107 ( 30.1 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 19.6482 ns per block ( 97.20%) - 0.3070 ns per byte - 3.6646 ns per structural - 3.2572 GB/s
| Cycles : 60.8255 per block ( 97.62%) - 0.9504 per byte - 11.3446 per structural - 3.096 GHz est. frequency
| Instructions : 183.8463 per block (100.00%) - 2.8727 per byte - 34.2895 per structural - 3.023 per cycle
| Misses : 300 branch misses ( 96.97%) - 0 cache misses ( 0.00%) - 18361.00 cache references
|- Stage 1
| Speed : 5.4053 ns per block ( 26.74%) - 0.0845 ns per byte - 1.0081 ns per structural - 11.8399 GB/s
| Cycles : 16.7545 per block ( 26.89%) - 0.2618 per byte - 3.1249 per structural - 3.100 GHz est. frequency
| Instructions : 56.5028 per block ( 30.73%) - 0.8829 per byte - 10.5384 per structural - 3.372 per cycle
| Misses : 98 branch misses ( 31.68%) - 1 cache misses ( 18.41%) - 10786.00 cache references
|- Stage 2
| Speed : 14.0367 ns per block ( 69.44%) - 0.2193 ns per byte - 2.6180 ns per structural - 4.5593 GB/s
| Cycles : 43.4371 per block ( 69.71%) - 0.6787 per byte - 8.1015 per structural - 3.095 GHz est. frequency
| Instructions : 127.3447 per block ( 69.27%) - 1.9898 per byte - 23.7513 per structural - 2.932 per cycle
| Misses : 216 branch misses ( 69.82%) - 0 cache misses ( 0.00%) - 7835.00 cache references
4937.9 documents parsed per second (best)
jsonexamples/twitter-allstrings-arrays.json
===========================================
10306 blocks - 659626 bytes - 55262 structurals ( 8.4 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2853 ( 27.7 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3107 ( 30.1 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 23.6768 ns per block ( 96.29%) - 0.3700 ns per byte - 4.4160 ns per structural - 2.7030 GB/s
| Cycles : 72.7530 per block ( 95.79%) - 1.1368 per byte - 13.5693 per structural - 3.073 GHz est. frequency
| Instructions : 225.0789 per block (100.00%) - 3.5170 per byte - 41.9798 per structural - 3.094 per cycle
| Misses : 547 branch misses (104.38%) - 0 cache misses ( 0.00%) - 17259.00 cache references
|- Stage 1
| Speed : 5.4063 ns per block ( 21.99%) - 0.0845 ns per byte - 1.0083 ns per structural - 11.8376 GB/s
| Cycles : 16.7598 per block ( 22.07%) - 0.2619 per byte - 3.1259 per structural - 3.100 GHz est. frequency
| Instructions : 56.5028 per block ( 25.10%) - 0.8829 per byte - 10.5384 per structural - 3.371 per cycle
| Misses : 101 branch misses ( 19.27%) - 0 cache misses ( 0.00%) - 10570.00 cache references
|- Stage 2
| Speed : 18.2062 ns per block ( 74.04%) - 0.2845 ns per byte - 3.3957 ns per structural - 3.5152 GB/s
| Cycles : 55.7937 per block ( 73.46%) - 0.8718 per byte - 10.4062 per structural - 3.065 GHz est. frequency
| Instructions : 168.5761 per block ( 74.90%) - 2.6341 per byte - 31.4414 per structural - 3.021 per cycle
| Misses : 427 branch misses ( 81.48%) - 0 cache misses ( 0.00%) - 6797.00 cache references
4097.7 documents parsed per second (best)
jsonexamples/twitter-allstrings-flatarray.json
==============================================
10306 blocks - 659626 bytes - 49890 structurals ( 7.6 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1279 ( 12.4 %) - 1+ structurals 9028 ( 87.6 %) - 8+ structurals 2100 ( 20.4 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 946 ( 9.2 %) - 1+ structurals 946 ( 9.2 %) - 8+ structurals 2668 ( 25.9 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 20.5314 ns per block ( 98.77%) - 0.3208 ns per byte - 4.2417 ns per structural - 3.1171 GB/s
| Cycles : 63.5535 per block ( 98.95%) - 0.9931 per byte - 13.1298 per structural - 3.095 GHz est. frequency
| Instructions : 208.9967 per block (100.00%) - 3.2657 per byte - 43.1776 per structural - 3.289 per cycle
| Misses : 94 branch misses ( 99.52%) - 0 cache misses ( 0.00%) - 15420.00 cache references
|- Stage 1
| Speed : 5.3985 ns per block ( 25.97%) - 0.0844 ns per byte - 1.1153 ns per structural - 11.8548 GB/s
| Cycles : 16.7318 per block ( 26.05%) - 0.2614 per byte - 3.4567 per structural - 3.099 GHz est. frequency
| Instructions : 56.4917 per block ( 27.03%) - 0.8827 per byte - 11.6709 per structural - 3.376 per cycle
| Misses : 87 branch misses ( 92.11%) - 0 cache misses ( 0.00%) - 9889.00 cache references
|- Stage 2
| Speed : 15.0691 ns per block ( 72.49%) - 0.2355 ns per byte - 3.1132 ns per structural - 4.2470 GB/s
| Cycles : 46.6318 per block ( 72.61%) - 0.7286 per byte - 9.6339 per structural - 3.095 GHz est. frequency
| Instructions : 152.5050 per block ( 72.97%) - 2.3830 per byte - 31.5067 per structural - 3.270 per cycle
| Misses : 3 branch misses ( 3.18%) - 0 cache misses ( 0.00%) - 5610.00 cache references
4725.5 documents parsed per second (best)
jsonexamples/twitter-allstrings-flatobjects.json
================================================
10306 blocks - 659626 bytes - 56091 structurals ( 8.5 %)
special blocks with: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 1269 ( 12.3 %) - 1+ structurals 9038 ( 87.7 %) - 8+ structurals 2973 ( 28.8 %) - 16+ structurals 0 ( 0.0 %)
special block flips: utf8 0 ( 0.0 %) - escape 0 ( 0.0 %) - 0 structurals 928 ( 9.0 %) - 1+ structurals 928 ( 9.0 %) - 8+ structurals 3093 ( 30.0 %) - 16+ structurals 0 ( 0.0 %)
All Stages (excluding allocation)
| Speed : 19.4292 ns per block ( 97.71%) - 0.3036 ns per byte - 3.5702 ns per structural - 3.2939 GB/s
| Cycles : 60.1577 per block ( 97.90%) - 0.9400 per byte - 11.0543 per structural - 3.096 GHz est. frequency
| Instructions : 181.6747 per block (100.00%) - 2.8388 per byte - 33.3836 per structural - 3.020 per cycle
| Misses : 508 branch misses (100.57%) - 0 cache misses ( 0.00%) - 17421.00 cache references
|- Stage 1
| Speed : 5.4138 ns per block ( 27.22%) - 0.0846 ns per byte - 0.9948 ns per structural - 11.8213 GB/s
| Cycles : 16.7833 per block ( 27.31%) - 0.2622 per byte - 3.0840 per structural - 3.100 GHz est. frequency
| Instructions : 56.5028 per block ( 31.10%) - 0.8829 per byte - 10.3827 per structural - 3.367 per cycle
| Misses : 99 branch misses ( 19.60%) - 0 cache misses ( 0.00%) - 10517.00 cache references
|- Stage 2
| Speed : 13.9445 ns per block ( 70.12%) - 0.2179 ns per byte - 2.5624 ns per structural - 4.5895 GB/s
| Cycles : 43.1562 per block ( 70.23%) - 0.6743 per byte - 7.9302 per structural - 3.095 GHz est. frequency
| Instructions : 125.1719 per block ( 68.90%) - 1.9559 per byte - 23.0010 per structural - 2.900 per cycle
| Misses : 402 branch misses ( 79.59%) - 0 cache misses ( 0.00%) - 6973.00 cache references
4993.6 documents parsed per second (best)
+8
View File
@@ -32,4 +32,12 @@
#error simdjson requires a compiler compliant with the C++11 standard
#endif
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
#endif // SIMDJSON_COMPILER_CHECK_H
+4 -8
View File
@@ -186,8 +186,7 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q. In very rare cases, even that
// will not suffice, though it is seemingly very hard to find such a scenario.
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
@@ -202,12 +201,9 @@ simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative,
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// At this point, we might need to add at most one to firstproduct, but this
// can only change the value of firstproduct.high if firstproduct.low is maximal.
if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) {
// This is very unlikely, but if so, we need to do much more work!
return false;
}
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
@@ -16,7 +16,7 @@ using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
+2 -2
View File
@@ -4,7 +4,7 @@
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.2"
#define SIMDJSON_VERSION "3.2.3"
namespace simdjson {
enum {
@@ -19,7 +19,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 2
SIMDJSON_VERSION_REVISION = 3
};
} // namespace simdjson
+263
View File
@@ -0,0 +1,263 @@
from itertools import groupby
from pathlib import Path
from typing import Any, Iterable, Literal, Optional, OrderedDict, cast
from io import BufferedWriter, BufferedReader
import re
import sys
ContainerState = Literal['orig', 'array', 'flat']
ScalarState = Literal['orig', '1digit', 'str']
StringState = Literal['orig', 'unescaped', 'ascii', 'empty']
CONTAINER_STATES: list[ContainerState] = ['orig', 'array', 'flat']
SCALAR_STATES: list[ScalarState] = ['orig', '1digit', 'str']
STRING_STATES: list[StringState] = ['orig', 'unescaped', 'ascii', 'empty']
class Result:
class Metric:
def __init__(self, name: str, value: float, units: str):
self.name = name
self.value = value
self.units = units
def best(self, other: 'Result.Metric'):
assert self.name == other.name
assert self.units == other.units
if self.name == 'Speed':
return self if self.value > other.value else other
else:
return self if self.value < other.value else other
class Stage:
def __init__(self, stage: str):
self.stage = stage
self.metrics: dict[str, Result.Metric] = {}
@property
def speed(self):
return self.metrics['Speed']
@property
def cycles(self):
return self.metrics['Cycles']
@property
def instructions(self):
return self.metrics['Instructions']
@property
def misses(self):
return self.metrics['Misses']
def _merge_in(self, other: 'Result.Stage'):
assert self.stage == other.stage
for metric in other.metrics.values():
if metric.name not in self.metrics:
self.metrics[metric.name] = metric
self.metrics[metric.name] = self.metrics[metric.name].best(metric)
def __init__(self, json_file: Path):
self.json_file = json_file
self.stages = dict[str, Result.Stage]()
match = re.match(r'(.*)-([^-]*)-([^-]*)-([^-]*)$', json_file.stem)
if match:
self.base_json_file = match.group(1)
assert match.group(2) in CONTAINER_STATES
self.container_state: ContainerState = cast(ContainerState, match.group(2))
assert match.group(3) in SCALAR_STATES
self.scalar_state: ScalarState = cast(ScalarState, match.group(3))
assert match.group(4) in STRING_STATES
self.string_state: StringState = cast(StringState, match.group(4))
else:
self.base_json_file = self.json_file.stem
self.container_state = 'orig'
self.scalar_state = 'orig'
self.string_state = 'orig'
self.docs_per_second: float = -1
def __lt__(self, other: 'Result'):
if self.base_json_file != other.base_json_file:
return self.base_json_file < other.base_json_file
if self.container_state != other.container_state:
return CONTAINER_STATES.index(self.container_state) < CONTAINER_STATES.index(other.container_state)
if self.scalar_state != other.scalar_state:
return SCALAR_STATES.index(self.scalar_state) < SCALAR_STATES.index(other.scalar_state)
if self.string_state != other.string_state:
return STRING_STATES.index(self.string_state) < STRING_STATES.index(other.string_state)
return False
def merge(self, other: 'Result'):
merged = Result(self.json_file)
merged._merge_in(self)
merged._merge_in(other)
return merged
def _merge_in(self, other: 'Result'):
assert self.json_file == other.json_file
if other.docs_per_second > self.docs_per_second:
self.docs_per_second = other.docs_per_second
for stage in other.stages.values():
if stage.stage not in self.stages:
self.stages[stage.stage] = Result.Stage(stage.stage)
self.stages[stage.stage]._merge_in(stage)
@property
def stage1(self):
return self.stages['Stage 1']
@property
def stage2(self):
return self.stages['Stage 2']
def read_results(results_file: Path):
with open(result_file, 'rt') as input:
result = None
stage = None
prev_line = None
for line in input:
if re.match(r'=+$', line):
assert result is None
assert prev_line is not None
result = Result(Path(prev_line.strip()))
# Stage
match = re.match(r'\|-(.+)$', line)
if match:
assert result is not None
stage = Result.Stage(match.group(1).strip())
result.stages[stage.stage] = stage
# Metrics
if stage is not None:
match = re.match(r'\|([^:]+):\s*([-+0-9.]+)\s+([^(-]+)', line)
if match and stage is not None:
metric = Result.Metric(match.group(1).strip(), float(match.group(2)), match.group(3).strip())
stage.metrics[metric.name] = metric
# Documents per second
match = re.match(r'\s*([-+0-9.]+)\s*documents parsed per second', line)
if match:
assert result is not None
result.docs_per_second = float(match.group(1))
yield result
result = None
stage = None
prev_line = line
# Merge multiple results for the same file
all_results = dict[Path, Result]()
for result_file in sys.argv[1:]:
for result in read_results(Path(result_file)):
if result.json_file in all_results:
all_results[result.json_file] = all_results[result.json_file].merge(result)
else:
all_results[result.json_file] = result
def print_row(row: Iterable, key_lengths: OrderedDict[str, int], rjust: set[str]):
column_iter = iter(row)
for (key, width) in key_lengths.items():
value = str(next(column_iter))
if key in rjust:
print(f"| {str(value).rjust(width)} ", end='')
else:
print(f"| {str(value).ljust(width)} ", end='')
print("|")
def print_table(rows: list[OrderedDict], key_lengths: Optional[OrderedDict[str, int]] = None, rjust = set[str]()):
if key_lengths is None:
key_lengths = OrderedDict[str, int]()
for entry in rows:
for (key, value) in entry.items():
if key not in key_lengths:
key_lengths[key] = len(key)
key_lengths[key] = max(key_lengths[key], len(str(value)))
print_row(list(key_lengths.keys()), key_lengths, rjust)
print("|", *[ f"{(':' if key in rjust else '').rjust(len+2, '-')}|" for (key, len) in key_lengths.items() ], sep='')
for row in rows:
print_row(row.values(), key_lengths, rjust)
for (file, results) in groupby(sorted(all_results.values()), lambda r: r.base_json_file):
results = [*results]
print()
print(f"# {file}.json Branch Miss Variants")
print()
print_table(
[
OrderedDict([
('Contain', result.container_state),
('Scalars', result.scalar_state),
('Strings', result.string_state),
('Cycles', '%.4f' % result.stage2.cycles.value),
('Instrs', '%.4f' % result.stage2.instructions.value),
('Misses', int(result.stage2.misses.value)),
('Docs/sec', '%.1f' % result.docs_per_second),
])
for result in results
],
rjust = set(['Cycles', 'Instrs', 'Misses', 'Docs/sec'])
)
misses = {
(r.container_state, r.scalar_state, r.string_state): int(r.stage2.misses.value)
for r in results
}
print()
print('## Container State Transition Miss Reduction')
print()
rows = list[OrderedDict[str, object]]()
PRINT_STRING_STATES: list[StringState] = [s for s in STRING_STATES if s != 'unescaped']
for (i,from_state) in enumerate(CONTAINER_STATES[0:-1]):
for to_state in CONTAINER_STATES[i+1:]:
rows.append(OrderedDict([
('Contain', f"{from_state} -> {to_state}"),
*[
(
f"{scalar_state} {string_state}",
misses[(from_state, scalar_state, string_state)] - misses[(to_state, scalar_state, string_state)]
)
for scalar_state in SCALAR_STATES
for string_state in PRINT_STRING_STATES
]
]))
print_table(rows, rjust = [*rows[0].keys()][1:])
print()
print('## Scalar State Transition Miss Reduction')
print()
rows = list[OrderedDict[str, object]]()
for (i,from_state) in enumerate(SCALAR_STATES[0:-1]):
for to_state in SCALAR_STATES[i+1:]:
rows.append(OrderedDict([
('Scalars', f"{from_state} -> {to_state}"),
*[
(
f"{container_state} {string_state}",
misses[(container_state, from_state, string_state)] - misses[(container_state, to_state, string_state)]
)
for container_state in CONTAINER_STATES
for string_state in PRINT_STRING_STATES
]
]))
print_table(rows, rjust = [*rows[0].keys()][1:])
print()
print('## String State Transition Miss Reduction')
print()
rows = list[OrderedDict[str, object]]()
for (i,from_state) in enumerate(STRING_STATES[0:-1]):
for to_state in STRING_STATES[i+1:]:
rows.append(OrderedDict([
('Strings', f"{from_state} -> {to_state}"),
*[
(
f"{container_state} {scalar_state}",
misses[(container_state, scalar_state, from_state)] - misses[(container_state, scalar_state, to_state)]
)
for container_state in CONTAINER_STATES
for scalar_state in SCALAR_STATES
]
]))
print_table(rows, rjust = [*rows[0].keys()][1:])
+170
View File
@@ -0,0 +1,170 @@
from pathlib import Path
from typing import Literal
from io import BufferedWriter, BufferedReader
import re
import sys
ContainerState = Literal['orig', 'array', 'flat']
ScalarState = Literal['orig', '1digit', 'str']
StringState = Literal['orig', 'unescaped', 'ascii', 'empty']
CONTAINER_STATES: list[ContainerState] = ['orig', 'array', 'flat']
SCALAR_STATES: list[ScalarState] = ['orig', '1digit', 'str']
STRING_STATES: list[StringState] = ['orig', 'unescaped', 'ascii', 'empty']
def right_pad(padded_length: int, b: bytes):
assert len(b) <= padded_length
return b + b' '*(padded_length-len(b))
def right_pad2(r: bytes, b: bytes):
print(f"right_pad({r}, {b})")
return right_pad(len(r), b)
class JsonFile:
def __init__(self,
original_json_file: Path,
container_state: ContainerState = 'orig',
scalar_state: ScalarState = 'orig',
string_state: StringState = 'orig'):
self.original_json_file = original_json_file
self.container_state: ContainerState = container_state
self.scalar_state: ScalarState = scalar_state
self.string_state: StringState = string_state
@property
def path(self):
if self.container_state == 'orig' and self.scalar_state == 'orig' and self.string_state == 'orig':
return self.original_json_file
else:
return self.original_json_file.with_stem(f"{self.original_json_file.stem}-{self.container_state}-{self.scalar_state}-{self.string_state}")
def write(self, force: bool = False):
if force or not self.path.exists():
with open(self.path, 'wb') as out:
self.write_to(out)
def with_container_state(self, container_state: ContainerState):
return JsonFile(self.original_json_file, container_state, self.scalar_state, self.string_state)
def with_scalar_state(self, scalar_state: ScalarState):
return JsonFile(self.original_json_file, self.container_state, scalar_state, self.string_state)
def with_string_state(self, string_state: StringState):
return JsonFile(self.original_json_file, self.container_state, self.scalar_state, string_state)
def open(self):
return open(self.path, 'rb')
def write_to(self, out: BufferedWriter):
if self.string_state == 'unescaped':
return self.remove_escapes(self.with_string_state('orig').open(), out)
elif self.string_state == 'ascii':
return self.remove_utf8(self.with_string_state('unescaped').open(), out)
elif self.string_state == 'empty':
return self.replace_strings(self.with_string_state('ascii').open(), out, b'""')
else:
assert self.string_state == 'orig'
if self.scalar_state == '1digit':
return self.replace_numbers(self.with_scalar_state('orig').open(), out, b'0')
elif self.scalar_state == 'str':
return self.replace_non_strings(self.with_scalar_state('1digit').open(), out, b'""')
else:
assert self.scalar_state == 'orig'
if self.container_state == 'array':
return self.replace_objects_with_arrays(self.with_container_state('orig').open(), out)
elif self.container_state == 'flat':
return self.remove_nesting(self.with_container_state('array').open(), out)
else:
assert self.container_state == 'orig'
assert self.path.exists()
def remove_escapes(self, input: BufferedReader, out: BufferedWriter):
for line in input:
out.write(re.sub(rb'\\(.)', rb'__', line))
def remove_utf8(self, input: BufferedReader, out: BufferedWriter):
for line in input:
out.write(bytes([(b if b < 128 else ord('_')) for b in line]))
def replace_strings(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
for line in input:
assert line.find(b'\\') == -1
out.write(re.sub(rb'"([^"]*)"', lambda s: right_pad(len(s.group(0)), replacement), line))
def replace_numbers(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
for line in input:
for (non_string, string) in self.split_by_strings(line):
out.write(re.sub(rb'\s*[-0-9][-+0-9.eE]*\s*', lambda s: right_pad(len(s.group(0)), replacement), non_string))
out.write(string)
def replace_non_strings(self, input: BufferedReader, out: BufferedWriter, replacement: bytes):
for line in input:
for (non_string, string) in self.split_by_strings(line):
out.write(re.sub(rb'\s*[^,:{}[\] \r\t\n]+\s*', lambda s: right_pad(len(s.group(0)), replacement), non_string))
out.write(string)
def replace_objects_with_arrays(self, input: BufferedReader, out: BufferedWriter):
for line in input:
for (non_string, string) in self.split_by_strings(line):
out.write(non_string.replace(b'{', b'[').replace(b'}', b']').replace(b':', b','))
out.write(string)
def remove_nesting(self, input: BufferedReader, out: BufferedWriter):
prev_line = None
is_first_line = True
lines = iter(input)
line = next(lines, None)
next_line = None
while line is not None:
out_line = b''
# Remove any { } or [ ], and replace : with ,
for (non_string, string) in self.split_by_strings(line):
# Replace empty objects or arrays with ""
non_string = re.sub(rb'(\{(\s|\n)*\}|\[(\s|\n*)\])', lambda s: right_pad(len(s.group(0)), b'""'), non_string)
# Remove other braces entirely
non_string = re.sub(rb'([{}[\]])', lambda s: right_pad(len(s.group(0)), b' '), non_string)
# Replace : with ,
non_string = non_string.replace(b':', b',')
out_line += non_string
out_line += string
# Replace the first character with [
if next_line is None:
assert line[0] in [ord(x) for x in [ b'[', b'{', b' ', b'\t', b'\r', b'\n' ]]
out_line = b'[' + out_line[1:]
# Replace the last character with ]
next_line = next(lines, None)
if next_line is None:
assert out_line[-1] in [ord(x) for x in [ b']', b'}', b' ', b'\t', b'\r', b'\n' ]]
out_line = bytes(out_line[:-1] + b']')
line = next_line
out.write(out_line)
def split_by_strings(self, line: bytes):
result: list[tuple[bytes, bytes]] = []
while len(line) > 0:
quote = line.find(b'"')
if quote == -1:
result.append((line, b''))
break
end_quote = quote+1
while line[end_quote] != ord(b'"'):
assert end_quote < len(line)
if line[end_quote] == ord(b'\\'):
end_quote += 1
end_quote += 1
result.append((line[:quote],line[quote:end_quote+1]))
line = line[end_quote+1:]
return result
original_json_file = Path(sys.argv[1])
for container_state in CONTAINER_STATES:
for scalar_state in SCALAR_STATES:
for string_state in STRING_STATES:
output_file = JsonFile(original_json_file, container_state, scalar_state, string_state)
if output_file.path.exists():
print(f"Skipping {output_file.path}")
continue
print(f"Writing {output_file.path}")
output_file.write()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2023-08-02 16:00:45 -0400. Do not edit! */
/* auto-generated on 2023-08-18 14:37:10 -0400. Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
@@ -24489,7 +24489,7 @@ using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
@@ -27073,7 +27073,7 @@ using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
+5 -5
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2023-08-02 16:00:45 -0400. Do not edit! */
/* auto-generated on 2023-08-18 14:37:10 -0400. Do not edit! */
/* including simdjson.h: */
/* begin file simdjson.h */
#ifndef SIMDJSON_H
@@ -2314,7 +2314,7 @@ namespace std {
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "3.2.2"
#define SIMDJSON_VERSION "3.2.3"
namespace simdjson {
enum {
@@ -2329,7 +2329,7 @@ enum {
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 2
SIMDJSON_VERSION_REVISION = 3
};
} // namespace simdjson
@@ -18211,7 +18211,7 @@ using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
@@ -56851,7 +56851,7 @@ using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
+67 -77
View File
@@ -27,6 +27,58 @@ public:
simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {}
#if SIMDJSON_PREFER_REVERSE_BITS
/**
* ARM lacks a fast trailing zero instruction, but it has a fast
* bit reversal instruction and a fast leading zero instruction.
* Thus it may be profitable to reverse the bits (once) and then
* to rely on a sequence of instructions that call the leading
* zero instruction.
*
* Performance notes:
* The chosen routine is not optimal in terms of data dependency
* since zero_leading_bit might require two instructions. However,
* it tends to minimize the total number of instructions which is
* beneficial.
*/
simdjson_inline void write_index(uint32_t idx, uint64_t& rev_bits, int i) {
int lz = leading_zeroes(rev_bits);
this->tail[i] = static_cast<uint32_t>(idx) + lz;
rev_bits = zero_leading_bit(rev_bits, lz);
}
#else
/**
* Under recent x64 systems, we often have both a fast trailing zero
* instruction and a fast 'clear-lower-bit' instruction so the following
* algorithm can be competitive.
*/
simdjson_inline void write_index(uint32_t idx, uint64_t& bits, int i) {
this->tail[i] = idx + trailing_zeroes(bits);
bits = clear_lowest_bit(bits);
}
#endif // SIMDJSON_PREFER_REVERSE_BITS
template <int START, int N>
simdjson_inline int write_indexes(uint32_t idx, uint64_t& bits) {
write_index(idx, bits, START);
SIMDJSON_IF_CONSTEXPR (N > 1) {
write_indexes<(N-1>0?START+1:START), (N-1>=0?N-1:1)>(idx, bits);
}
return START+N;
}
template <int START, int END, int STEP>
simdjson_inline int write_indexes_stepped(uint32_t idx, uint64_t& bits, int cnt) {
write_indexes<START, STEP>(idx, bits);
SIMDJSON_IF_CONSTEXPR ((START+STEP) < END) {
if (simdjson_unlikely((START+STEP) < cnt)) {
write_indexes_stepped<(START+STEP<END?START+STEP:END), END, STEP>(idx, bits, cnt);
}
}
return ((END-START) % STEP) == 0 ? END : (END-START) - ((END-START) % STEP) + STEP;
}
// flatten out values in 'bits' assuming that they are are to have values of idx
// plus their position in the bitvector, and store these indexes at
// base_ptr[base] incrementing base as we go
@@ -44,91 +96,29 @@ public:
// it helps tremendously.
if (bits == 0)
return;
#if SIMDJSON_PREFER_REVERSE_BITS
/**
* ARM lacks a fast trailing zero instruction, but it has a fast
* bit reversal instruction and a fast leading zero instruction.
* Thus it may be profitable to reverse the bits (once) and then
* to rely on a sequence of instructions that call the leading
* zero instruction.
*
* Performance notes:
* The chosen routine is not optimal in terms of data dependency
* since zero_leading_bit might require two instructions. However,
* it tends to minimize the total number of instructions which is
* beneficial.
*/
uint64_t rev_bits = reverse_bits(bits);
int cnt = static_cast<int>(count_ones(bits));
int i = 0;
// Do the first 8 all together
for (; i<8; i++) {
int lz = leading_zeroes(rev_bits);
this->tail[i] = static_cast<uint32_t>(idx) + lz;
rev_bits = zero_leading_bit(rev_bits, lz);
}
// Do the next 8 all together (we hope in most cases it won't happen at all
// and the branch is easily predicted).
if (simdjson_unlikely(cnt > 8)) {
i = 8;
for (; i<16; i++) {
int lz = leading_zeroes(rev_bits);
this->tail[i] = static_cast<uint32_t>(idx) + lz;
rev_bits = zero_leading_bit(rev_bits, lz);
}
#if SIMDJSON_PREFER_REVERSE_BITS
bits = reverse_bits(bits);
#endif
#ifdef SIMDJSON_STRUCTURAL_INDEXER_STEP
static constexpr const int STEP = SIMDJSON_STRUCTURAL_INDEXER_STEP;
#else
static constexpr const int STEP = 4;
#endif
static constexpr const int STEP_UNTIL = 24;
// Most files don't have 16+ structurals per block, so we take several basically guaranteed
// branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :)
// or the start of a value ("abc" true 123) every four characters.
if (simdjson_unlikely(cnt > 16)) {
i = 16;
while (rev_bits != 0) {
int lz = leading_zeroes(rev_bits);
this->tail[i++] = static_cast<uint32_t>(idx) + lz;
rev_bits = zero_leading_bit(rev_bits, lz);
write_indexes_stepped<0, STEP_UNTIL, STEP>(idx, bits, cnt);
SIMDJSON_IF_CONSTEXPR (STEP_UNTIL < 64) {
if (simdjson_unlikely(STEP_UNTIL < cnt)) {
for (int i=STEP_UNTIL; i<cnt; i++) {
write_index(idx, bits, i);
}
}
}
this->tail += cnt;
#else // SIMDJSON_PREFER_REVERSE_BITS
/**
* Under recent x64 systems, we often have both a fast trailing zero
* instruction and a fast 'clear-lower-bit' instruction so the following
* algorithm can be competitive.
*/
int cnt = static_cast<int>(count_ones(bits));
// Do the first 8 all together
for (int i=0; i<8; i++) {
this->tail[i] = idx + trailing_zeroes(bits);
bits = clear_lowest_bit(bits);
}
// Do the next 8 all together (we hope in most cases it won't happen at all
// and the branch is easily predicted).
if (simdjson_unlikely(cnt > 8)) {
for (int i=8; i<16; i++) {
this->tail[i] = idx + trailing_zeroes(bits);
bits = clear_lowest_bit(bits);
}
// Most files don't have 16+ structurals per block, so we take several basically guaranteed
// branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :)
// or the start of a value ("abc" true 123) every four characters.
if (simdjson_unlikely(cnt > 16)) {
int i = 16;
do {
this->tail[i] = idx + trailing_zeroes(bits);
bits = clear_lowest_bit(bits);
i++;
} while (i < cnt);
}
}
this->tail += cnt;
#endif
}
#endif // SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER
@@ -365,4 +355,4 @@ simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementa
// Clear CUSTOM_BIT_INDEXER so other implementations can set it if they need to.
#undef SIMDJSON_GENERIC_JSON_STRUCTURAL_INDEXER_CUSTOM_BIT_INDEXER
#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H
#endif // SIMDJSON_SRC_GENERIC_STAGE1_JSON_STRUCTURAL_INDEXER_H
@@ -170,14 +170,6 @@ using namespace simd;
this->error |= this->prev_incomplete;
}
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
simdjson_inline void check_next_input(const simd8x64<uint8_t>& input) {
if(simdjson_likely(is_ascii(input))) {
this->error |= this->prev_incomplete;
+7 -5
View File
@@ -303,15 +303,17 @@ simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primit
}
template<typename V>
simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept {
// Use the fact that most scalars are going to be either strings or numbers.
if(*value == '"') {
return visitor.visit_string(*this, value);
} else if (((*value - '0') < 10) || (*value == '-')) {
return visitor.visit_number(*this, value);
}
// true, false, null are uncommon.
switch (*value) {
case '"': return visitor.visit_string(*this, value);
case 't': return visitor.visit_true_atom(*this, value);
case 'f': return visitor.visit_false_atom(*this, value);
case 'n': return visitor.visit_null_atom(*this, value);
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return visitor.visit_number(*this, value);
default:
log_error("Non-value found when value was expected!");
return TAPE_ERROR;
+1 -1
View File
@@ -32,7 +32,7 @@ static bool parse_and_validate(const std::string src, T expected) {
const padded_string pstr{src};
simdjson::dom::parser parser;
if constexpr (std::is_same<int64_t, T>::value) {
SIMDJSON_IF_CONSTEXPR (std::is_same<int64_t, T>::value) {
int64_t actual{};
ASSERT_SUCCESS( parser.parse(pstr)["key"].get(actual) );
std::cout << std::boolalpha << "test: " << (expected == actual) << std::endl;
+68
View File
@@ -0,0 +1,68 @@
# twitter.json Branch Miss Variants
| Contain | Scalars | Strings | Cycles | Instrs | Misses | Docs/sec |
|---------|---------|-----------|--------:|---------:|-------:|---------:|
| orig | orig | orig | 43.1557 | 140.4823 | 262 | 5051.3 |
| orig | orig | unescaped | 40.9559 | 137.7313 | 210 | 5257.1 |
| orig | orig | ascii | 40.8708 | 137.7313 | 219 | 5420.6 |
| orig | orig | empty | 38.6624 | 134.2775 | 193 | 5634.1 |
| orig | 1digit | orig | 41.5941 | 134.5389 | 299 | 5195.7 |
| orig | 1digit | unescaped | 39.1917 | 131.7879 | 244 | 5422.2 |
| orig | 1digit | ascii | 39.3877 | 131.7879 | 242 | 5560.8 |
| orig | 1digit | empty | 37.1633 | 128.3341 | 204 | 5796.6 |
| orig | str | orig | 46.2681 | 128.7201 | 193 | 4816.3 |
| orig | str | unescaped | 43.7883 | 125.9691 | 186 | 5022.5 |
| orig | str | ascii | 43.9507 | 125.9691 | 177 | 5147.6 |
| orig | str | empty | 41.1975 | 122.5153 | 110 | 5397.7 |
| array | orig | orig | 50.1138 | 171.0958 | 469 | 4548.1 |
| array | orig | unescaped | 47.4255 | 168.3448 | 429 | 4738.2 |
| array | orig | ascii | 47.5435 | 168.3448 | 426 | 4851.1 |
| array | orig | empty | 45.5023 | 164.8910 | 406 | 5012.3 |
| array | 1digit | orig | 47.5542 | 165.1527 | 386 | 4693.9 |
| array | 1digit | unescaped | 45.8350 | 162.4017 | 342 | 4862.4 |
| array | 1digit | ascii | 45.6465 | 162.4017 | 335 | 4995.8 |
| array | 1digit | empty | 43.3332 | 158.9479 | 262 | 5205.8 |
| array | str | orig | 52.0014 | 160.6582 | 331 | 4416.4 |
| array | str | unescaped | 49.2261 | 157.9072 | 272 | 4618.3 |
| array | str | ascii | 49.2947 | 157.9072 | 271 | 4728.9 |
| array | str | empty | 46.3058 | 154.4534 | 179 | 4967.3 |
| flat | orig | orig | 43.3339 | 159.8664 | 407 | 5049.4 |
| flat | orig | unescaped | 41.7361 | 157.1154 | 384 | 5190.1 |
| flat | orig | ascii | 41.6981 | 157.1154 | 393 | 5330.9 |
| flat | orig | empty | 39.4102 | 153.6616 | 386 | 5583.5 |
| flat | 1digit | orig | 41.7575 | 153.9234 | 364 | 5180.8 |
| flat | 1digit | unescaped | 40.0306 | 151.1724 | 308 | 5344.4 |
| flat | 1digit | ascii | 39.8567 | 151.1724 | 313 | 5507.9 |
| flat | 1digit | empty | 37.3057 | 147.7186 | 250 | 5789.3 |
| flat | str | orig | 46.2458 | 149.4289 | 471 | 4821.6 |
| flat | str | unescaped | 44.4161 | 146.6778 | 413 | 4985.2 |
| flat | str | ascii | 44.4534 | 146.6778 | 414 | 5112.6 |
| flat | str | empty | 41.0894 | 143.2241 | 3 | 5419.1 |
## Container State Transition Miss Reduction
| Contain | orig orig | orig ascii | orig empty | 1digit orig | 1digit ascii | 1digit empty | str orig | str ascii | str empty |
|---------------|----------:|-----------:|-----------:|------------:|-------------:|-------------:|---------:|----------:|----------:|
| orig -> array | -207 | -207 | -213 | -87 | -93 | -58 | -138 | -94 | -69 |
| orig -> flat | -145 | -174 | -193 | -65 | -71 | -46 | -278 | -237 | 107 |
| array -> flat | 62 | 33 | 20 | 22 | 22 | 12 | -140 | -143 | 176 |
## Scalar State Transition Miss Reduction
| Scalars | orig orig | orig ascii | orig empty | array orig | array ascii | array empty | flat orig | flat ascii | flat empty |
|----------------|----------:|-----------:|-----------:|-----------:|------------:|------------:|----------:|-----------:|-----------:|
| orig -> 1digit | -37 | -23 | -11 | 83 | 91 | 144 | 43 | 80 | 136 |
| orig -> str | 69 | 42 | 83 | 138 | 155 | 227 | -64 | -21 | 383 |
| 1digit -> str | 106 | 65 | 94 | 55 | 64 | 83 | -107 | -101 | 247 |
## String State Transition Miss Reduction
| Strings | orig orig | orig 1digit | orig str | array orig | array 1digit | array str | flat orig | flat 1digit | flat str |
|--------------------|----------:|------------:|---------:|-----------:|-------------:|----------:|----------:|------------:|---------:|
| orig -> unescaped | 52 | 55 | 7 | 40 | 44 | 59 | 23 | 56 | 58 |
| orig -> ascii | 43 | 57 | 16 | 43 | 51 | 60 | 14 | 51 | 57 |
| orig -> empty | 69 | 95 | 83 | 63 | 124 | 152 | 21 | 114 | 468 |
| unescaped -> ascii | -9 | 2 | 9 | 3 | 7 | 1 | -9 | -5 | -1 |
| unescaped -> empty | 17 | 40 | 76 | 23 | 80 | 93 | -2 | 58 | 410 |
| ascii -> empty | 26 | 38 | 67 | 20 | 73 | 92 | 7 | 63 | 411 |
+535435
View File
File diff suppressed because it is too large Load Diff