Commit 121ecb47 authored by Giuseppe Ottaviano's avatar Giuseppe Ottaviano Committed by Facebook Github Bot

Template EliasFanoReader on size type

Summary: Shrink the reader type if less than 64 bits are sufficient for sizes. Do the same for `BitVectorCoding`, where we don't need an extra template parameter because the size is limited by the value domain.

Reviewed By: philippv, luciang

Differential Revision: D4976756

fbshipit-source-id: 685047da81a556d049fb924c612f99cea3056a82
parent 71ca4d5e
...@@ -240,6 +240,8 @@ class BitVectorReader { ...@@ -240,6 +240,8 @@ class BitVectorReader {
public: public:
typedef Encoder EncoderType; typedef Encoder EncoderType;
typedef typename Encoder::ValueType ValueType; typedef typename Encoder::ValueType ValueType;
// A bitvector can only be as large as its largest value.
typedef typename Encoder::ValueType SizeType;
typedef typename Encoder::SkipValueType SkipValueType; typedef typename Encoder::SkipValueType SkipValueType;
explicit BitVectorReader(const typename Encoder::CompressedList& list) explicit BitVectorReader(const typename Encoder::CompressedList& list)
...@@ -281,7 +283,7 @@ class BitVectorReader { ...@@ -281,7 +283,7 @@ class BitVectorReader {
return setValue(inner); return setValue(inner);
} }
bool skip(size_t n) { bool skip(SizeType n) {
CHECK_GT(n, 0); CHECK_GT(n, 0);
if (!kUnchecked && position() + n >= size_) { if (!kUnchecked && position() + n >= size_) {
...@@ -344,8 +346,9 @@ class BitVectorReader { ...@@ -344,8 +346,9 @@ class BitVectorReader {
if (Encoder::skipQuantum > 0 && v - value_ > Encoder::skipQuantum) { if (Encoder::skipQuantum > 0 && v - value_ > Encoder::skipQuantum) {
size_t q = v / Encoder::skipQuantum; size_t q = v / Encoder::skipQuantum;
position_ = size_t(folly::loadUnaligned<SkipValueType>( auto skipPointer = folly::loadUnaligned<SkipValueType>(
skipPointers_ + (q - 1) * sizeof(SkipValueType))) - 1; skipPointers_ + (q - 1) * sizeof(SkipValueType));
position_ = static_cast<SizeType>(skipPointer) - 1;
reposition(q * Encoder::skipQuantum); reposition(q * Encoder::skipQuantum);
} }
...@@ -376,19 +379,23 @@ class BitVectorReader { ...@@ -376,19 +379,23 @@ class BitVectorReader {
return true; return true;
} }
size_t size() const { return size_; } SizeType size() const {
return size_;
}
bool valid() const { bool valid() const {
return position() < size(); // Also checks that position() != -1. return position() < size(); // Also checks that position() != -1.
} }
size_t position() const { return position_; } SizeType position() const {
return position_;
}
ValueType value() const { ValueType value() const {
DCHECK(valid()); DCHECK(valid());
return value_; return value_;
} }
bool jump(size_t n) { bool jump(SizeType n) {
reset(); reset();
return skip(n + 1); return skip(n + 1);
} }
...@@ -422,12 +429,12 @@ class BitVectorReader { ...@@ -422,12 +429,12 @@ class BitVectorReader {
constexpr static size_t kLinearScanThreshold = 4; constexpr static size_t kLinearScanThreshold = 4;
size_t outer_;
size_t position_;
uint64_t block_; uint64_t block_;
SizeType outer_;
SizeType position_;
ValueType value_; ValueType value_;
size_t size_; SizeType size_;
ValueType upperBound_; ValueType upperBound_;
const uint8_t* const bits_; const uint8_t* const bits_;
const uint8_t* const skipPointers_; const uint8_t* const skipPointers_;
......
...@@ -331,7 +331,7 @@ struct EliasFanoEncoderV2<Value, ...@@ -331,7 +331,7 @@ struct EliasFanoEncoderV2<Value,
namespace detail { namespace detail {
template <class Encoder, class Instructions> template <class Encoder, class Instructions, class SizeType>
class UpperBitsReader { class UpperBitsReader {
typedef typename Encoder::SkipValueType SkipValueType; typedef typename Encoder::SkipValueType SkipValueType;
public: public:
...@@ -346,13 +346,17 @@ class UpperBitsReader { ...@@ -346,13 +346,17 @@ class UpperBitsReader {
void reset() { void reset() {
block_ = start_ != nullptr ? folly::loadUnaligned<block_t>(start_) : 0; block_ = start_ != nullptr ? folly::loadUnaligned<block_t>(start_) : 0;
position_ = std::numeric_limits<SizeType>::max();
outer_ = 0; outer_ = 0;
position_ = std::numeric_limits<size_t>::max();
value_ = 0; value_ = 0;
} }
size_t position() const { return position_; } SizeType position() const {
ValueType value() const { return value_; } return position_;
}
ValueType value() const {
return value_;
}
ValueType next() { ValueType next() {
// Skip to the first non-zero block. // Skip to the first non-zero block.
...@@ -368,7 +372,7 @@ class UpperBitsReader { ...@@ -368,7 +372,7 @@ class UpperBitsReader {
return setValue(inner); return setValue(inner);
} }
ValueType skip(size_t n) { ValueType skip(SizeType n) {
DCHECK_GT(n, 0); DCHECK_GT(n, 0);
position_ += n; // n 1-bits will be read. position_ += n; // n 1-bits will be read.
...@@ -448,7 +452,8 @@ class UpperBitsReader { ...@@ -448,7 +452,8 @@ class UpperBitsReader {
if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) { if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) {
reset(); reset();
} else { } else {
position_ = size_t(-1); // Avoid reading the head, skip() will reposition. // Avoid reading the head, skip() will reposition.
position_ = std::numeric_limits<SizeType>::max();
} }
return skip(n); return skip(n);
} }
...@@ -463,17 +468,17 @@ class UpperBitsReader { ...@@ -463,17 +468,17 @@ class UpperBitsReader {
} }
ValueType previousValue() const { ValueType previousValue() const {
DCHECK_NE(position(), -1); DCHECK_NE(position(), std::numeric_limits<SizeType>::max());
DCHECK_GT(position(), 0); DCHECK_GT(position(), 0);
size_t outer = outer_; auto outer = outer_;
auto inner = size_t(value_) - 8 * outer_ + position_; auto inner = size_t(value_) - 8 * outer_ + position_;
block_t block = folly::loadUnaligned<block_t>(start_ + outer); block_t block = folly::loadUnaligned<block_t>(start_ + outer);
block &= (block_t(1) << inner) - 1; block &= (block_t(1) << inner) - 1;
while (UNLIKELY(block == 0)) { while (UNLIKELY(block == 0)) {
DCHECK_GT(outer, 0); DCHECK_GT(outer, 0);
outer -= std::min(sizeof(block_t), outer); outer -= std::min<OuterType>(sizeof(block_t), outer);
block = folly::loadUnaligned<block_t>(start_ + outer); block = folly::loadUnaligned<block_t>(start_ + outer);
} }
...@@ -481,7 +486,7 @@ class UpperBitsReader { ...@@ -481,7 +486,7 @@ class UpperBitsReader {
return static_cast<ValueType>(8 * outer + inner - (position_ - 1)); return static_cast<ValueType>(8 * outer + inner - (position_ - 1));
} }
void setDone(size_t endPos) { void setDone(SizeType endPos) {
position_ = endPos; position_ = endPos;
} }
...@@ -491,19 +496,23 @@ class UpperBitsReader { ...@@ -491,19 +496,23 @@ class UpperBitsReader {
return value_; return value_;
} }
void reposition(size_t dest) { void reposition(SizeType dest) {
outer_ = dest / 8; outer_ = dest / 8;
block_ = folly::loadUnaligned<block_t>(start_ + outer_); block_ = folly::loadUnaligned<block_t>(start_ + outer_);
block_ &= ~((block_t(1) << (dest % 8)) - 1); block_ &= ~((block_t(1) << (dest % 8)) - 1);
} }
typedef uint64_t block_t; using block_t = uint64_t;
// The size in bytes of the upper bits is limited by n + universe / 8,
// so a type that can hold either sizes or values is sufficient.
using OuterType = typename std::common_type<ValueType, SizeType>::type;
const unsigned char* const forwardPointers_; const unsigned char* const forwardPointers_;
const unsigned char* const skipPointers_; const unsigned char* const skipPointers_;
const unsigned char* const start_; const unsigned char* const start_;
block_t block_; block_t block_;
size_t outer_; // Outer offset: number of consumed bytes in upper. SizeType position_; // Index of current value (= #reads - 1).
size_t position_; // Index of current value (= #reads - 1). OuterType outer_; // Outer offset: number of consumed bytes in upper.
ValueType value_; ValueType value_;
}; };
...@@ -512,18 +521,20 @@ class UpperBitsReader { ...@@ -512,18 +521,20 @@ class UpperBitsReader {
// If kUnchecked = true the caller must guarantee that all the // If kUnchecked = true the caller must guarantee that all the
// operations return valid elements, i.e., they would never return // operations return valid elements, i.e., they would never return
// false if checked. // false if checked.
template <class Encoder, template <
class Instructions = instructions::Default, class Encoder,
bool kUnchecked = false> class Instructions = instructions::Default,
bool kUnchecked = false,
class SizeType = size_t>
class EliasFanoReader { class EliasFanoReader {
public: public:
typedef Encoder EncoderType; typedef Encoder EncoderType;
typedef typename Encoder::ValueType ValueType; typedef typename Encoder::ValueType ValueType;
explicit EliasFanoReader(const typename Encoder::CompressedList& list) explicit EliasFanoReader(const typename Encoder::CompressedList& list)
: size_(list.size), : upper_(list),
lower_(list.lower), lower_(list.lower),
upper_(list), size_(list.size),
numLowerBits_(list.numLowerBits) { numLowerBits_(list.numLowerBits) {
DCHECK(Instructions::supported()); DCHECK(Instructions::supported());
// To avoid extra branching during skipTo() while reading // To avoid extra branching during skipTo() while reading
...@@ -556,12 +567,13 @@ class EliasFanoReader { ...@@ -556,12 +567,13 @@ class EliasFanoReader {
return true; return true;
} }
bool skip(size_t n) { bool skip(SizeType n) {
CHECK_GT(n, 0); CHECK_GT(n, 0);
if (kUnchecked || LIKELY(position() + n < size_)) { if (kUnchecked || LIKELY(position() + n < size_)) {
if (LIKELY(n < kLinearScanThreshold)) { if (LIKELY(n < kLinearScanThreshold)) {
for (size_t i = 0; i < n; ++i) upper_.next(); for (SizeType i = 0; i < n; ++i)
upper_.next();
} else { } else {
upper_.skip(n); upper_.skip(n);
} }
...@@ -599,7 +611,7 @@ class EliasFanoReader { ...@@ -599,7 +611,7 @@ class EliasFanoReader {
return true; return true;
} }
bool jump(size_t n) { bool jump(SizeType n) {
if (LIKELY(n < size_)) { // Also checks that n != -1. if (LIKELY(n < size_)) { // Also checks that n != -1.
value_ = readLowerPart(n) | (upper_.jump(n + 1) << numLowerBits_); value_ = readLowerPart(n) | (upper_.jump(n + 1) << numLowerBits_);
return true; return true;
...@@ -624,21 +636,26 @@ class EliasFanoReader { ...@@ -624,21 +636,26 @@ class EliasFanoReader {
(upper_.previousValue() << numLowerBits_); (upper_.previousValue() << numLowerBits_);
} }
size_t size() const { return size_; } SizeType size() const {
return size_;
}
bool valid() const { bool valid() const {
return position() < size(); // Also checks that position() != -1. return position() < size(); // Also checks that position() != -1.
} }
size_t position() const { return upper_.position(); } SizeType position() const {
return upper_.position();
}
ValueType value() const { ValueType value() const {
DCHECK(valid()); DCHECK(valid());
return value_; return value_;
} }
private: private:
// Must hold kInvalidValue + 1 == 0.
constexpr static ValueType kInvalidValue = constexpr static ValueType kInvalidValue =
std::numeric_limits<ValueType>::max(); // Must hold kInvalidValue + 1 == 0. std::numeric_limits<ValueType>::max();
bool setDone() { bool setDone() {
value_ = kInvalidValue; value_ = kInvalidValue;
...@@ -646,7 +663,7 @@ class EliasFanoReader { ...@@ -646,7 +663,7 @@ class EliasFanoReader {
return false; return false;
} }
ValueType readLowerPart(size_t i) const { ValueType readLowerPart(SizeType i) const {
DCHECK_LT(i, size_); DCHECK_LT(i, size_);
const size_t pos = i * numLowerBits_; const size_t pos = i * numLowerBits_;
const unsigned char* ptr = lower_ + (pos / 8); const unsigned char* ptr = lower_ + (pos / 8);
...@@ -668,9 +685,9 @@ class EliasFanoReader { ...@@ -668,9 +685,9 @@ class EliasFanoReader {
constexpr static size_t kLinearScanThreshold = 8; constexpr static size_t kLinearScanThreshold = 8;
size_t size_; detail::UpperBitsReader<Encoder, Instructions, SizeType> upper_;
const uint8_t* lower_; const uint8_t* lower_;
detail::UpperBitsReader<Encoder, Instructions> upper_; SizeType size_;
ValueType value_ = kInvalidValue; ValueType value_ = kInvalidValue;
ValueType lastValue_; ValueType lastValue_;
uint8_t numLowerBits_; uint8_t numLowerBits_;
......
...@@ -94,11 +94,12 @@ class EliasFanoCodingTest : public ::testing::Test { ...@@ -94,11 +94,12 @@ class EliasFanoCodingTest : public ::testing::Test {
testEmpty<Reader, Encoder>(); testEmpty<Reader, Encoder>();
} }
template <size_t kSkipQuantum, size_t kForwardQuantum> template <size_t kSkipQuantum, size_t kForwardQuantum, class SizeType>
void doTestAll() { void doTestAll() {
typedef EliasFanoEncoderV2< typedef EliasFanoEncoderV2<
uint32_t, uint32_t, kSkipQuantum, kForwardQuantum> Encoder; uint32_t, uint32_t, kSkipQuantum, kForwardQuantum> Encoder;
typedef EliasFanoReader<Encoder, instructions::EF_TEST_ARCH> Reader; using Reader =
EliasFanoReader<Encoder, instructions::EF_TEST_ARCH, false, SizeType>;
testAll<Reader, Encoder>({0}); testAll<Reader, Encoder>({0});
testAll<Reader, Encoder>(generateRandomList(100 * 1000, 10 * 1000 * 1000)); testAll<Reader, Encoder>(generateRandomList(100 * 1000, 10 * 1000 * 1000));
testAll<Reader, Encoder>(generateSeqList(1, 100000, 100)); testAll<Reader, Encoder>(generateSeqList(1, 100000, 100));
...@@ -110,19 +111,23 @@ TEST_F(EliasFanoCodingTest, Empty) { ...@@ -110,19 +111,23 @@ TEST_F(EliasFanoCodingTest, Empty) {
} }
TEST_F(EliasFanoCodingTest, Simple) { TEST_F(EliasFanoCodingTest, Simple) {
doTestAll<0, 0>(); doTestAll<0, 0, uint32_t>();
doTestAll<0, 0, size_t>();
} }
TEST_F(EliasFanoCodingTest, SkipPointers) { TEST_F(EliasFanoCodingTest, SkipPointers) {
doTestAll<128, 0>(); doTestAll<128, 0, uint32_t>();
doTestAll<128, 0, size_t>();
} }
TEST_F(EliasFanoCodingTest, ForwardPointers) { TEST_F(EliasFanoCodingTest, ForwardPointers) {
doTestAll<0, 128>(); doTestAll<0, 128, uint32_t>();
doTestAll<0, 128, size_t>();
} }
TEST_F(EliasFanoCodingTest, SkipForwardPointers) { TEST_F(EliasFanoCodingTest, SkipForwardPointers) {
doTestAll<128, 128>(); doTestAll<128, 128, uint32_t>();
doTestAll<128, 128, size_t>();
} }
TEST_F(EliasFanoCodingTest, Select64) { TEST_F(EliasFanoCodingTest, Select64) {
......
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