Revert "Less indirection in FixedHashTable"

This reverts commit 743aee9940.
This commit is contained in:
Alexey Milovidov 2020-07-31 00:52:37 +03:00
parent 1088bfffb1
commit 8496c984c5
4 changed files with 58 additions and 48 deletions

View File

@ -33,12 +33,12 @@ struct FixedClearableHashTableCell
}; };
template <typename Key> template <typename Key, typename Allocator = HashTableAllocator>
class FixedClearableHashSet : public FixedHashTable< class FixedClearableHashSet : public FixedHashTable<
Key, FixedClearableHashTableCell<Key>, FixedHashTableStoredSize<FixedClearableHashTableCell<Key>>> Key, FixedClearableHashTableCell<Key>, FixedHashTableStoredSize<FixedClearableHashTableCell<Key>>, Allocator>
{ {
public: public:
using Base = FixedHashTable<Key, FixedClearableHashTableCell<Key>, FixedHashTableStoredSize<FixedClearableHashTableCell<Key>>>; using Base = FixedHashTable<Key, FixedClearableHashTableCell<Key>, FixedHashTableStoredSize<FixedClearableHashTableCell<Key>>, Allocator>;
using LookupResult = typename Base::LookupResult; using LookupResult = typename Base::LookupResult;
void clear() void clear()

View File

@ -16,7 +16,7 @@ struct FixedHashMapCell
bool full; bool full;
Mapped mapped; Mapped mapped;
FixedHashMapCell() {} /// Not default to avoid unnecessary zero-initialization. FixedHashMapCell() {}
FixedHashMapCell(const Key &, const State &) : full(true) {} FixedHashMapCell(const Key &, const State &) : full(true) {}
FixedHashMapCell(const value_type & value_, const State &) : full(true), mapped(value_.second) {} FixedHashMapCell(const value_type & value_, const State &) : full(true), mapped(value_.second) {}
@ -61,7 +61,7 @@ struct FixedHashMapImplicitZeroCell
Mapped mapped; Mapped mapped;
FixedHashMapImplicitZeroCell() {} /// Not default to avoid unnecessary zero-initialization. FixedHashMapImplicitZeroCell() {}
FixedHashMapImplicitZeroCell(const Key &, const State &) {} FixedHashMapImplicitZeroCell(const Key &, const State &) {}
FixedHashMapImplicitZeroCell(const value_type & value_, const State &) : mapped(value_.second) {} FixedHashMapImplicitZeroCell(const value_type & value_, const State &) : mapped(value_.second) {}
@ -98,11 +98,12 @@ template <
typename Key, typename Key,
typename Mapped, typename Mapped,
typename Cell = FixedHashMapCell<Key, Mapped>, typename Cell = FixedHashMapCell<Key, Mapped>,
typename Size = FixedHashTableStoredSize<Cell>> typename Size = FixedHashTableStoredSize<Cell>,
class FixedHashMap : public FixedHashTable<Key, Cell, Size> typename Allocator = HashTableAllocator>
class FixedHashMap : public FixedHashTable<Key, Cell, Size, Allocator>
{ {
public: public:
using Base = FixedHashTable<Key, Cell, Size>; using Base = FixedHashTable<Key, Cell, Size, Allocator>;
using Self = FixedHashMap; using Self = FixedHashMap;
using LookupResult = typename Base::LookupResult; using LookupResult = typename Base::LookupResult;
@ -159,9 +160,10 @@ public:
} }
}; };
template <typename Key, typename Mapped> template <typename Key, typename Mapped, typename Allocator = HashTableAllocator>
using FixedImplicitZeroHashMapWithCalculatedSize = FixedHashMap< using FixedImplicitZeroHashMapWithCalculatedSize = FixedHashMap<
Key, Key,
Mapped, Mapped,
FixedHashMapImplicitZeroCell<Key, Mapped>, FixedHashMapImplicitZeroCell<Key, Mapped>,
FixedHashTableCalculatedSize<FixedHashMapImplicitZeroCell<Key, Mapped>>>; FixedHashTableCalculatedSize<FixedHashMapImplicitZeroCell<Key, Mapped>>,
Allocator>;

View File

