scoped storage

This commit is contained in:
Vinnie Falco 2019-10-27 18:20:43 -07:00
parent 1754d9cadd
commit 96ecab8cc8
14 changed files with 117 additions and 544 deletions

View File

@ -7,6 +7,8 @@
// Official repository: https://github.com/vinniefalco/json // Official repository: https://github.com/vinniefalco/json
// //
//#define RAPIDJSON_SSE42
#include "lib/nlohmann/single_include/nlohmann/json.hpp" #include "lib/nlohmann/single_include/nlohmann/json.hpp"
#include "lib/rapidjson/include/rapidjson/rapidjson.h" #include "lib/rapidjson/include/rapidjson/rapidjson.h"
@ -19,7 +21,6 @@
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <random>
#include <cstdio> #include <cstdio>
#include <vector> #include <vector>
@ -47,10 +48,7 @@ class any_impl
{ {
public: public:
virtual ~any_impl() = default; virtual ~any_impl() = default;
virtual string_view name() const noexcept = 0; 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 parse(string_view s, int repeat) const = 0;
virtual void serialize(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"; 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 void
parse( parse(
string_view s, string_view s,
@ -115,9 +71,9 @@ public:
{ {
while(repeat--) while(repeat--)
{ {
auto sp = json::make_storage< json::scoped_storage<
json::block_storage>(); json::block_storage> ss;
json::parse(s, sp); json::parse(s, ss);
} }
} }
@ -142,25 +98,6 @@ struct rapidjson_impl : public any_impl
return "rapidjson"; 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 void
parse(string_view s, int repeat) const override parse(string_view s, int repeat) const override
{ {
@ -197,13 +134,6 @@ struct nlohmann_impl : public any_impl
return "nlohmann"; return "nlohmann";
} }
void
work(string_view s) const override
{
auto jv = nlohmann::json::parse(
s.begin(), s.end());
}
void void
parse(string_view s, int repeat) const override 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_; string_view name;
std::mt19937 g_; std::string text;
std::string lorem_;
int depth_;
int indent_ = 4;
int max_depth_ = 6;
std::size_t
rand(std::size_t n)
{
return static_cast<std::size_t>(
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<int>::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_;
}
}; };
//---------------------------------------------------------- using file_list = std::vector<file_item>;
// 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();
}
std::string std::string
load_file(char const* path) load_file(char const* path)
@ -476,14 +172,6 @@ load_file(char const* path)
return s; return s;
} }
struct file_item
{
string_view name;
std::string text;
};
using file_list = std::vector<file_item>;
void void
benchParse( benchParse(
file_list const& vs, file_list const& vs,
@ -499,13 +187,14 @@ benchParse(
std::endl; std::endl;
for(unsigned j = 0; j < vi.size(); ++j) 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(); auto const when = clock_type::now();
vi[j]->parse(vs[i].text, 250); vi[j]->parse(vs[i].text, 250);
auto const ms = std::chrono::duration_cast< auto const ms = std::chrono::duration_cast<
std::chrono::milliseconds>( std::chrono::milliseconds>(
clock_type::now() - when).count(); clock_type::now() - when).count();
if(k > 2)
dout << " " << vi[j]->name() << ": " << dout << " " << vi[j]->name() << ": " <<
std::to_string(ms) << "ms" << std::to_string(ms) << "ms" <<
std::endl; std::endl;
@ -566,7 +255,7 @@ main(
//vi.emplace_back(new nlohmann_impl); //vi.emplace_back(new nlohmann_impl);
benchParse(vs, vi); benchParse(vs, vi);
benchSerialize(vs, vi); //benchSerialize(vs, vi);
return 0; return 0;
} }

View File

@ -16,7 +16,6 @@
#include <boost/json/basic_parser.hpp> #include <boost/json/basic_parser.hpp>
#include <boost/json/block_storage.hpp> #include <boost/json/block_storage.hpp>
#include <boost/json/error.hpp> #include <boost/json/error.hpp>
#include <boost/json/fixed_storage.hpp>
#include <boost/json/ieee_decimal.hpp> #include <boost/json/ieee_decimal.hpp>
#include <boost/json/kind.hpp> #include <boost/json/kind.hpp>
#include <boost/json/number.hpp> #include <boost/json/number.hpp>

View File

@ -13,7 +13,7 @@
#include <boost/json/detail/config.hpp> #include <boost/json/detail/config.hpp>
#include <boost/json/detail/assert.hpp> #include <boost/json/detail/assert.hpp>
#include <boost/json/storage.hpp> #include <boost/json/storage.hpp>
#include <cstddef> #include <cstdint>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@ -22,30 +22,45 @@ namespace json {
/** A storage which uses a multiple fixed size blocks /** A storage which uses a multiple fixed size blocks
*/ */
class block_storage : public storage class block_storage final
: public storage
{ {
struct block struct block
{ {
std::size_t const size; std::size_t const size;
std::size_t used; std::uintptr_t top;
block* next; block* next;
char* block(
begin() noexcept 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* void*
end() noexcept 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<void*>(i);
} }
}; };
std::size_t const block_size_; std::size_t const block_size_;
std::size_t refs_ = 0;
block* head_ = nullptr; block* head_ = nullptr;
public: public:
@ -64,7 +79,7 @@ public:
explicit explicit
block_storage( block_storage(
std::size_t block_size = 256 * 1024) std::size_t block_size = 64 * 1024)
: block_size_(block_size) : block_size_(block_size)
{ {
} }
@ -78,10 +93,8 @@ private:
size + sizeof(block) - 1) / size + sizeof(block) - 1) /
sizeof(block); sizeof(block);
auto& b = *::new( auto& b = *::new(
a.allocate(n + 1)) block{ a.allocate(n + 1)) block(
n * sizeof(block), n * sizeof(block), head_);
0,
head_};
head_ = &b; head_ = &b;
return b; return b;
} }
@ -91,35 +104,18 @@ private:
std::size_t n, std::size_t n,
std::size_t align) override 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_) if(head_)
{ {
auto const avail = auto p = head_->alloc(n, align);
head_->size - head_->used; if(p)
if(avail < n) return p;
alloc_block(needed());
} }
if(n > block_size_ - 2 * align)
alloc_block(n + 2 * align);
else else
{ alloc_block(block_size_);
alloc_block(needed()); auto p = head_->alloc(n, align);
} BOOST_JSON_ASSERT(p);
++refs_;
auto p =
head_->begin() + head_->used;
head_->used += n;
return p; return p;
} }
@ -129,9 +125,6 @@ private:
std::size_t, std::size_t,
std::size_t) noexcept override std::size_t) noexcept override
{ {
if(--refs_ > 0)
return;
//clear();
} }
}; };

