diff --git a/bench/bench.cpp b/bench/bench.cpp index a349d7ab..bc1cbca3 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -7,6 +7,8 @@ // Official repository: https://github.com/vinniefalco/json // +//#define RAPIDJSON_SSE42 + #include "lib/nlohmann/single_include/nlohmann/json.hpp" #include "lib/rapidjson/include/rapidjson/rapidjson.h" @@ -19,7 +21,6 @@ #include #include #include -#include #include #include @@ -47,10 +48,7 @@ class any_impl { public: virtual ~any_impl() = default; - virtual string_view name() const noexcept = 0; - virtual void work(string_view s) const = 0; - virtual void parse(string_view s, int repeat) const = 0; virtual void serialize(string_view s, int repeat) const = 0; }; @@ -66,48 +64,6 @@ public: return "Boost.JSON"; } - static - std::string - to_string( - json::value const& jv, - std::size_t size_hint) - { - std::string s; - std::size_t len = 0; - s.resize(size_hint); - json::serializer p(jv); - for(;;) - { - auto const used = p.next( - &s[len], s.size() - len); - len += used; - s.resize(len); - if(p.is_done()) - break; - s.resize((len * 3) / 2); - } - return s; - } - - void - work(string_view s) const override - { - std::string s2; - { - json::parser p; - boost::system::error_code ec; - p.write(s.data(), s.size(), ec); - s2 = to_string(p.get(), s.size() * 2); - dout << "s2.size() == " << s2.size() << std::endl; - } - { - json::parser p; - boost::system::error_code ec; - p.write(s2.data(), s2.size(), ec); - dout << "ec.message() == " << ec.message() << std::endl; - } - } - void parse( string_view s, @@ -115,9 +71,9 @@ public: { while(repeat--) { - auto sp = json::make_storage< - json::block_storage>(); - json::parse(s, sp); + json::scoped_storage< + json::block_storage> ss; + json::parse(s, ss); } } @@ -142,25 +98,6 @@ struct rapidjson_impl : public any_impl return "rapidjson"; } - void - work(string_view s) const override - { - rapidjson::StringBuffer s2; - { - rapidjson::Document d; - d.Parse(s.data(), s.size()); - s2.Clear(); - rapidjson::Writer< - rapidjson::StringBuffer> wr(s2); - d.Accept(wr); - dout << "s2.GetSize() == " << s2.GetSize() << std::endl; - } - { - rapidjson::Document d; - d.Parse(s2.GetString(), s2.GetSize()); - } - } - void parse(string_view s, int repeat) const override { @@ -197,13 +134,6 @@ struct nlohmann_impl : public any_impl return "nlohmann"; } - void - work(string_view s) const override - { - auto jv = nlohmann::json::parse( - s.begin(), s.end()); - } - void parse(string_view s, int repeat) const override { @@ -220,247 +150,13 @@ struct nlohmann_impl : public any_impl //---------------------------------------------------------- -class factory +struct file_item { - std::string s_; - std::mt19937 g_; - std::string lorem_; - int depth_; - int indent_ = 4; - int max_depth_ = 6; - - std::size_t - rand(std::size_t n) - { - return static_cast( - std::uniform_int_distribution< - std::size_t>{0, n-1}(g_)); - } - -public: - factory() - : lorem_( - // 40 characters - "Lorem ipsum dolor sit amet, consectetur " // 1 - "adipiscing elit, sed do eiusmod tempor i" // 2 - "ncididunt ut labore et dolore magna aliq" // 3 - "ua. Ut enim ad minim veniam, quis nostru" // 4 - "d exercitation ullamco laboris nisi ut a" // 5 - "liquip ex ea commodo consequat. Duis aut" // 6 - "e irure dolor in reprehenderit in volupt" // 7 - "ate velit esse cillum dolore eu fugiat n" // 8 - "ulla pariatur. Excepteur sint occaecat c" // 9 - "upidatat non proident, sunt in culpa qui" // 10 - " officia deserunt mollit anim id est lab" // 11 - "orum." - ) - { - } - - std::string const& - key(std::string& s) noexcept - { - s.clear(); - s.reserve(20); - auto const append = - [this, &s]() - { - s.push_back( - "0123456789" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ - this->rand(62)]); - }; - append(); - append(); - for(;;) - { - append(); - if(! rand(5) || s.size() >= s.max_size()) - return s; - } - } - - string_view - string() noexcept - { - return { - lorem_.data(), - 1 + rand(lorem_.size()) }; - //1 + rand(20) }; - } - - std::size_t - integer() noexcept - { - return rand(std::numeric_limits< - std::size_t>::max()); - } - - //--- - -private: - void - append_key() noexcept - { - auto const append = - [this]() - { - s_.push_back( - "0123456789" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ - rand(62)]); - }; - s_.push_back('"'); - append(); - append(); - append(); - append(); - append(); - for(;;) - { - append(); - if(! rand(5)) - break; - } - s_.append("\" : ", 4); - } - - void - append_object() - { - s_.append("{\n", 2); - ++depth_; - - s_.append(depth_ * indent_, ' '); - append_key(); - append_value(); - while(rand(40)) - { - s_.append(",\n", 2); - s_.append(depth_ * indent_, ' '); - append_key(); - append_value(); - } - s_.push_back('\n'); - - --depth_; - s_.append(depth_ * indent_, ' '); - s_.push_back('}'); - } - - void - append_array() - { - s_.append("[\n", 2); - ++depth_; - - s_.append(depth_ * indent_, ' '); - append_value(); - while(rand(20)) - { - s_.append(",\n", 2); - s_.append(depth_ * indent_, ' '); - append_value(); - } - s_.push_back('\n'); - - --depth_; - s_.append(depth_ * indent_, ' '); - s_.push_back(']'); - } - - void - append_string() - { - auto const v = string(); - s_.reserve( - s_.size() + 1 + v.size() + 1); - s_.push_back('\"'); - s_.append(v.data(), v.size()); - s_.push_back('\"'); - } - - void - append_integer() - { - auto const ns = std::to_string( - rand(std::numeric_limits::max())); - s_.append(ns.c_str(), ns.size()); - } - - void - append_boolean() - { - if(rand(2)) - s_.append("true", 4); - else - s_.append("false", 5); - } - - void - append_null() - { - s_.append("null", 4); - } - - void - append_value() - { - switch(rand(depth_ < max_depth_ ? 6 : 4)) - { - case 5: return append_object(); - case 4: return append_array(); - case 3: return append_string(); - case 2: return append_integer(); - case 1: return append_boolean(); - case 0: return append_null(); - } - } - -public: - void - max_depth(int n) - { - max_depth_ = n; - } - - string_view - make_document() - { - s_.clear(); - depth_ = 0; - append_array(); - return s_; - } + string_view name; + std::string text; }; -//---------------------------------------------------------- - -// parse random documents -void -benchParse( - string_view doc, - any_impl& impl, - int repeat = 1) -{ - using clock_type = std::chrono::steady_clock; - auto const when = clock_type::now(); - dout << impl.name(); - while(repeat--) - impl.work(doc); - auto const elapsed = - std::chrono::duration_cast< - std::chrono::milliseconds>( - clock_type::now() - when); - dout << - " parse " << - doc.size() << " bytes" - " in " << elapsed.count() << "ms" << - "\n"; - dout.flush(); -} +using file_list = std::vector; std::string load_file(char const* path) @@ -476,14 +172,6 @@ load_file(char const* path) return s; } -struct file_item -{ - string_view name; - std::string text; -}; - -using file_list = std::vector; - void benchParse( file_list const& vs, @@ -499,13 +187,14 @@ benchParse( std::endl; for(unsigned j = 0; j < vi.size(); ++j) { - for(unsigned k = 0; k < 3; ++k) + for(unsigned k = 0; k < 6; ++k) { auto const when = clock_type::now(); vi[j]->parse(vs[i].text, 250); auto const ms = std::chrono::duration_cast< std::chrono::milliseconds>( clock_type::now() - when).count(); + if(k > 2) dout << " " << vi[j]->name() << ": " << std::to_string(ms) << "ms" << std::endl; @@ -566,7 +255,7 @@ main( //vi.emplace_back(new nlohmann_impl); benchParse(vs, vi); - benchSerialize(vs, vi); + //benchSerialize(vs, vi); return 0; } diff --git a/include/boost/json.hpp b/include/boost/json.hpp index 8df3ca23..6e0936f3 100644 --- a/include/boost/json.hpp +++ b/include/boost/json.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/include/boost/json/block_storage.hpp b/include/boost/json/block_storage.hpp index 901595d4..a4439fc9 100644 --- a/include/boost/json/block_storage.hpp +++ b/include/boost/json/block_storage.hpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include @@ -22,30 +22,45 @@ namespace json { /** A storage which uses a multiple fixed size blocks */ -class block_storage : public storage +class block_storage final + : public storage { struct block { std::size_t const size; - std::size_t used; + std::uintptr_t top; block* next; - char* - begin() noexcept + block( + std::size_t size_, + block* next_) + : size(size_) + , top(reinterpret_cast< + std::uintptr_t>(this+1)) + , next(next_) { - return reinterpret_cast< - char*>(this+1); } - char* - end() noexcept + void* + alloc( + std::size_t n, + std::size_t align) noexcept { - return begin() + size; + // must be power of 2 + BOOST_JSON_ASSERT( + (align & (align - 1)) == 0); + auto i = top; + if((i & (align - 1)) != 0) + i = (i | (align - 1)) + 1; + if(i + n > (reinterpret_cast< + std::uintptr_t>(this+1) + size)) + return nullptr; + top = i + n; + return reinterpret_cast(i); } }; std::size_t const block_size_; - std::size_t refs_ = 0; block* head_ = nullptr; public: @@ -64,7 +79,7 @@ public: explicit block_storage( - std::size_t block_size = 256 * 1024) + std::size_t block_size = 64 * 1024) : block_size_(block_size) { } @@ -78,10 +93,8 @@ private: size + sizeof(block) - 1) / sizeof(block); auto& b = *::new( - a.allocate(n + 1)) block{ - n * sizeof(block), - 0, - head_}; + a.allocate(n + 1)) block( + n * sizeof(block), head_); head_ = &b; return b; } @@ -91,35 +104,18 @@ private: std::size_t n, std::size_t align) override { - (void)align; - // must be power of 2 - BOOST_JSON_ASSERT( - (align & (align - 1)) == 0); - // cannot exceed max alignment - BOOST_JSON_ASSERT( - align <= sizeof(std::max_align_t)); - auto const needed = - [&] - { - if(n < block_size_) - return block_size_; - return n + sizeof(block); - }; if(head_) { - auto const avail = - head_->size - head_->used; - if(avail < n) - alloc_block(needed()); + auto p = head_->alloc(n, align); + if(p) + return p; } + if(n > block_size_ - 2 * align) + alloc_block(n + 2 * align); else - { - alloc_block(needed()); - } - ++refs_; - auto p = - head_->begin() + head_->used; - head_->used += n; + alloc_block(block_size_); + auto p = head_->alloc(n, align); + BOOST_JSON_ASSERT(p); return p; } @@ -129,9 +125,6 @@ private: std::size_t, std::size_t) noexcept override { - if(--refs_ > 0) - return; - //clear(); } }; diff --git a/include/boost/json/detail/config.hpp b/include/boost/json/detail/config.hpp index 261686b9..9228a40f 100644 --- a/include/boost/json/detail/config.hpp +++ b/include/boost/json/detail/config.hpp @@ -45,7 +45,7 @@ #endif #ifdef _MSC_VER -#define BOOST_JSON_VALUE_IS_TRIVIAL +//#define BOOST_JSON_VALUE_IS_TRIVIAL #endif #ifndef BOOST_NO_EXCEPTIONS diff --git a/include/boost/json/fixed_storage.hpp b/include/boost/json/fixed_storage.hpp deleted file mode 100644 index 45a04296..00000000 --- a/include/boost/json/fixed_storage.hpp +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/vinniefalco/json -// - -#ifndef BOOST_JSON_FIXED_STORAGE_HPP -#define BOOST_JSON_FIXED_STORAGE_HPP - -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace json { - -/** A storage which uses a single fixed size allocation -*/ -class fixed_storage : public storage -{ - std::size_t const size_; - char* const base_; - std::size_t used_ = 0; - std::size_t refs_ = 0; - -public: - ~fixed_storage() - { - std::allocator< - char>{}.deallocate(base_, size_); - } - - explicit - fixed_storage( - std::size_t bytes) - : size_( - [bytes] - { - auto const align = - sizeof(std::max_align_t) - 1; - if(bytes & align) - return bytes | align; - return bytes; - }()) - , base_(std::allocator< - char>{}.allocate(size_)) - { - } - -protected: - void* - do_allocate( - std::size_t n, - std::size_t align) override - { - (void)align; - // must be power of 2 - BOOST_JSON_ASSERT( - (align & (align - 1)) == 0); - auto offset = used_; - if(offset & (align - 1)) - { - offset |= (align - 1); - ++offset; - } - if( offset > size_ || - n > size_ - offset) - BOOST_JSON_THROW(std::bad_alloc()); - ++refs_; - used_ = offset + n; - return base_ + offset; - } - - void - do_deallocate( - void*, - std::size_t, - std::size_t) noexcept override - { - if(--refs_ > 0) - return; - used_ = 0; - } -}; - -} // json -} // boost - -#endif diff --git a/include/boost/json/impl/array.ipp b/include/boost/json/impl/array.ipp index a98f95e6..1aa9ae43 100644 --- a/include/boost/json/impl/array.ipp +++ b/include/boost/json/impl/array.ipp @@ -194,7 +194,8 @@ undo_insert( array:: ~array() { - release_storage(); + if(sp_ && ! sp_->is_scoped()) + release_storage(); } //---------------------------------------------------------- diff --git a/include/boost/json/impl/object.ipp b/include/boost/json/impl/object.ipp index 43df76dc..c7484fea 100644 --- a/include/boost/json/impl/object.ipp +++ b/include/boost/json/impl/object.ipp @@ -260,7 +260,8 @@ const_iterator( object:: ~object() { - release_storage(); + if(sp_ && ! sp_->is_scoped()) + release_storage(); } object:: diff --git a/include/boost/json/impl/parser.ipp b/include/boost/json/impl/parser.ipp index a691c69b..db9d8fc1 100644 --- a/include/boost/json/impl/parser.ipp +++ b/include/boost/json/impl/parser.ipp @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -20,32 +19,6 @@ namespace boost { namespace json { -template -void -parser:: -assign(T&& t) -{ - auto& jv = *stack_.front(); - BOOST_JSON_ASSERT(! jv.is_object()); - if(obj_) - { - BOOST_JSON_ASSERT(jv.is_null()); - jv = std::forward(t); - stack_.pop(); - } - else if(stack_.front()->is_array()) - { - BOOST_JSON_ASSERT(s_.empty()); - jv.if_array()->emplace_back( - std::forward(t)); - } - else - { - BOOST_JSON_ASSERT(jv.is_null()); - jv = std::forward(t); - } -} - parser:: ~parser() { @@ -55,8 +28,6 @@ parser:: parser:: parser() - : jv_(make_storage< - block_storage>()) { } @@ -353,7 +324,24 @@ void parser:: on_bool(bool b, error_code&) { - assign(b); + auto& jv = *stack_.front(); + BOOST_JSON_ASSERT(! jv.is_object()); + if(obj_) + { + BOOST_JSON_ASSERT(jv.is_null()); + jv.emplace_bool() = b; + stack_.pop(); + } + else if(stack_.front()->is_array()) + { + BOOST_JSON_ASSERT(s_.empty()); + jv.if_array()->emplace_back(b); + } + else + { + BOOST_JSON_ASSERT(jv.is_null()); + jv.emplace_bool() = b; + } } void diff --git a/include/boost/json/impl/storage.ipp b/include/boost/json/impl/storage.ipp index 9c3699b3..d031cda7 100644 --- a/include/boost/json/impl/storage.ipp +++ b/include/boost/json/impl/storage.ipp @@ -30,6 +30,8 @@ void storage:: release() noexcept { + if(scoped_) + return; if(--refs_ > 0) return; delete this; diff --git a/include/boost/json/parser.hpp b/include/boost/json/parser.hpp index 406f575c..e43caa7a 100644 --- a/include/boost/json/parser.hpp +++ b/include/boost/json/parser.hpp @@ -22,7 +22,7 @@ namespace boost { namespace json { -class parser +class parser final : public basic_parser { using size_type = unsigned; @@ -76,10 +76,6 @@ public: release() noexcept; private: - template - void - assign(T&& t); - BOOST_JSON_DECL void reset(); diff --git a/include/boost/json/storage.hpp b/include/boost/json/storage.hpp index 57c93c79..27b7e668 100644 --- a/include/boost/json/storage.hpp +++ b/include/boost/json/storage.hpp @@ -12,10 +12,10 @@ #include #include -#include #include #include #include +#include namespace boost { namespace json { @@ -25,7 +25,9 @@ namespace json { class storage { std::atomic refs_; + //std::size_t refs_; unsigned long long id_ = 0; + bool scoped_ = false; BOOST_JSON_DECL void @@ -38,18 +40,17 @@ class storage template friend class basic_storage_ptr; -public: - static std::size_t constexpr max_align = - sizeof(max_align_t); + template + friend class scoped_storage; +public: virtual ~storage() = default; void* allocate( std::size_t n, - std::size_t align = - max_align) + std::size_t align) { return do_allocate(n, align); } @@ -58,12 +59,17 @@ public: deallocate( void* p, std::size_t n, - std::size_t align = - max_align) + std::size_t align) noexcept { return do_deallocate(p, n, align); } + bool + is_scoped() const noexcept + { + return scoped_; + } + bool is_equal( storage const& other) const noexcept @@ -125,7 +131,8 @@ class basic_storage_ptr template friend class basic_storage_ptr; - using count = std::atomic; + template + friend class scoped_storage; T* t_ = nullptr; @@ -136,7 +143,7 @@ class basic_storage_ptr } public: - /** Construct an null storage pointer + /** Construct a null storage pointer This constructs a null storage pointer. @@ -507,6 +514,31 @@ using storage_ptr = basic_storage_ptr; //---------------------------------------------------------- +template +class scoped_storage +{ + T t_; + + BOOST_JSON_STATIC_ASSERT( + std::is_base_of::value); + +public: + template + explicit + scoped_storage(Args&&... args) + : t_(std::forward(args)...) + { + t_.scoped_ = true; + } + + operator storage_ptr() noexcept + { + return storage_ptr(&t_); + } +}; + +//---------------------------------------------------------- + /** Return a pointer to the default storage This function returns the default storage, which is diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bfd0d60a..c5d85d60 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,7 +36,6 @@ add_executable (json-tests basic_parser.cpp block_storage.cpp error.cpp - fixed_storage.cpp ieee_decimal.cpp json.cpp kind.cpp diff --git a/test/Jamfile b/test/Jamfile index fe16a161..4364d6b5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -32,7 +32,6 @@ local SOURCES = basic_parser.cpp block_storage.cpp error.cpp - fixed_storage.cpp ieee_decimal.cpp json.cpp kind.cpp diff --git a/test/fixed_storage.cpp b/test/fixed_storage.cpp deleted file mode 100644 index 0c043b73..00000000 --- a/test/fixed_storage.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/vinniefalco/json -// - -// Test that header file is self-contained. -#include - -#include - -namespace boost { -namespace json { - -class fixed_storage_test : public beast::unit_test::suite -{ -public: - void run() override - { - make_storage(65536); - pass(); - } -}; - -BEAST_DEFINE_TESTSUITE(boost,json,fixed_storage); - -} // json -} // boost