Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small FlatMap optimization with default initialization #2620

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/brpc/builtin/hotspots_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ static DisplayType StringToDisplayType(const std::string& val) {
static std::once_flag flag;
std::call_once(flag, []() {
display_type_map = new butil::CaseIgnoredFlatMap<DisplayType>;
display_type_map->init(10);
(*display_type_map)["dot"] = DisplayType::kDot;
#if defined(OS_LINUX)
(*display_type_map)["flame"] = DisplayType::kFlameGraph;
Expand Down
2 changes: 0 additions & 2 deletions src/brpc/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ friend void policy::ProcessThriftRequest(InputMessageBase*);
UserFieldsMap* request_user_fields() {
if (!_request_user_fields) {
_request_user_fields = new UserFieldsMap;
_request_user_fields->init(29);
}
return _request_user_fields;
}
Expand All @@ -274,7 +273,6 @@ friend void policy::ProcessThriftRequest(InputMessageBase*);
UserFieldsMap* response_user_fields() {
if (!_response_user_fields) {
_response_user_fields = new UserFieldsMap;
_response_user_fields->init(29);
}
return _response_user_fields;
}
Expand Down
3 changes: 1 addition & 2 deletions src/brpc/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ class Extension {

private:
friend class butil::GetLeakySingleton<Extension<T> >;
Extension();
~Extension();
Extension() = default;
butil::CaseIgnoredFlatMap<T*> _instance_map;
butil::Mutex _map_mutex;
};
Expand Down
9 changes: 0 additions & 9 deletions src/brpc/extension_inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,6 @@ Extension<T>* Extension<T>::instance() {
return butil::get_leaky_singleton<Extension<T> >();
}

template <typename T>
Extension<T>::Extension() {
_instance_map.init(29);
}

template <typename T>
Extension<T>::~Extension() {
}

template <typename T>
int Extension<T>::Register(const std::string& name, T* instance) {
if (NULL == instance) {
Expand Down
20 changes: 10 additions & 10 deletions src/brpc/kvmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@ class KVMap {
// Get value of a key(case-sensitive)
// Return pointer to the value, NULL on not found.
const std::string* Get(const char* key) const { return _entries.seek(key); }
const std::string* Get(const std::string& key) const { return _entries.seek(key); }
const std::string* Get(const std::string& key) const {
return _entries.seek(key);
}

// Set value of a key
void Set(const std::string& key, const std::string& value) { GetOrAdd(key) = value; }
void Set(const std::string& key, const char* value) { GetOrAdd(key) = value; }
void Set(const std::string& key, const std::string& value) {
_entries[key] = value;
}
void Set(const std::string& key, const char* value) { _entries[key] = value; }
// Convert other types to string as well
template <typename T>
void Set(const std::string& key, const T& value) { GetOrAdd(key) = std::to_string(value); }
void Set(const std::string& key, const T& value) {
_entries[key] = std::to_string(value);
}

// Remove a key
void Remove(const char* key) { _entries.erase(key); }
Expand All @@ -60,12 +66,6 @@ class KVMap {
size_t Count() const { return _entries.size(); }

private:
std::string& GetOrAdd(const std::string& key) {
if (!_entries.initialized()) {
_entries.init(29);
}
return _entries[key];
}

Map _entries;
};
Expand Down
6 changes: 0 additions & 6 deletions src/brpc/uri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ static void ParseQueries(URI::QueryMap& query_map, const std::string &query) {
}
for (QuerySplitter sp(query.c_str()); sp; ++sp) {
if (!sp.key().empty()) {
if (!query_map.initialized()) {
query_map.init(URI::QUERY_MAP_INITIAL_BUCKET);
}
std::string key(sp.key().data(), sp.key().size());
std::string value(sp.value().data(), sp.value().size());
query_map[key] = value;
Expand Down Expand Up @@ -347,9 +344,6 @@ void URI::PrintWithoutHost(std::ostream& os) const {
}

void URI::InitializeQueryMap() const {
if (!_query_map.initialized()) {
CHECK_EQ(0, _query_map.init(QUERY_MAP_INITIAL_BUCKET));
}
ParseQueries(_query_map, _query);
_query_was_modified = false;
_initialized_query_map = true;
Expand Down
1 change: 0 additions & 1 deletion src/brpc/uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ namespace brpc {
// interpretable as extension
class URI {
public:
static const size_t QUERY_MAP_INITIAL_BUCKET = 16;
typedef butil::FlatMap<std::string, std::string> QueryMap;
typedef QueryMap::const_iterator QueryIterator;

Expand Down
2 changes: 0 additions & 2 deletions src/bthread/butex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@ int butex_wake_n(void* arg, size_t n, bool nosignal) {
return nwakeup;
}
butil::FlatMap<bthread_tag_t, TaskGroup*> nwakeups;
nwakeups.init(FLAGS_task_group_ntags);
// We will exchange with first waiter in the end.
ButexBthreadWaiter* next = static_cast<ButexBthreadWaiter*>(
bthread_waiters.head()->value());
Expand Down Expand Up @@ -440,7 +439,6 @@ int butex_wake_except(void* arg, bthread_t excluded_bthread) {
return nwakeup;
}
butil::FlatMap<bthread_tag_t, TaskGroup*> nwakeups;
nwakeups.init(FLAGS_task_group_ntags);
do {
// pop reversely
ButexBthreadWaiter* w = static_cast<ButexBthreadWaiter*>(bthread_waiters.tail()->value());
Expand Down
115 changes: 93 additions & 22 deletions src/butil/containers/flat_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
#define BUTIL_FLAT_MAP_H

#include <stdint.h>
#include <cstddef>
#include <functional>
#include <iostream> // std::ostream
#include <type_traits> // std::aligned_storage
Expand Down Expand Up @@ -121,6 +122,14 @@ struct BucketInfo {
double average_length;
};

#ifndef BRPC_FLATMAP_DEFAULT_NBUCKET
#ifdef FLAT_MAP_ROUND_BUCKET_BY_USE_NEXT_PRIME
#define BRPC_FLATMAP_DEFAULT_NBUCKET 29U
#else
#define BRPC_FLATMAP_DEFAULT_NBUCKET 16U
#endif
#endif // BRPC_FLATMAP_DEFAULT_NBUCKET

// NOTE: Objects stored in FlatMap MUST be copyable.
template <typename _K, typename _T,
// Compute hash code from key.
Expand Down Expand Up @@ -148,6 +157,9 @@ class FlatMap {
FlatMapIterator<FlatMap, const value_type> >::type const_iterator;
typedef _Hash hasher;
typedef _Equal key_equal;
typedef typename conditional<_Multi, true_type, false_type>::type multi_type;
static constexpr size_t DEFAULT_NBUCKET = BRPC_FLATMAP_DEFAULT_NBUCKET;

struct PositionHint {
size_t nbucket;
size_t offset;
Expand All @@ -158,13 +170,13 @@ class FlatMap {
explicit FlatMap(const hasher& hashfn = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type());
~FlatMap();
FlatMap(const FlatMap& rhs);
~FlatMap();

FlatMap& operator=(const FlatMap& rhs);
void swap(FlatMap & rhs);

// Must be called to initialize this map, otherwise insert/operator[]
// crashes, and seek/erase fails.
// FlatMap will be automatically initialized, so no need to call this function.
// `nbucket' is the initial number of buckets. `load_factor' is the
// maximum value of size()*100/nbucket, if the value is reached, nbucket
// will be doubled and all items stored will be rehashed which is costly.
Expand Down Expand Up @@ -216,7 +228,7 @@ class FlatMap {
// Resize this map. This is optional because resizing will be triggered by
// insert() or operator[] if there're too many items.
// Returns successful or not.
bool resize(size_t nbucket);
bool resize(size_t new_nbucket);

// Iterators
iterator begin();
Expand Down Expand Up @@ -254,7 +266,7 @@ class FlatMap {
void save_iterator(const const_iterator&, PositionHint*) const;
const_iterator restore_iterator(const PositionHint&) const;

// True if init() was successfully called.
// Always returns true.
bool initialized() const { return _buckets != NULL; }

bool empty() const { return _size == 0; }
Expand Down Expand Up @@ -282,14 +294,28 @@ class FlatMap {
const Element& element() const {
return *element_;
}

void swap(Bucket& rhs) {
if (!is_valid() && !rhs.is_valid()) {
return;
} else if (is_valid() && !rhs.is_valid()) {
rhs.element_.Init(std::move(element()));
element().~Element();
} else if (!is_valid() && rhs.is_valid()) {
element_.Init(std::move(rhs.element()));
rhs.element().~Element();
} else {
element().swap(rhs.element());
}
std::swap(next, rhs.next);
}

Bucket *next;

private:
ManualConstructor<Element> element_;
};

allocator_type& get_allocator() { return _pool.get_allocator(); }

private:
template <typename _Map, typename _Element> friend class FlatMapIterator;
template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
Expand All @@ -304,7 +330,8 @@ template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
size_t nbucket;
};

NewBucketsInfo new_buckets_and_thumbnail(size_t size, size_t new_nbucket);
NewBucketsInfo new_buckets_and_thumbnail(
size_t size, size_t new_nbucket, bool ignore_same_nbucket);

// For `_Multi=true'.
// Insert a new default-constructed associated with |key| always.
Expand All @@ -314,6 +341,9 @@ template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
template<bool Multi = _Multi>
typename std::enable_if<Multi, mapped_type&>::type operator[](const key_type& key);

allocator_type& get_allocator() { return _pool.get_allocator(); }
allocator_type get_allocator() const { return _pool.get_allocator(); }

// True if buckets need to be resized before holding `size' elements.
bool is_too_crowded(size_t size) const {
return is_too_crowded(size, _nbucket, _load_factor);
Expand All @@ -322,8 +352,21 @@ template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
return size * 100 >= nbucket * load_factor;
}

static void init_buckets_and_thumbnail(
Bucket* buckets, uint64_t* thumbnail, size_t nbucket) {
void init_load_factor(u_int load_factor) {
if (_is_default_load_factor) {
_is_default_load_factor = false;
_load_factor = load_factor;
}
}

// True if using default buckets which is for small map optimization.
bool is_default_buckets() const {
return _buckets == (Bucket*)(&_default_buckets_spaces);
}

static void init_buckets_and_thumbnail(Bucket* buckets,
uint64_t* thumbnail,
size_t nbucket) {
for (size_t i = 0; i < nbucket; ++i) {
buckets[i].set_invalid();
}
Expand All @@ -332,12 +375,20 @@ template <typename _Map, typename _Element> friend class SparseFlatMapIterator;
bit_array_clear(thumbnail, nbucket);
}
}


static const size_t default_nthumbnail = BIT_ARRAY_LEN(DEFAULT_NBUCKET);
// Small map optimization.
typedef typename std::aligned_storage<sizeof(Bucket), alignof(Bucket)>::type
buckets_spaces_type;
// Note: need an extra bucket to let iterator know where buckets end.
buckets_spaces_type _default_buckets_spaces[DEFAULT_NBUCKET + 1];
uint64_t _default_thumbnail[default_nthumbnail];
size_t _size;
size_t _nbucket;
Bucket* _buckets;
uint64_t* _thumbnail;
u_int _load_factor;
bool _is_default_load_factor;
hasher _hashfn;
key_equal _eql;
SingleThreadedPool<sizeof(Bucket), 1024, 16, allocator_type> _pool;
Expand All @@ -348,7 +399,8 @@ template <typename _K, typename _T,
typename _Equal = DefaultEqualTo<_K>,
bool _Sparse = false,
typename _Alloc = PtAllocator>
using MultiFlatMap = FlatMap<_K, _T, _Hash, _Equal, _Sparse, _Alloc, true>;
using MultiFlatMap = FlatMap<
_K, _T, _Hash, _Equal, _Sparse, _Alloc, true>;

template <typename _K,
typename _Hash = DefaultHasher<_K>,
Expand All @@ -357,7 +409,8 @@ template <typename _K,
typename _Alloc = PtAllocator>
class FlatSet {
public:
typedef FlatMap<_K, FlatMapVoid, _Hash, _Equal, _Sparse, _Alloc> Map;
typedef FlatMap<_K, FlatMapVoid, _Hash, _Equal, _Sparse,
_Alloc, false> Map;
typedef typename Map::key_type key_type;
typedef typename Map::value_type value_type;
typedef typename Map::Bucket Bucket;
Expand Down Expand Up @@ -408,15 +461,18 @@ class FlatSet {

template <typename _K, typename _T,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K> >
class SparseFlatMap : public FlatMap<_K, _T, _Hash, _Equal, true> {
};
typename _Equal = DefaultEqualTo<_K>,
typename _Alloc = PtAllocator,
bool _Multi = false>
class SparseFlatMap : public FlatMap<
_K, _T, _Hash, _Equal, true, _Alloc, _Multi> {};

template <typename _K,
typename _Hash = DefaultHasher<_K>,
typename _Equal = DefaultEqualTo<_K> >
class SparseFlatSet : public FlatSet<_K, _Hash, _Equal, true> {
};
typename _Equal = DefaultEqualTo<_K>,
typename _Alloc = PtAllocator>
class SparseFlatSet : public FlatSet<
_K, _Hash, _Equal, true, _Alloc> {};

// Implement FlatMapElement
template <typename K, typename T>
Expand All @@ -431,6 +487,13 @@ class FlatMapElement {
// POD) which is wrong generally.
explicit FlatMapElement(const K& k) : _key(k), _value(T()) {}
// ^^^^^^^^^^^

FlatMapElement(const FlatMapElement& rhs)
: _key(rhs._key), _value(rhs._value) {}

FlatMapElement(FlatMapElement&& rhs) noexcept
: _key(std::move(rhs._key)), _value(std::move(rhs._value)) {}

const K& first_ref() const { return _key; }
T& second_ref() { return _value; }
T&& second_movable_ref() { return std::move(_value); }
Expand All @@ -442,16 +505,25 @@ class FlatMapElement {
inline static T&& second_movable_ref_from_value(value_type& v)
{ return std::move(v.second); }

void swap(FlatMapElement& rhs) {
std::swap(_key, rhs._key);
std::swap(_value, rhs._value);
}

private:
const K _key;
K _key;
T _value;
};

template <typename K>
class FlatMapElement<K, FlatMapVoid> {
public:
typedef const K value_type;
// See the comment in the above FlatMapElement.
explicit FlatMapElement(const K& k) : _key(k) {}
FlatMapElement(const FlatMapElement& rhs) : _key(rhs._key) {}
FlatMapElement(FlatMapElement&& rhs) noexcept : _key(std::move(rhs._key)) {}

const K& first_ref() const { return _key; }
FlatMapVoid& second_ref() { return second_ref_from_value(_key); }
FlatMapVoid& second_movable_ref() { return second_ref(); }
Expand All @@ -470,8 +542,7 @@ class FlatMapElement<K, FlatMapVoid> {

// Implement DefaultHasher and DefaultEqualTo
template <typename K>
struct DefaultHasher : public BUTIL_HASH_NAMESPACE::hash<K> {
};
struct DefaultHasher : public BUTIL_HASH_NAMESPACE::hash<K> {};

template <>
struct DefaultHasher<std::string> {
Expand Down
Loading
Loading