From c8e7874534e05088a7c655516bd2d8732ef15fe7 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Mon, 24 Nov 2008 23:15:55 +0000 Subject: [PATCH] Use aligned storage for the value. [SVN r49927] --- doc/changes.qbk | 7 + include/boost/unordered/detail/hash_table.hpp | 2 + .../unordered/detail/hash_table_impl.hpp | 182 ++++-------------- 3 files changed, 51 insertions(+), 140 deletions(-) diff --git a/doc/changes.qbk b/doc/changes.qbk index 09fd4c4d..2677caa4 100644 --- a/doc/changes.qbk +++ b/doc/changes.qbk @@ -52,5 +52,12 @@ First official release. * [@https://svn.boost.org/trac/boost/ticket/1710 Ticket 1710]: Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé Brönnimann. +* Use + [@../../libs/type_traits/doc/html/boost_typetraits/category/alignment.html + aligned storage] to store the types. This changes the way the allocator is + used to construct nodes. It used to construct the node with two calls to + the allocator's `construct` method - once for the pointers and once for the + value. It now constructs the node with a single call to construct and + then constructs the value using in place construction. [endsect] diff --git a/include/boost/unordered/detail/hash_table.hpp b/include/boost/unordered/detail/hash_table.hpp index b3826ab1..264f2ea5 100644 --- a/include/boost/unordered/detail/hash_table.hpp +++ b/include/boost/unordered/detail/hash_table.hpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/include/boost/unordered/detail/hash_table_impl.hpp b/include/boost/unordered/detail/hash_table_impl.hpp index 89302072..20e4e69c 100644 --- a/include/boost/unordered/detail/hash_table_impl.hpp +++ b/include/boost/unordered/detail/hash_table_impl.hpp @@ -34,7 +34,6 @@ namespace boost { public: typedef BOOST_UNORDERED_TABLE_DATA data; - struct node_base; struct node; struct bucket; typedef std::size_t size_type; @@ -45,9 +44,6 @@ namespace boost { typedef BOOST_DEDUCED_TYPENAME boost::unordered_detail::rebind_wrap::type node_allocator; - typedef BOOST_DEDUCED_TYPENAME - boost::unordered_detail::rebind_wrap::type - node_base_allocator; typedef BOOST_DEDUCED_TYPENAME boost::unordered_detail::rebind_wrap::type bucket_allocator; @@ -88,39 +84,36 @@ namespace boost { } }; + // Value Base + + struct value_base { + typename boost::aligned_storage< + sizeof(value_type), + boost::alignment_of::value>::type data_; + + void* address() { return this; } + }; + // Hash Node // // all no throw - struct node_base : bucket - { + struct node : value_base, bucket { #if BOOST_UNORDERED_EQUIVALENT_KEYS public: - node_base() : group_prev_() + node() : group_prev_() { BOOST_UNORDERED_MSVC_RESET_PTR(group_prev_); } link_ptr group_prev_; #endif + + value_type& value() { + return *static_cast(this->address()); + } }; - struct node : node_base - { - public: -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) - template - node(Args&&... args) - : node_base(), value_(std::forward(args)...) {} -#else - node(value_type const& v) : node_base(), value_(v) {} -#endif - - value_type value_; - }; - -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) - // allocators // // Stores all the allocators that we're going to need. @@ -136,7 +129,9 @@ namespace boost { void destroy(link_ptr ptr) { - node_ptr n(node_alloc_.address(*static_cast(&*ptr))); + node* raw_ptr = static_cast(&*ptr); + raw_ptr->value().~value_type(); + node_ptr n(node_alloc_.address(*raw_ptr)); node_alloc_.destroy(n); node_alloc_.deallocate(n, 1); } @@ -163,146 +158,62 @@ namespace boost { node_ptr node_; bool node_constructed_; + bool value_constructed_; public: node_constructor(allocators& a) : allocators_(a), - node_(), node_constructed_(false) + node_(), node_constructed_(false), value_constructed_(false) { } ~node_constructor() { if (node_) { + if (value_constructed_) { + node_->value().~value_type(); + } + if (node_constructed_) allocators_.node_alloc_.destroy(node_); allocators_.node_alloc_.deallocate(node_, 1); } } +#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) template void construct(Args&&... args) { BOOST_ASSERT(!node_); node_constructed_ = false; + value_constructed_ = false; node_ = allocators_.node_alloc_.allocate(1); - allocators_.node_alloc_.construct(node_, std::forward(args)...); + + allocators_.node_alloc_.construct(node_, node()); node_constructed_ = true; - } - node_ptr get() const - { - BOOST_ASSERT(node_); - return node_; + new(node_->address()) value_type(std::forward(args)...); + value_constructed_ = true; } - - // no throw - link_ptr release() - { - node_ptr p = node_; - unordered_detail::reset(node_); - return link_ptr(allocators_.bucket_alloc_.address(*p)); - } - - private: - node_constructor(node_constructor const&); - node_constructor& operator=(node_constructor const&); - }; #else - - // allocators - // - // Stores all the allocators that we're going to need. - - struct allocators - { - node_allocator node_alloc_; - bucket_allocator bucket_alloc_; - value_allocator value_alloc_; - node_base_allocator node_base_alloc_; - - allocators(value_allocator const& a) - : node_alloc_(a), bucket_alloc_(a), - value_alloc_(a), node_base_alloc_(a) - {} - - void destroy(link_ptr ptr) - { - node_ptr n(node_alloc_.address(*static_cast(&*ptr))); - value_alloc_.destroy(value_alloc_.address(n->value_)); - node_base_alloc_.destroy(node_base_alloc_.address(*n)); - node_alloc_.deallocate(n, 1); - } - - void swap(allocators& x) - { - boost::swap(node_alloc_, x.node_alloc_); - boost::swap(bucket_alloc_, x.bucket_alloc_); - boost::swap(value_alloc_, x.value_alloc_); - boost::swap(node_base_alloc_, x.node_base_alloc_); - } - - bool operator==(allocators const& x) - { - return value_alloc_ == x.value_alloc_; - } - }; - - // node_constructor - // - // Used to construct nodes in an exception safe manner. - - class node_constructor - { - allocators& allocators_; - - node_ptr node_; - bool value_constructed_; - bool node_base_constructed_; - - public: - - node_constructor(allocators& a) - : allocators_(a), - node_(), value_constructed_(false), node_base_constructed_(false) - { - BOOST_UNORDERED_MSVC_RESET_PTR(node_); - } - - ~node_constructor() - { - if (node_) { - if (value_constructed_) - allocators_.value_alloc_.destroy( - allocators_.value_alloc_.address(node_->value_)); - if (node_base_constructed_) - allocators_.node_base_alloc_.destroy( - allocators_.node_base_alloc_.address(*node_)); - - allocators_.node_alloc_.deallocate(node_, 1); - } - } - template void construct(V const& v) { BOOST_ASSERT(!node_); + node_constructed_ = false; value_constructed_ = false; - node_base_constructed_ = false; node_ = allocators_.node_alloc_.allocate(1); - allocators_.node_base_alloc_.construct( - allocators_.node_base_alloc_.address(*node_), - node_base()); - node_base_constructed_ = true; + allocators_.node_alloc_.construct(node_, node()); + node_constructed_ = true; - allocators_.value_alloc_.construct( - allocators_.value_alloc_.address(node_->value_), v); + new(node_->address()) value_type(v); value_constructed_ = true; } +#endif node_ptr get() const { @@ -322,7 +233,6 @@ namespace boost { node_constructor(node_constructor const&); node_constructor& operator=(node_constructor const&); }; -#endif // Methods for navigating groups of elements with equal keys. @@ -351,8 +261,7 @@ namespace boost { // pre: Must be pointing to a node static inline reference get_value(link_ptr p) { - BOOST_ASSERT(p); - return static_cast(&*p)->value_; + return get_node(p).value(); } class iterator_base @@ -1357,17 +1266,10 @@ namespace boost { // accessors // no throw -#if defined(BOOST_HAS_RVALUE_REFS) && defined(BOOST_HAS_VARIADIC_TMPL) node_allocator get_allocator() const { return data_.allocators_.node_alloc_; } -#else - value_allocator get_allocator() const - { - return data_.allocators_.value_alloc_; - } -#endif // no throw hasher const& hash_function() const @@ -1728,7 +1630,7 @@ namespace boost { iterator_base insert_impl(node_constructor& a) { - key_type const& k = extract_key(a.get()->value_); + key_type const& k = extract_key(a.get()->value()); size_type hash_value = hash_function()(k); bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); link_ptr position = find_iterator(bucket, k); @@ -1750,7 +1652,7 @@ namespace boost { iterator_base insert_hint_impl(iterator_base const& it, node_constructor& a) { // equal can throw, but with no effects - if (it == data_.end() || !equal(extract_key(a.get()->value_), *it)) { + if (it == data_.end() || !equal(extract_key(a.get()->value()), *it)) { // Use the standard insert if the iterator doesn't point // to a matching key. return insert_impl(a); @@ -1766,7 +1668,7 @@ namespace boost { // reserve has basic exception safety if the hash function // throws, strong otherwise. bucket_ptr base = reserve_for_insert(size() + 1) ? - get_bucket(extract_key(a.get()->value_)) : it.bucket_; + get_bucket(extract_key(a.get()->value())) : it.bucket_; // Nothing after this point can throw @@ -1796,7 +1698,7 @@ namespace boost { for (; i != j; ++i) { a.construct(*i); - key_type const& k = extract_key(a.get()->value_); + key_type const& k = extract_key(a.get()->value()); bucket_ptr bucket = get_bucket(k); link_ptr position = find_iterator(bucket, k); @@ -1981,7 +1883,7 @@ namespace boost { a.construct(std::forward(args)...); // No side effects in this initial code - key_type const& k = extract_key(a.get()->value_); + key_type const& k = extract_key(a.get()->value()); size_type hash_value = hash_function()(k); bucket_ptr bucket = data_.bucket_ptr_from_hash(hash_value); link_ptr pos = find_iterator(bucket, k);