@ -2,12 +2,12 @@
#include <Common/HashTable/FixedHashTable.h> #include <Common/HashTable/FixedHashTable.h>
template <typename Key> template <typename Key, typename Allocator = HashTableAllocator>
class FixedHashSet : public FixedHashTable<Key, FixedHashTableCell<Key>, FixedHashTableStoredSize<FixedHashTableCell<Key>>> class FixedHashSet : public FixedHashTable<Key, FixedHashTableCell<Key>, FixedHashTableStoredSize<FixedHashTableCell<Key>>, Allocator>
{ {
public: public:
using Cell = FixedHashTableCell<Key>; using Cell = FixedHashTableCell<Key>;
using Base = FixedHashTable<Key, Cell, FixedHashTableStoredSize<Cell>>; using Base = FixedHashTable<Key, Cell, FixedHashTableStoredSize<Cell>, Allocator>;
using Self = FixedHashSet; using Self = FixedHashSet;
void merge(const Self & rhs) void merge(const Self & rhs)

View File

@ -19,7 +19,7 @@ struct FixedHashTableCell
using mapped_type = VoidMapped; using mapped_type = VoidMapped;
bool full; bool full;
FixedHashTableCell() {} /// Not default to avoid unnecessary zero-initialization. FixedHashTableCell() {}
FixedHashTableCell(const Key &, const State &) : full(true) {} FixedHashTableCell(const Key &, const State &) : full(true) {}
const VoidKey getKey() const { return {}; } const VoidKey getKey() const { return {}; }
@ -95,8 +95,6 @@ struct FixedHashTableCalculatedSize
* comparision; c) The number of cycles for checking cell empty is halved; d) * comparision; c) The number of cycles for checking cell empty is halved; d)
* Memory layout is tighter, especially the Clearable variants. * Memory layout is tighter, especially the Clearable variants.
* *
* The sizeof is large - not meant to be allocated on stack. Use unique_ptr.
*
* NOTE: For Set variants this should always be better. For Map variants * NOTE: For Set variants this should always be better. For Map variants
* however, as we need to assemble the real cell inside each iterator, there * however, as we need to assemble the real cell inside each iterator, there
* might be some cases we fall short. * might be some cases we fall short.
@ -106,8 +104,8 @@ struct FixedHashTableCalculatedSize
* transfer, key updates (f.g. StringRef) and serde. This will allow * transfer, key updates (f.g. StringRef) and serde. This will allow
* TwoLevelHashSet(Map) to contain different type of sets(maps). * TwoLevelHashSet(Map) to contain different type of sets(maps).
*/ */
template <typename Key, typename Cell, typename Size> template <typename Key, typename Cell, typename Size, typename Allocator>
class FixedHashTable : private boost::noncopyable, protected Cell::State, protected Size class FixedHashTable : private boost::noncopyable, protected Allocator, protected Cell::State, protected Size
{ {
static constexpr size_t NUM_CELLS = 1ULL << (sizeof(Key) * 8); static constexpr size_t NUM_CELLS = 1ULL << (sizeof(Key) * 8);
@ -118,7 +116,18 @@ protected:
using Self = FixedHashTable; using Self = FixedHashTable;
Cell buf[NUM_CELLS]; /// A piece of memory for all elements. Cell * buf; /// A piece of memory for all elements.
void alloc() { buf = reinterpret_cast<Cell *>(Allocator::alloc(NUM_CELLS * sizeof(Cell))); }
void free()
{
if (buf)
{
Allocator::free(buf, getBufferSizeInBytes());
buf = nullptr;
}
}
void destroyElements() void destroyElements()
{ {
@ -193,29 +202,25 @@ public:
size_t hash(const Key & x) const { return x; } size_t hash(const Key & x) const { return x; }
FixedHashTable() FixedHashTable() { alloc(); }
{
/// Must zero-initialize.
/// Note: Cell buf[NUM_CELLS]{} does not zero-initialize if the Cell constructor is not default for POD type.
memset(buf, 0, NUM_CELLS * sizeof(Cell));
}
FixedHashTable(FixedHashTable && rhs) { *this = std::move(rhs); } FixedHashTable(FixedHashTable && rhs) : buf(nullptr) { *this = std::move(rhs); }
~FixedHashTable() ~FixedHashTable()
{ {
destroyElements(); destroyElements();
free();
} }
FixedHashTable & operator=(FixedHashTable && rhs) FixedHashTable & operator=(FixedHashTable && rhs)
{ {
destroyElements(); destroyElements();
free();
memcpy(buf, rhs.buf, NUM_CELLS * sizeof(Cell)); std::swap(buf, rhs.buf);
memset(rhs.buf, 0, NUM_CELLS * sizeof(Cell));
this->setSize(rhs.size()); this->setSize(rhs.size());
Allocator::operator=(std::move(rhs));
Cell::State::operator=(std::move(rhs)); Cell::State::operator=(std::move(rhs));
return *this; return *this;
@ -283,6 +288,9 @@ public:
const_iterator begin() const const_iterator begin() const
{ {
if (!buf)
return end();
const Cell * ptr = buf; const Cell * ptr = buf;
auto buf_end = buf + NUM_CELLS; auto buf_end = buf + NUM_CELLS;
while (ptr < buf_end && ptr->isZero(*this)) while (ptr < buf_end && ptr->isZero(*this))
@ -295,6 +303,9 @@ public:
iterator begin() iterator begin()
{ {
if (!buf)
return end();
Cell * ptr = buf; Cell * ptr = buf;
auto buf_end = buf + NUM_CELLS; auto buf_end = buf + NUM_CELLS;
while (ptr < buf_end && ptr->isZero(*this)) while (ptr < buf_end && ptr->isZero(*this))
@ -306,7 +317,7 @@ public:
const_iterator end() const const_iterator end() const
{ {
/// Avoid UBSan warning about adding zero to nullptr. It is valid in C++20 (and earlier) but not valid in C. /// Avoid UBSan warning about adding zero to nullptr. It is valid in C++20 (and earlier) but not valid in C.
return const_iterator(this, buf + NUM_CELLS); return const_iterator(this, buf ? buf + NUM_CELLS : buf);
} }
const_iterator cend() const const_iterator cend() const
@ -316,7 +327,7 @@ public:
iterator end() iterator end()
{ {
return iterator(this, buf + NUM_CELLS); return iterator(this, buf ? buf + NUM_CELLS : buf);
} }
@ -366,6 +377,9 @@ public:
Cell::State::write(wb); Cell::State::write(wb);
DB::writeVarUInt(size(), wb); DB::writeVarUInt(size(), wb);
if (!buf)
return;
for (auto ptr = buf, buf_end = buf + NUM_CELLS; ptr < buf_end; ++ptr) for (auto ptr = buf, buf_end = buf + NUM_CELLS; ptr < buf_end; ++ptr)
{ {
if (!ptr->isZero(*this)) if (!ptr->isZero(*this))
@ -381,6 +395,9 @@ public:
Cell::State::writeText(wb); Cell::State::writeText(wb);
DB::writeText(size(), wb); DB::writeText(size(), wb);
if (!buf)
return;
for (auto ptr = buf, buf_end = buf + NUM_CELLS; ptr < buf_end; ++ptr) for (auto ptr = buf, buf_end = buf + NUM_CELLS; ptr < buf_end; ++ptr)
{ {
if (!ptr->isZero(*this)) if (!ptr->isZero(*this))
@ -395,18 +412,13 @@ public:
void read(DB::ReadBuffer & rb) void read(DB::ReadBuffer & rb)
{ {
size_t old_size = size();
Cell::State::read(rb); Cell::State::read(rb);
destroyElements();
if (old_size)
destroyElements();
size_t m_size; size_t m_size;
DB::readVarUInt(m_size, rb); DB::readVarUInt(m_size, rb);
this->setSize(m_size); this->setSize(m_size);
free();
if (old_size) alloc();
memset(buf, 0, NUM_CELLS * sizeof(Cell));
for (size_t i = 0; i < m_size; ++i) for (size_t i = 0; i < m_size; ++i)
{ {
@ -420,18 +432,13 @@ public:
void readText(DB::ReadBuffer & rb) void readText(DB::ReadBuffer & rb)
{ {
size_t old_size = size();
Cell::State::readText(rb); Cell::State::readText(rb);
destroyElements();
if (old_size)
destroyElements();
size_t m_size; size_t m_size;
DB::readText(m_size, rb); DB::readText(m_size, rb);
this->setSize(m_size); this->setSize(m_size);
free();
if (old_size) alloc();
memset(buf, 0, NUM_CELLS * sizeof(Cell));
for (size_t i = 0; i < m_size; ++i) for (size_t i = 0; i < m_size; ++i)
{ {
@ -453,7 +460,7 @@ public:
destroyElements(); destroyElements();
this->clearSize(); this->clearSize();
memset(buf, 0, NUM_CELLS * sizeof(Cell)); memset(static_cast<void *>(buf), 0, NUM_CELLS * sizeof(*buf));
} }
/// After executing this function, the table can only be destroyed, /// After executing this function, the table can only be destroyed,
@ -462,6 +469,7 @@ public:
{ {
destroyElements(); destroyElements();
this->clearSize(); this->clearSize();
free();
} }
size_t getBufferSizeInBytes() const { return NUM_CELLS * sizeof(Cell); } size_t getBufferSizeInBytes() const { return NUM_CELLS * sizeof(Cell); }