Add minimal prototype of concurrent_flat_map

This commit is contained in:
Christian Mazakas
2023-03-17 14:22:24 -07:00
parent 2386188d69
commit 12935eb695
3 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,128 @@
/* Fast open-addressing concurrent hash table.
*
* Copyright 2023 Christian Mazakas.
* 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
/* Reference:
* https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/type_traits/type_identity.hpp>
#include <functional>
#include <utility>
namespace boost {
namespace unordered {
namespace detail {
template <class Key, class T> struct concurrent_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class T, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class concurrent_flat_map
{
private:
using type_policy = detail::concurrent_map_types<Key,T>;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
public:
using key_type = Key;
using mapped_type = T;
using value_type = typename type_policy::value_type;
using init_type = typename type_policy::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::type_identity<Hash>::type;
using key_equal = typename boost::type_identity<Pred>::type;
using allocator_type = typename boost::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
concurrent_flat_map() : concurrent_flat_map(0) {}
explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
}
bool insert(value_type const& obj)
{
return table_.insert(obj);
}
};
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP

View File

@ -157,3 +157,10 @@ run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORD
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
rule build_cfoa ( name )
{
run cfoa/$(name).cpp : : : $(CPP11) : cfoa_$(name) ;
}
build_cfoa insert_tests ;

163
test/cfoa/insert_tests.cpp Normal file
View File

@ -0,0 +1,163 @@
// Copyright (C) 2023 Christian Mazakas
// 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)
#include "../helpers/generators.hpp"
#include "../helpers/test.hpp"
#include <boost/container_hash/hash.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <atomic>
#include <iostream>
#include <vector>
struct raii
{
static std::atomic_int default_constructor;
static std::atomic_int copy_constructor;
static std::atomic_int move_constructor;
static std::atomic_int destructor;
static std::atomic_int copy_assignment;
static std::atomic_int move_assignment;
int x_ = -1;
raii() { ++default_constructor; }
explicit raii(int const x) : x_{x} { ++default_constructor; }
raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; }
raii(raii&& rhs) noexcept : x_{rhs.x_}
{
rhs.x_ = -1;
++move_constructor;
}
~raii() { ++destructor; }
raii& operator=(raii const& rhs)
{
++copy_assignment;
if (this != &rhs) {
x_ = rhs.x_;
}
return *this;
}
raii& operator=(raii&& rhs) noexcept
{
++move_assignment;
if (this != &rhs) {
x_ = rhs.x_;
rhs.x_ = -1;
}
return *this;
}
static void reset_counts()
{
default_constructor = 0;
copy_constructor = 0;
move_constructor = 0;
destructor = 0;
copy_assignment = 0;
move_assignment = 0;
}
friend std::ostream& operator<<(std::ostream& os, raii const& rhs)
{
os << "{ x_: " << rhs.x_ << "}";
return os;
}
friend std::ostream& operator<<(
std::ostream& os, std::pair<raii const, raii> const& rhs)
{
os << "pair<" << rhs.first << ", " << rhs.second << ">";
return os;
}
friend bool operator==(raii const& lhs, raii const& rhs)
{
return lhs.x_ == rhs.x_;
}
friend bool operator!=(raii const& lhs, raii const& rhs)
{
return !(lhs == rhs);
}
};
std::atomic_int raii::default_constructor{0};
std::atomic_int raii::copy_constructor{0};
std::atomic_int raii::move_constructor{0};
std::atomic_int raii::destructor{0};
std::atomic_int raii::copy_assignment{0};
std::atomic_int raii::move_assignment{0};
std::size_t hash_value(raii const& r) noexcept
{
boost::hash<int> hasher;
return hasher(r.x_);
}
std::vector<std::pair<raii const, raii> > make_random_values(
std::size_t count, test::random_generator rg)
{
std::vector<std::pair<raii const, raii> > v;
v.reserve(count);
for (std::size_t i = 0; i < count; ++i) {
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
v.emplace_back(raii{a}, raii{b});
}
raii::reset_counts();
return v;
}
namespace {
test::seed_t initialize_seed(78937);
template <class X> void insert(X*, test::random_generator generator)
{
raii::reset_counts();
auto values = make_random_values(1024, generator);
BOOST_TEST_GT(values.size(), 0);
{
X x;
for (auto const& r : values) {
bool b = x.insert(r);
(void)b;
}
}
BOOST_TEST_GE(raii::default_constructor, 0);
BOOST_TEST_GE(raii::copy_constructor, 0);
BOOST_TEST_GE(raii::move_constructor, 0);
BOOST_TEST_GT(raii::destructor, 0);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0);
BOOST_TEST_EQ(raii::move_assignment, 0);
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
} // namespace
using test::default_generator;
using test::generate_collisions;
using test::limited_range;
UNORDERED_TEST(
insert, ((map))((default_generator)(generate_collisions)(limited_range)))
RUN_TESTS()