View File

@ -45,7 +45,7 @@
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define BOOST_JSON_VALUE_IS_TRIVIAL //#define BOOST_JSON_VALUE_IS_TRIVIAL
#endif #endif
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS

View File

@ -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 <boost/json/detail/config.hpp>
#include <boost/json/detail/assert.hpp>
#include <boost/json/storage.hpp>
#include <cstddef>
#include <memory>
#include <stdexcept>
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

View File

@ -194,6 +194,7 @@ undo_insert(
array:: array::
~array() ~array()
{ {
if(sp_ && ! sp_->is_scoped())
release_storage(); release_storage();
} }

View File

@ -260,6 +260,7 @@ const_iterator(
object:: object::
~object() ~object()
{ {
if(sp_ && ! sp_->is_scoped())
release_storage(); release_storage();
} }

View File

@ -12,7 +12,6 @@
#include <boost/json/parser.hpp> #include <boost/json/parser.hpp>
#include <boost/json/error.hpp> #include <boost/json/error.hpp>
#include <boost/json/block_storage.hpp>
#include <boost/json/detail/assert.hpp> #include <boost/json/detail/assert.hpp>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
@ -20,32 +19,6 @@
namespace boost { namespace boost {
namespace json { namespace json {
template<class T>
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>(t);
stack_.pop();
}
else if(stack_.front()->is_array())
{
BOOST_JSON_ASSERT(s_.empty());
jv.if_array()->emplace_back(
std::forward<T>(t));
}
else
{
BOOST_JSON_ASSERT(jv.is_null());
jv = std::forward<T>(t);
}
}
parser:: parser::
~parser() ~parser()
{ {
@ -55,8 +28,6 @@ parser::
parser:: parser::
parser() parser()
: jv_(make_storage<
block_storage>())
{ {
} }
@ -353,7 +324,24 @@ void
parser:: parser::
on_bool(bool b, error_code&) 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 void

View File

@ -30,6 +30,8 @@ void
storage:: storage::
release() noexcept release() noexcept
{ {
if(scoped_)
return;
if(--refs_ > 0) if(--refs_ > 0)
return; return;
delete this; delete this;

View File

@ -22,7 +22,7 @@
namespace boost { namespace boost {
namespace json { namespace json {
class parser class parser final
: public basic_parser : public basic_parser
{ {
using size_type = unsigned; using size_type = unsigned;
@ -76,10 +76,6 @@ public:
release() noexcept; release() noexcept;
private: private:
template<class T>
void
assign(T&& t);
BOOST_JSON_DECL BOOST_JSON_DECL
void void
reset(); reset();

View File

@ -12,10 +12,10 @@
#include <boost/json/detail/config.hpp> #include <boost/json/detail/config.hpp>
#include <atomic> #include <atomic>
#include <cstddef>
#include <cstdlib> #include <cstdlib>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <utility>
namespace boost { namespace boost {
namespace json { namespace json {
@ -25,7 +25,9 @@ namespace json {
class storage class storage
{ {
std::atomic<std::size_t> refs_; std::atomic<std::size_t> refs_;
//std::size_t refs_;
unsigned long long id_ = 0; unsigned long long id_ = 0;
bool scoped_ = false;
BOOST_JSON_DECL BOOST_JSON_DECL
void void
@ -38,18 +40,17 @@ class storage
template<class T> template<class T>
friend class basic_storage_ptr; friend class basic_storage_ptr;
public: template<class T>
static std::size_t constexpr max_align = friend class scoped_storage;
sizeof(max_align_t);
public:
virtual virtual
~storage() = default; ~storage() = default;
void* void*
allocate( allocate(
std::size_t n, std::size_t n,
std::size_t align = std::size_t align)
max_align)
{ {
return do_allocate(n, align); return do_allocate(n, align);
} }
@ -58,12 +59,17 @@ public:
deallocate( deallocate(
void* p, void* p,
std::size_t n, std::size_t n,
std::size_t align = std::size_t align) noexcept
max_align)
{ {
return do_deallocate(p, n, align); return do_deallocate(p, n, align);
} }
bool
is_scoped() const noexcept
{
return scoped_;
}
bool bool
is_equal( is_equal(
storage const& other) const noexcept storage const& other) const noexcept
@ -125,7 +131,8 @@ class basic_storage_ptr
template<class U> template<class U>
friend class basic_storage_ptr; friend class basic_storage_ptr;
using count = std::atomic<unsigned>; template<class U>
friend class scoped_storage;
T* t_ = nullptr; T* t_ = nullptr;
@ -136,7 +143,7 @@ class basic_storage_ptr
} }
public: public:
/** Construct an null storage pointer /** Construct a null storage pointer
This constructs a null storage pointer. This constructs a null storage pointer.
@ -507,6 +514,31 @@ using storage_ptr = basic_storage_ptr<storage>;
//---------------------------------------------------------- //----------------------------------------------------------
template<class T>
class scoped_storage
{
T t_;
BOOST_JSON_STATIC_ASSERT(
std::is_base_of<storage, T>::value);
public:
template<class... Args>
explicit
scoped_storage(Args&&... args)
: t_(std::forward<Args>(args)...)
{
t_.scoped_ = true;
}
operator storage_ptr() noexcept
{
return storage_ptr(&t_);
}
};
//----------------------------------------------------------
/** Return a pointer to the default storage /** Return a pointer to the default storage
This function returns the default storage, which is This function returns the default storage, which is

View File

@ -36,7 +36,6 @@ add_executable (json-tests
basic_parser.cpp basic_parser.cpp
block_storage.cpp block_storage.cpp
error.cpp error.cpp
fixed_storage.cpp
ieee_decimal.cpp ieee_decimal.cpp
json.cpp json.cpp
kind.cpp kind.cpp

View File

@ -32,7 +32,6 @@ local SOURCES =
basic_parser.cpp basic_parser.cpp
block_storage.cpp block_storage.cpp
error.cpp error.cpp
fixed_storage.cpp
ieee_decimal.cpp ieee_decimal.cpp
json.cpp json.cpp
kind.cpp kind.cpp

View File

@ -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 <boost/json/fixed_storage.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
namespace boost {
namespace json {
class fixed_storage_test : public beast::unit_test::suite
{
public:
void run() override
{
make_storage<fixed_storage>(65536);
pass();
}
};
BEAST_DEFINE_TESTSUITE(boost,json,fixed_storage);
} // json
} // boost