mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Remove set_capacity, replace with allocate
Makes allocation point more predictable
This commit is contained in:
@@ -256,7 +256,6 @@ static void print_json(State& state) noexcept {
|
||||
// Prints the number of results in twitter.json
|
||||
padded_string json = get_corpus(JSON_TEST_PATH);
|
||||
dom::parser parser;
|
||||
if (!parser.allocate_capacity(json.length())) { cerr << "allocation failed" << endl; return; }
|
||||
if (int error = json_parse(json, parser); error != SUCCESS) { cerr << error_message(error) << endl; return; }
|
||||
for (auto _ : state) {
|
||||
std::stringstream s;
|
||||
|
||||
@@ -9,10 +9,10 @@ const padded_string EMPTY_ARRAY("[]", 2);
|
||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||
static void json_parse(State& state) {
|
||||
dom::parser parser;
|
||||
if (parser.set_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
ParsedJson pj;
|
||||
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
for (auto _ : state) {
|
||||
auto error = simdjson::json_parse(EMPTY_ARRAY, parser);
|
||||
auto error = json_parse(EMPTY_ARRAY, pj);
|
||||
if (error) { return; }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ SIMDJSON_POP_DISABLE_WARNINGS
|
||||
BENCHMARK(json_parse);
|
||||
static void parser_parse_error_code(State& state) {
|
||||
dom::parser parser;
|
||||
if (parser.set_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
||||
for (auto _ : state) {
|
||||
auto [doc, error] = parser.parse(EMPTY_ARRAY);
|
||||
if (error) { return; }
|
||||
@@ -29,7 +29,7 @@ static void parser_parse_error_code(State& state) {
|
||||
BENCHMARK(parser_parse_error_code);
|
||||
static void parser_parse_exception(State& state) {
|
||||
dom::parser parser;
|
||||
if (parser.set_capacity(EMPTY_ARRAY.length())) { return; }
|
||||
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
||||
for (auto _ : state) {
|
||||
try {
|
||||
UNUSED dom::element doc = parser.parse(EMPTY_ARRAY);
|
||||
|
||||
@@ -294,7 +294,10 @@ struct benchmarker {
|
||||
// Allocate dom::parser
|
||||
collector.start();
|
||||
dom::parser parser;
|
||||
error_code alloc_error = parser.set_capacity(json.size());
|
||||
error_code error = parser.allocate(json.size());
|
||||
if (error) {
|
||||
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON result: " + error_message(error));
|
||||
}
|
||||
event_count allocate_count = collector.end();
|
||||
allocate_stage << allocate_count;
|
||||
// Run it once to get hot buffers
|
||||
@@ -305,14 +308,11 @@ struct benchmarker {
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc_error) {
|
||||
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON result: " + error_message(alloc_error));
|
||||
}
|
||||
verbose() << "[verbose] allocated memory for parsed JSON " << endl;
|
||||
|
||||
// Stage 1 (find structurals)
|
||||
collector.start();
|
||||
error_code error = active_implementation->stage1((const uint8_t *)json.data(), json.size(), parser, false);
|
||||
error = active_implementation->stage1((const uint8_t *)json.data(), json.size(), parser, false);
|
||||
event_count stage1_count = collector.end();
|
||||
stage1 << stage1_count;
|
||||
if (error) {
|
||||
|
||||
@@ -56,25 +56,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
|
||||
|
||||
@@ -36,7 +36,7 @@ int main (int argc, char *argv[]){
|
||||
for (auto i = 0; i < 3; i++) {
|
||||
//Actual test
|
||||
simdjson::dom::parser parser;
|
||||
simdjson::error_code alloc_error = parser.set_capacity(p.size());
|
||||
simdjson::error_code alloc_error = parser.allocate(p.size());
|
||||
if (alloc_error) {
|
||||
std::cerr << alloc_error << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -161,7 +161,7 @@ int main(int argc, char *argv[]) {
|
||||
#ifdef __linux__
|
||||
simdjson::dom::parser parser;
|
||||
const simdjson::implementation &stage_parser = *simdjson::active_implementation;
|
||||
simdjson::error_code alloc_error = parser.set_capacity(p.size());
|
||||
simdjson::error_code alloc_error = parser.allocate(p.size());
|
||||
if (alloc_error) {
|
||||
std::cerr << alloc_error << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ without bound:
|
||||
|
||||
```c++
|
||||
dom::parser parser(0); // This parser will refuse to automatically grow capacity
|
||||
simdjson::error_code allocate_error = parser.set_capacity(1024*1024); // This allocates enough capacity to handle documents <= 1MB
|
||||
simdjson::error_code allocate_error = parser.allocate(1024*1024); // This allocates enough capacity to handle documents <= 1MB
|
||||
if (allocate_error) { cerr << allocate_error << endl; exit(1); }
|
||||
|
||||
for (web_request request : listen()) {
|
||||
|
||||
+36
-52
@@ -350,7 +350,7 @@ public:
|
||||
std::unique_ptr<uint8_t[]> string_buf;
|
||||
|
||||
private:
|
||||
inline error_code set_capacity(size_t len) noexcept;
|
||||
inline error_code allocate(size_t len) noexcept;
|
||||
template<typename T>
|
||||
friend class simdjson::minify;
|
||||
friend class parser;
|
||||
@@ -609,13 +609,10 @@ public:
|
||||
* @param max_capacity The maximum document length the parser can automatically handle. The parser
|
||||
* will allocate more capacity on an as needed basis (when it sees documents too big to handle)
|
||||
* up to this amount. The parser still starts with zero capacity no matter what this number is:
|
||||
* to allocate an initial capacity, call set_capacity() after constructing the parser. Defaults
|
||||
* to SIMDJSON_MAXSIZE_BYTES (the largest single document simdjson can process).
|
||||
* @param max_depth The maximum depth--number of nested objects and arrays--this parser can handle.
|
||||
* This will not be allocated until parse() is called for the first time. Defaults to
|
||||
* DEFAULT_MAX_DEPTH.
|
||||
* to allocate an initial capacity, call allocate() after constructing the parser.
|
||||
* Defaults to SIMDJSON_MAXSIZE_BYTES (the largest single document simdjson can process).
|
||||
*/
|
||||
really_inline parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
|
||||
really_inline parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
|
||||
|
||||
/**
|
||||
* Take another parser's buffers and state.
|
||||
@@ -835,6 +832,30 @@ public:
|
||||
/** @private We do not want to allow implicit conversion from C string to std::string. */
|
||||
really_inline simdjson_result<element> parse_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
|
||||
|
||||
/**
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
* and `max_depth` depth.
|
||||
*
|
||||
* @param capacity The new capacity.
|
||||
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
|
||||
* @return The error, if there is one.
|
||||
*/
|
||||
WARN_UNUSED inline error_code allocate(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
|
||||
|
||||
/**
|
||||
* @private deprecated because it returns bool instead of error_code, which is our standard for
|
||||
* failures. Use allocate() instead.
|
||||
*
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
* and `max_depth` depth.
|
||||
*
|
||||
* @param capacity The new capacity.
|
||||
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
|
||||
* @return true if successful, false if allocation failed.
|
||||
*/
|
||||
[[deprecated("Use allocate() instead.")]]
|
||||
WARN_UNUSED inline bool allocate_capacity(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
|
||||
|
||||
/**
|
||||
* The largest document this parser can support without reallocating.
|
||||
*
|
||||
@@ -858,17 +879,6 @@ public:
|
||||
*/
|
||||
really_inline size_t max_depth() const noexcept;
|
||||
|
||||
/**
|
||||
* Set capacity. This is the largest document this parser can support without reallocating.
|
||||
*
|
||||
* This will allocate or deallocate as necessary.
|
||||
*
|
||||
* @param capacity The new capacity, in bytes.
|
||||
*
|
||||
* @return MEMALLOC if unsuccessful, SUCCESS otherwise.
|
||||
*/
|
||||
WARN_UNUSED inline error_code set_capacity(size_t capacity) noexcept;
|
||||
|
||||
/**
|
||||
* Set max_capacity. This is the largest document this parser can automatically support.
|
||||
*
|
||||
@@ -880,32 +890,6 @@ public:
|
||||
*/
|
||||
really_inline void set_max_capacity(size_t max_capacity) noexcept;
|
||||
|
||||
/**
|
||||
* Set the maximum level of nested object and arrays supported by this parser.
|
||||
*
|
||||
* This will allocate or deallocate as necessary.
|
||||
*
|
||||
* @param max_depth The new maximum depth, in bytes.
|
||||
*
|
||||
* @return MEMALLOC if unsuccessful, SUCCESS otherwise.
|
||||
*/
|
||||
WARN_UNUSED inline error_code set_max_depth(size_t max_depth) noexcept;
|
||||
|
||||
/**
|
||||
* @private @deprecated Use set_capacity() instead.
|
||||
*
|
||||
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
|
||||
* and `max_depth` depth.
|
||||
*
|
||||
* Equivalent to calling set_capacity() and set_max_depth().
|
||||
*
|
||||
* @param capacity The new capacity.
|
||||
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
|
||||
* @return true if successful, false if allocation failed.
|
||||
*/
|
||||
[[deprecated("Use set_capacity() instead.")]]
|
||||
WARN_UNUSED inline bool allocate_capacity(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
|
||||
|
||||
/** @private Use the new DOM API instead */
|
||||
class Iterator;
|
||||
/** @private Use simdjson_error instead */
|
||||
@@ -987,13 +971,6 @@ public:
|
||||
really_inline bool on_number_double(double value) noexcept; ///< @private
|
||||
|
||||
private:
|
||||
/**
|
||||
* The maximum document length this parser supports.
|
||||
*
|
||||
* Buffers are large enough to handle any document up to this length.
|
||||
*/
|
||||
size_t _capacity{0};
|
||||
|
||||
/**
|
||||
* The maximum document length this parser will automatically support.
|
||||
*
|
||||
@@ -1001,12 +978,19 @@ private:
|
||||
*/
|
||||
size_t _max_capacity;
|
||||
|
||||
/**
|
||||
* The maximum document length this parser supports.
|
||||
*
|
||||
* Buffers are large enough to handle any document up to this length.
|
||||
*/
|
||||
size_t _capacity{0};
|
||||
|
||||
/**
|
||||
* The maximum depth (number of nested objects and arrays) supported by this parser.
|
||||
*
|
||||
* Defaults to DEFAULT_MAX_DEPTH.
|
||||
*/
|
||||
size_t _max_depth;
|
||||
size_t _max_depth{0};
|
||||
|
||||
/**
|
||||
* The loaded buffer (reused each time load() is called)
|
||||
|
||||
@@ -193,7 +193,7 @@ inline element document::root() const noexcept {
|
||||
#define RETURN_ERROR(CODE, MESSAGE) return REPORT_ERROR((CODE), (MESSAGE));
|
||||
|
||||
WARN_UNUSED
|
||||
inline error_code document::set_capacity(size_t capacity) noexcept {
|
||||
inline error_code document::allocate(size_t capacity) noexcept {
|
||||
if (capacity == 0) {
|
||||
string_buf.reset();
|
||||
tape.reset();
|
||||
@@ -310,8 +310,8 @@ inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
|
||||
//
|
||||
// parser inline implementation
|
||||
//
|
||||
really_inline parser::parser(size_t max_capacity, size_t max_depth) noexcept
|
||||
: _max_capacity{max_capacity}, _max_depth{max_depth}, loaded_bytes(nullptr, &aligned_free_char) {}
|
||||
really_inline parser::parser(size_t max_capacity) noexcept
|
||||
: _max_capacity{max_capacity}, loaded_bytes(nullptr, &aligned_free_char) {}
|
||||
inline bool parser::is_valid() const noexcept { return valid; }
|
||||
inline int parser::get_error_code() const noexcept { return error; }
|
||||
inline std::string parser::get_error_message() const noexcept { return error_message(error); }
|
||||
@@ -431,98 +431,97 @@ really_inline size_t parser::max_depth() const noexcept {
|
||||
}
|
||||
|
||||
WARN_UNUSED
|
||||
inline error_code parser::set_capacity(size_t capacity) noexcept {
|
||||
if (_capacity == capacity) {
|
||||
return SUCCESS;
|
||||
}
|
||||
inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept {
|
||||
//
|
||||
// If capacity has changed, reallocate capacity-based buffers
|
||||
//
|
||||
if (_capacity != capacity) {
|
||||
// Set capacity to 0 until we finish, in case there's an error
|
||||
_capacity = 0;
|
||||
|
||||
// Set capacity to 0 until we finish, in case there's an error
|
||||
_capacity = 0;
|
||||
//
|
||||
// Reallocate the document
|
||||
//
|
||||
error_code err = doc.allocate(capacity);
|
||||
if (err) { return err; }
|
||||
|
||||
//
|
||||
// Don't allocate 0 bytes, just return.
|
||||
//
|
||||
if (capacity == 0) {
|
||||
structural_indexes.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize stage 1 output
|
||||
//
|
||||
uint32_t max_structures = ROUNDUP_N(capacity, 64) + 2 + 7;
|
||||
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); // TODO realloc
|
||||
if (!structural_indexes) {
|
||||
return MEMALLOC;
|
||||
}
|
||||
|
||||
_capacity = capacity;
|
||||
|
||||
//
|
||||
// Reallocate the document
|
||||
// If capacity hasn't changed, but the document was taken, allocate a new document.
|
||||
//
|
||||
error_code err = doc.set_capacity(capacity);
|
||||
if (err) { return err; }
|
||||
|
||||
//
|
||||
// Don't allocate 0 bytes, just return.
|
||||
//
|
||||
if (capacity == 0) {
|
||||
structural_indexes.reset();
|
||||
return SUCCESS;
|
||||
} else if (!doc.tape) {
|
||||
error_code err = doc.allocate(capacity);
|
||||
if (err) { return err; }
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize stage 1 output
|
||||
// If max_depth has changed, reallocate those buffers
|
||||
//
|
||||
uint32_t max_structures = ROUNDUP_N(capacity, 64) + 2 + 7;
|
||||
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); // TODO realloc
|
||||
if (!structural_indexes) {
|
||||
return MEMALLOC;
|
||||
}
|
||||
if (max_depth != _max_depth) {
|
||||
_max_depth = 0;
|
||||
|
||||
_capacity = capacity;
|
||||
if (max_depth == 0) {
|
||||
ret_address.reset();
|
||||
containing_scope_offset.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize stage 2 state
|
||||
//
|
||||
containing_scope_offset.reset(new (std::nothrow) uint32_t[max_depth]); // TODO realloc
|
||||
#ifdef SIMDJSON_USE_COMPUTED_GOTO
|
||||
ret_address.reset(new (std::nothrow) void *[max_depth]);
|
||||
#else
|
||||
ret_address.reset(new (std::nothrow) char[max_depth]);
|
||||
#endif
|
||||
|
||||
if (!ret_address || !containing_scope_offset) {
|
||||
// Could not allocate memory
|
||||
return MEMALLOC;
|
||||
}
|
||||
|
||||
_max_depth = max_depth;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
WARN_UNUSED
|
||||
inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept {
|
||||
return !allocate(capacity, max_depth);
|
||||
}
|
||||
|
||||
really_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
|
||||
_max_capacity = max_capacity;
|
||||
}
|
||||
|
||||
WARN_UNUSED inline error_code parser::set_max_depth(size_t max_depth) noexcept {
|
||||
if (max_depth == _max_depth && ret_address) { return SUCCESS; }
|
||||
|
||||
_max_depth = 0;
|
||||
|
||||
if (max_depth == 0) {
|
||||
ret_address.reset();
|
||||
containing_scope_offset.reset();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize stage 2 state
|
||||
//
|
||||
containing_scope_offset.reset(new (std::nothrow) uint32_t[max_depth]); // TODO realloc
|
||||
#ifdef SIMDJSON_USE_COMPUTED_GOTO
|
||||
ret_address.reset(new (std::nothrow) void *[max_depth]);
|
||||
#else
|
||||
ret_address.reset(new (std::nothrow) char[max_depth]);
|
||||
#endif
|
||||
|
||||
if (!ret_address || !containing_scope_offset) {
|
||||
// Could not allocate memory
|
||||
return MEMALLOC;
|
||||
}
|
||||
|
||||
_max_depth = max_depth;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
WARN_UNUSED inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept {
|
||||
return !set_capacity(capacity) && !set_max_depth(max_depth);
|
||||
}
|
||||
|
||||
inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept {
|
||||
// If we don't have enough capacity, (try to) automatically bump it.
|
||||
if (unlikely(desired_capacity > capacity())) {
|
||||
// If the document was taken, reallocate that too.
|
||||
// Both in one if statement to minimize unlikely branching.
|
||||
if (unlikely(desired_capacity > capacity() || !doc.tape)) {
|
||||
if (desired_capacity > max_capacity()) {
|
||||
return error = CAPACITY;
|
||||
}
|
||||
|
||||
error = set_capacity(desired_capacity);
|
||||
if (error) { return error; }
|
||||
}
|
||||
|
||||
// Allocate depth-based buffers if they aren't already.
|
||||
error = set_max_depth(max_depth());
|
||||
if (error) { return error; }
|
||||
|
||||
// If the last doc was taken, we need to allocate a new one
|
||||
if (!doc.tape) {
|
||||
error = doc.set_capacity(desired_capacity);
|
||||
if (error) { return error; }
|
||||
return allocate(desired_capacity, _max_depth > 0 ? _max_depth : DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
|
||||
@@ -125,7 +125,7 @@ void performance_2() {
|
||||
// The web_request part of this is aspirational, so we compile as much as we can here
|
||||
void performance_3() {
|
||||
dom::parser parser(0); // This parser will refuse to automatically grow capacity
|
||||
simdjson::error_code allocate_error = parser.set_capacity(1024*1024); // This allocates enough capacity to handle documents <= 1MB
|
||||
simdjson::error_code allocate_error = parser.allocate(1024*1024); // This allocates enough capacity to handle documents <= 1MB
|
||||
if (allocate_error) { cerr << allocate_error << endl; exit(1); }
|
||||
|
||||
// for (web_request request : listen()) {
|
||||
|
||||
Reference in New Issue
Block a user