Commit a2aff3ba authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook Github Bot

save 8 bytes and a bit of work in F14Vector{Map,Set}

Summary:
F14VectorMap and F14VectorSet use ItemIter to interact with F14Table,
but they don't actually need to be able to identify the begin ItemIter.
This diff removes both the storage for packedBegin_ and the code to
update it (present in all insert and erase functions) for that policy.
This is a small CPU optimization, and reduces sizeof(F14Vector{Map,Set})
from 32 bytes to 24.

Reviewed By: shixiao

Differential Revision: D7181769

fbshipit-source-id: 318c63584543fca339a002d8bcb5872a7bd0bdd3
parent bfdcacf9
...@@ -82,6 +82,9 @@ struct BasePolicy ...@@ -82,6 +82,9 @@ struct BasePolicy
using Super = std::tuple<Hasher, KeyEqual, Alloc>; using Super = std::tuple<Hasher, KeyEqual, Alloc>;
// if false, F14Table will be smaller but F14Table::begin() won't work
static constexpr bool kEnableItemIteration = true;
static constexpr bool isAvalanchingHasher() { static constexpr bool isAvalanchingHasher() {
return IsAvalanchingHasher<Hasher, Key>::value; return IsAvalanchingHasher<Hasher, Key>::value;
} }
...@@ -469,10 +472,8 @@ class ValueContainerPolicy : public BasePolicy< ...@@ -469,10 +472,8 @@ class ValueContainerPolicy : public BasePolicy<
AllocTraits::destroy(a, std::addressof(item)); AllocTraits::destroy(a, std::addressof(item));
} }
std::size_t indirectBytesUsed( std::size_t indirectBytesUsed(std::size_t /*size*/, std::size_t /*capacity*/)
std::size_t /*size*/, const {
std::size_t /*capacity*/,
ItemIter /*underlying*/) const {
return 0; return 0;
} }
...@@ -681,10 +682,8 @@ class NodeContainerPolicy ...@@ -681,10 +682,8 @@ class NodeContainerPolicy
item.~Item(); item.~Item();
} }
std::size_t indirectBytesUsed( std::size_t indirectBytesUsed(std::size_t size, std::size_t /*capacity*/)
std::size_t size, const {
std::size_t /*capacity*/,
ItemIter /*underlying*/) const {
return size * sizeof(Value); return size * sizeof(Value);
} }
...@@ -817,6 +816,8 @@ class VectorContainerPolicy : public BasePolicy< ...@@ -817,6 +816,8 @@ class VectorContainerPolicy : public BasePolicy<
using typename Super::AllocTraits; using typename Super::AllocTraits;
public: public:
static constexpr bool kEnableItemIteration = false;
using InternalSizeType = Item; using InternalSizeType = Item;
using ConstIter = using ConstIter =
...@@ -1109,10 +1110,8 @@ class VectorContainerPolicy : public BasePolicy< ...@@ -1109,10 +1110,8 @@ class VectorContainerPolicy : public BasePolicy<
} }
} }
std::size_t indirectBytesUsed( std::size_t indirectBytesUsed(std::size_t /*size*/, std::size_t capacity)
std::size_t /*size*/, const {
std::size_t capacity,
ItemIter /*underlying*/) const {
return sizeof(Value) * capacity; return sizeof(Value) * capacity;
} }
......
...@@ -636,8 +636,11 @@ class F14Table : public Policy { ...@@ -636,8 +636,11 @@ class F14Table : public Policy {
ChunkPtr chunks_{Chunk::emptyInstance()}; ChunkPtr chunks_{Chunk::emptyInstance()};
typename Policy::InternalSizeType chunkMask_{0}; typename Policy::InternalSizeType chunkMask_{0};
typename Policy::InternalSizeType size_{0}; SizeAndPackedBegin<
typename ItemIter::Packed packedBegin_{ItemIter{}.pack()}; typename Policy::InternalSizeType,
ItemIter,
Policy::kEnableItemIteration>
sizeAndPackedBegin_;
//////// end fields //////// end fields
...@@ -645,8 +648,12 @@ class F14Table : public Policy { ...@@ -645,8 +648,12 @@ class F14Table : public Policy {
using std::swap; using std::swap;
swap(chunks_, rhs.chunks_); swap(chunks_, rhs.chunks_);
swap(chunkMask_, rhs.chunkMask_); swap(chunkMask_, rhs.chunkMask_);
swap(size_, rhs.size_); swap(sizeAndPackedBegin_.size_, rhs.sizeAndPackedBegin_.size_);
swap(packedBegin_, rhs.packedBegin_); if (Policy::kEnableItemIteration) {
swap(
sizeAndPackedBegin_.packedBegin(),
rhs.sizeAndPackedBegin_.packedBegin());
}
} }
public: public:
...@@ -838,7 +845,8 @@ class F14Table : public Policy { ...@@ -838,7 +845,8 @@ class F14Table : public Policy {
public: public:
ItemIter begin() const noexcept { ItemIter begin() const noexcept {
return ItemIter{packedBegin_}; FOLLY_SAFE_DCHECK(Policy::kEnableItemIteration, "");
return ItemIter{sizeAndPackedBegin_.packedBegin()};
} }
ItemIter end() const noexcept { ItemIter end() const noexcept {
...@@ -850,7 +858,7 @@ class F14Table : public Policy { ...@@ -850,7 +858,7 @@ class F14Table : public Policy {
} }
std::size_t size() const noexcept { std::size_t size() const noexcept {
return size_; return sizeAndPackedBegin_.size_;
} }
std::size_t max_size() const noexcept { std::size_t max_size() const noexcept {
...@@ -1005,13 +1013,15 @@ class F14Table : public Policy { ...@@ -1005,13 +1013,15 @@ class F14Table : public Policy {
private: private:
void adjustSizeAndBeginAfterInsert(ItemIter iter) { void adjustSizeAndBeginAfterInsert(ItemIter iter) {
// packedBegin_ is the max of all valid ItemIter::pack() if (Policy::kEnableItemIteration) {
auto packed = iter.pack(); // packedBegin is the max of all valid ItemIter::pack()
if (packedBegin_ < packed) { auto packed = iter.pack();
packedBegin_ = packed; if (sizeAndPackedBegin_.packedBegin() < packed) {
sizeAndPackedBegin_.packedBegin() = packed;
}
} }
++size_; ++sizeAndPackedBegin_.size_;
} }
// Ignores hp if pos.chunk()->hostedOverflowCount() == 0 // Ignores hp if pos.chunk()->hostedOverflowCount() == 0
...@@ -1037,14 +1047,16 @@ class F14Table : public Policy { ...@@ -1037,14 +1047,16 @@ class F14Table : public Policy {
} }
void adjustSizeAndBeginBeforeErase(ItemIter iter) { void adjustSizeAndBeginBeforeErase(ItemIter iter) {
--size_; --sizeAndPackedBegin_.size_;
if (iter.pack() == packedBegin_) { if (Policy::kEnableItemIteration) {
if (size_ == 0) { if (iter.pack() == sizeAndPackedBegin_.packedBegin()) {
iter = ItemIter{}; if (size() == 0) {
} else { iter = ItemIter{};
iter.precheckedAdvance(); } else {
iter.precheckedAdvance();
}
sizeAndPackedBegin_.packedBegin() = iter.pack();
} }
packedBegin_ = iter.pack();
} }
} }
...@@ -1053,7 +1065,7 @@ class F14Table : public Policy { ...@@ -1053,7 +1065,7 @@ class F14Table : public Policy {
try { try {
auto dst = pos.itemAddr(); auto dst = pos.itemAddr();
folly::assume(dst != nullptr); folly::assume(dst != nullptr);
this->constructValueAtItem(size_, dst, std::forward<Args>(args)...); this->constructValueAtItem(size(), dst, std::forward<Args>(args)...);
} catch (...) { } catch (...) {
eraseBlank(pos, hp); eraseBlank(pos, hp);
throw; throw;
...@@ -1083,6 +1095,14 @@ class F14Table : public Policy { ...@@ -1083,6 +1095,14 @@ class F14Table : public Policy {
return ItemIter{chunk, itemIndex}; return ItemIter{chunk, itemIndex};
} }
ChunkPtr lastOccupiedChunk() const {
if (Policy::kEnableItemIteration) {
return begin().chunk();
} else {
return chunks_ + chunkMask_;
}
}
void directCopyFrom(F14Table const& src) { void directCopyFrom(F14Table const& src) {
FOLLY_SAFE_DCHECK(src.size() > 0 && chunkMask_ == src.chunkMask_, ""); FOLLY_SAFE_DCHECK(src.size() > 0 && chunkMask_ == src.chunkMask_, "");
...@@ -1104,17 +1124,22 @@ class F14Table : public Policy { ...@@ -1104,17 +1124,22 @@ class F14Table : public Policy {
// partial failure should not occur. Sorry for the subtle invariants // partial failure should not occur. Sorry for the subtle invariants
// in the Policy API. // in the Policy API.
auto srcBegin = src.begin();
std::size_t maxChunkIndex = srcBegin.chunk() - src.chunks_;
if (FOLLY_IS_TRIVIALLY_COPYABLE(Item) && !this->destroyItemOnClear() && if (FOLLY_IS_TRIVIALLY_COPYABLE(Item) && !this->destroyItemOnClear() &&
bucket_count() == src.bucket_count()) { bucket_count() == src.bucket_count()) {
// most happy path // most happy path
auto n = allocSize(chunkMask_ + 1, bucket_count()); auto n = allocSize(chunkMask_ + 1, bucket_count());
std::memcpy(&chunks_[0], &src.chunks_[0], n); std::memcpy(&chunks_[0], &src.chunks_[0], n);
size_ = src.size_; sizeAndPackedBegin_.size_ = src.size();
packedBegin_ = ItemIter{chunks_ + maxChunkIndex, srcBegin.index()}.pack(); if (Policy::kEnableItemIteration) {
auto srcBegin = src.begin();
sizeAndPackedBegin_.packedBegin() =
ItemIter{chunks_ + (srcBegin.chunk() - src.chunks_),
srcBegin.index()}
.pack();
}
} else { } else {
std::size_t maxChunkIndex = src.lastOccupiedChunk() - src.chunks_;
// happy path, no rehash but pack items toward bottom of chunk and // happy path, no rehash but pack items toward bottom of chunk and
// use copy constructor // use copy constructor
Chunk const* srcChunk = &src.chunks_[maxChunkIndex]; Chunk const* srcChunk = &src.chunks_[maxChunkIndex];
...@@ -1138,18 +1163,20 @@ class F14Table : public Policy { ...@@ -1138,18 +1163,20 @@ class F14Table : public Policy {
this->constructValueAtItem( this->constructValueAtItem(
0, dst, std::forward<decltype(srcValue)>(srcValue)); 0, dst, std::forward<decltype(srcValue)>(srcValue));
dstChunk->setTag(dstI, srcChunk->tag(srcI)); dstChunk->setTag(dstI, srcChunk->tag(srcI));
++size_; ++sizeAndPackedBegin_.size_;
} }
--srcChunk; --srcChunk;
--dstChunk; --dstChunk;
} while (size_ != src.size_); } while (size() != src.size());
// reset doesn't care about packedBegin, so we don't fix it until the end // reset doesn't care about packedBegin, so we don't fix it until the end
packedBegin_ = if (Policy::kEnableItemIteration) {
ItemIter{chunks_ + maxChunkIndex, sizeAndPackedBegin_.packedBegin() =
folly::popcount(chunks_[maxChunkIndex].occupiedMask()) - 1} ItemIter{chunks_ + maxChunkIndex,
.pack(); folly::popcount(chunks_[maxChunkIndex].occupiedMask()) - 1}
.pack();
}
} }
success = true; success = true;
...@@ -1197,7 +1224,7 @@ class F14Table : public Policy { ...@@ -1197,7 +1224,7 @@ class F14Table : public Policy {
// lifecycle (F14Vector) then nothing after beforeCopy can throw and // lifecycle (F14Vector) then nothing after beforeCopy can throw and
// we don't have to worry about partial failure. // we don't have to worry about partial failure.
std::size_t srcChunkIndex = src.begin().chunk() - src.chunks_; std::size_t srcChunkIndex = src.lastOccupiedChunk() - src.chunks_;
while (true) { while (true) {
Chunk const* srcChunk = &src.chunks_[srcChunkIndex]; Chunk const* srcChunk = &src.chunks_[srcChunkIndex];
auto mask = srcChunk->occupiedMask(); auto mask = srcChunk->occupiedMask();
...@@ -1272,13 +1299,13 @@ class F14Table : public Policy { ...@@ -1272,13 +1299,13 @@ class F14Table : public Policy {
const auto origMaxSizeWithoutRehash = bucket_count(); const auto origMaxSizeWithoutRehash = bucket_count();
auto undoState = this->beforeRehash( auto undoState = this->beforeRehash(
size_, origMaxSizeWithoutRehash, newMaxSizeWithoutRehash); size(), origMaxSizeWithoutRehash, newMaxSizeWithoutRehash);
bool success = false; bool success = false;
SCOPE_EXIT { SCOPE_EXIT {
this->afterRehash( this->afterRehash(
std::move(undoState), std::move(undoState),
success, success,
size_, size(),
origMaxSizeWithoutRehash, origMaxSizeWithoutRehash,
newMaxSizeWithoutRehash); newMaxSizeWithoutRehash);
}; };
...@@ -1286,7 +1313,7 @@ class F14Table : public Policy { ...@@ -1286,7 +1313,7 @@ class F14Table : public Policy {
chunks_ = newChunks(newChunkCount, newMaxSizeWithoutRehash); chunks_ = newChunks(newChunkCount, newMaxSizeWithoutRehash);
chunkMask_ = newChunkCount - 1; chunkMask_ = newChunkCount - 1;
if (size_ == 0) { if (size() == 0) {
// nothing to do // nothing to do
} else if (origChunkCount == 1 && newChunkCount == 1) { } else if (origChunkCount == 1 && newChunkCount == 1) {
// no mask, no chunk scan, no hash computation, no probing // no mask, no chunk scan, no hash computation, no probing
...@@ -1294,7 +1321,7 @@ class F14Table : public Policy { ...@@ -1294,7 +1321,7 @@ class F14Table : public Policy {
auto dstChunk = chunks_; auto dstChunk = chunks_;
std::size_t srcI = 0; std::size_t srcI = 0;
std::size_t dstI = 0; std::size_t dstI = 0;
while (dstI < size_) { while (dstI < size()) {
if (LIKELY(srcChunk->occupied(srcI))) { if (LIKELY(srcChunk->occupied(srcI))) {
dstChunk->setTag(dstI, srcChunk->tag(srcI)); dstChunk->setTag(dstI, srcChunk->tag(srcI));
this->moveItemDuringRehash( this->moveItemDuringRehash(
...@@ -1303,7 +1330,9 @@ class F14Table : public Policy { ...@@ -1303,7 +1330,9 @@ class F14Table : public Policy {
} }
++srcI; ++srcI;
} }
packedBegin_ = ItemIter{dstChunk, dstI - 1}.pack(); if (Policy::kEnableItemIteration) {
sizeAndPackedBegin_.packedBegin() = ItemIter{dstChunk, dstI - 1}.pack();
}
} else { } else {
// 1 byte per chunk means < 1 bit per value temporary overhead // 1 byte per chunk means < 1 bit per value temporary overhead
std::array<uint8_t, 256> stackBuf; std::array<uint8_t, 256> stackBuf;
...@@ -1335,7 +1364,7 @@ class F14Table : public Policy { ...@@ -1335,7 +1364,7 @@ class F14Table : public Policy {
}; };
auto srcChunk = origChunks + origChunkCount - 1; auto srcChunk = origChunks + origChunkCount - 1;
std::size_t remaining = size_; std::size_t remaining = size();
while (remaining > 0) { while (remaining > 0) {
auto mask = srcChunk->occupiedMask(); auto mask = srcChunk->occupiedMask();
if (Policy::prefetchBeforeRehash()) { if (Policy::prefetchBeforeRehash()) {
...@@ -1357,12 +1386,15 @@ class F14Table : public Policy { ...@@ -1357,12 +1386,15 @@ class F14Table : public Policy {
--srcChunk; --srcChunk;
} }
// this code replaces size_ invocations of adjustSizeAndBeginAfterInsert if (Policy::kEnableItemIteration) {
std::size_t i = chunkMask_; // this code replaces size invocations of adjustSizeAndBeginAfterInsert
while (fullness[i] == 0) { std::size_t i = chunkMask_;
--i; while (fullness[i] == 0) {
--i;
}
sizeAndPackedBegin_.packedBegin() =
ItemIter{chunks_ + i, std::size_t{fullness[i]} - 1}.pack();
} }
packedBegin_ = ItemIter{chunks_ + i, std::size_t{fullness[i]} - 1}.pack();
} }
if (origMaxSizeWithoutRehash != 0) { if (origMaxSizeWithoutRehash != 0) {
...@@ -1507,8 +1539,10 @@ class F14Table : public Policy { ...@@ -1507,8 +1539,10 @@ class F14Table : public Policy {
} }
chunks_[0].markEof(c0c); chunks_[0].markEof(c0c);
} }
packedBegin_ = ItemIter{}.pack(); if (Policy::kEnableItemIteration) {
size_ = 0; sizeAndPackedBegin_.packedBegin() = ItemIter{}.pack();
}
sizeAndPackedBegin_.size_ = 0;
} }
if (willReset) { if (willReset) {
...@@ -1551,7 +1585,7 @@ class F14Table : public Policy { ...@@ -1551,7 +1585,7 @@ class F14Table : public Policy {
template <typename K> template <typename K>
std::size_t erase(K const& key) { std::size_t erase(K const& key) {
if (UNLIKELY(size_ == 0)) { if (UNLIKELY(size() == 0)) {
return 0; return 0;
} }
auto hp = splitHash(this->computeKeyHash(key)); auto hp = splitHash(this->computeKeyHash(key));
...@@ -1589,7 +1623,7 @@ class F14Table : public Policy { ...@@ -1589,7 +1623,7 @@ class F14Table : public Policy {
F14TableStats computeStats() const { F14TableStats computeStats() const {
F14TableStats stats; F14TableStats stats;
if (folly::kIsDebug) { if (folly::kIsDebug && Policy::kEnableItemIteration) {
// validate iteration // validate iteration
std::size_t n = 0; std::size_t n = 0;
ItemIter prev; ItemIter prev;
...@@ -1671,7 +1705,7 @@ class F14Table : public Policy { ...@@ -1671,7 +1705,7 @@ class F14Table : public Policy {
stats.totalBytes = sizeof(*this) + stats.totalBytes = sizeof(*this) +
(cc == 0 ? 0 : allocSize(cc, bucket_count())) + (cc == 0 ? 0 : allocSize(cc, bucket_count())) +
this->indirectBytesUsed(size(), bucket_count(), begin()); this->indirectBytesUsed(size(), bucket_count());
stats.overheadBytes = stats.totalBytes - size() * sizeof(value_type); stats.overheadBytes = stats.totalBytes - size() * sizeof(value_type);
return stats; return stats;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment