Commit 297d5cee authored by Philip Pronin's avatar Philip Pronin Committed by Sara Golemon

EliasFanoReader::{jump,jumpTo}

Summary:
Renamed `goTo` to `jump`, introduced `jumpTo`.

FBCode: Updated CSS tree benchmark to compare with EF-based search that
is using `jumpTo` (P14517259).

Test Plan: fbconfig -r folly/experimental/test:eliasfano_test && fbmake runtests_opt -j32

@override-unit-failures

Reviewed By: lucian@fb.com

Subscribers: fbcode-common-diffs@

FB internal diff: D1474431

Tasks: 4536072
parent 59bd43f8
...@@ -460,15 +460,24 @@ class UpperBitsReader { ...@@ -460,15 +460,24 @@ class UpperBitsReader {
return value_; return value_;
} }
ValueType goTo(size_t n) { ValueType jump(size_t n) {
if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) { if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) {
reset(); reset();
} else { } else {
position_ = -1; // Avoid reading the head, skip() will call reposition(). position_ = -1; // Avoid reading the head, skip() will reposition.
} }
return skip(n); return skip(n);
} }
ValueType jumpToNext(ValueType v) {
if (Encoder::skipQuantum == 0 || v < Encoder::skipQuantum) {
reset();
} else {
value_ = 0; // Avoid reading the head, skipToNext() will reposition.
}
return skipToNext(v);
}
private: private:
ValueType setValue() { ValueType setValue() {
value_ = static_cast<ValueType>(8 * outer_ + inner_ - position_); value_ = static_cast<ValueType>(8 * outer_ + inner_ - position_);
...@@ -520,10 +529,11 @@ class EliasFanoReader : private boost::noncopyable { ...@@ -520,10 +529,11 @@ class EliasFanoReader : private boost::noncopyable {
(lastUpperValue << list_.numLowerBits); (lastUpperValue << list_.numLowerBits);
} }
size_t size() const { return list_.size; } void reset() {
upper_.reset();
size_t position() const { return progress_ - 1; } progress_ = 0;
ValueType value() const { return value_; } value_ = 0;
}
bool next() { bool next() {
if (UNLIKELY(progress_ == list_.size)) { if (UNLIKELY(progress_ == list_.size)) {
...@@ -555,36 +565,24 @@ class EliasFanoReader : private boost::noncopyable { ...@@ -555,36 +565,24 @@ class EliasFanoReader : private boost::noncopyable {
DCHECK_GE(value, value_); DCHECK_GE(value, value_);
if (value <= value_) { if (value <= value_) {
return true; return true;
} } else if (value > lastValue_) {
if (value > lastValue_) {
progress_ = list_.size; progress_ = list_.size;
value_ = std::numeric_limits<ValueType>::max(); value_ = std::numeric_limits<ValueType>::max();
return false; return false;
} }
upper_.skipToNext(value >> list_.numLowerBits); upper_.skipToNext(value >> list_.numLowerBits);
progress_ = upper_.position(); iterateTo(value);
value_ = readLowerPart(progress_) |
(upper_.value() << list_.numLowerBits);
++progress_;
while (value_ < value) {
value_ = readLowerPart(progress_) |
(upper_.next() << list_.numLowerBits);
++progress_;
}
return true; return true;
} }
bool goTo(size_t n) { bool jump(size_t n) {
if (LIKELY(n - 1 < list_.size)) { // n > 0 && n <= list_.size if (LIKELY(n - 1 < list_.size)) { // n > 0 && n <= list_.size
progress_ = n; progress_ = n;
value_ = readLowerPart(n - 1) | (upper_.goTo(n) << list_.numLowerBits); value_ = readLowerPart(n - 1) | (upper_.jump(n) << list_.numLowerBits);
return true; return true;
} else if (n == 0) { } else if (n == 0) {
upper_.reset(); reset();
progress_ = 0;
value_ = 0;
return true; return true;
} }
progress_ = list_.size; progress_ = list_.size;
...@@ -592,6 +590,26 @@ class EliasFanoReader : private boost::noncopyable { ...@@ -592,6 +590,26 @@ class EliasFanoReader : private boost::noncopyable {
return false; return false;
} }
ValueType jumpTo(ValueType value) {
if (value <= 0) {
reset();
return true;
} else if (value > lastValue_) {
progress_ = list_.size;
value_ = std::numeric_limits<ValueType>::max();
return false;
}
upper_.jumpToNext(value >> list_.numLowerBits);
iterateTo(value);
return true;
}
size_t size() const { return list_.size; }
size_t position() const { return progress_ - 1; }
ValueType value() const { return value_; }
private: private:
ValueType readLowerPart(size_t i) const { ValueType readLowerPart(size_t i) const {
DCHECK_LT(i, list_.size); DCHECK_LT(i, list_.size);
...@@ -601,6 +619,16 @@ class EliasFanoReader : private boost::noncopyable { ...@@ -601,6 +619,16 @@ class EliasFanoReader : private boost::noncopyable {
return lowerMask_ & (ptrv >> (pos % 8)); return lowerMask_ & (ptrv >> (pos % 8));
} }
void iterateTo(ValueType value) {
progress_ = upper_.position();
value_ = readLowerPart(progress_) | (upper_.value() << list_.numLowerBits);
++progress_;
while (value_ < value) {
value_ = readLowerPart(progress_) | (upper_.next() << list_.numLowerBits);
++progress_;
}
}
const EliasFanoCompressedList list_; const EliasFanoCompressedList list_;
const ValueType lowerMask_; const ValueType lowerMask_;
detail::UpperBitsReader<Encoder, Instructions> upper_; detail::UpperBitsReader<Encoder, Instructions> upper_;
......
...@@ -153,7 +153,7 @@ void testSkipTo(const std::vector<uint32_t>& data, const List& list) { ...@@ -153,7 +153,7 @@ void testSkipTo(const std::vector<uint32_t>& data, const List& list) {
} }
template <class Reader, class List> template <class Reader, class List>
void testGoTo(const std::vector<uint32_t>& data, const List& list) { void testJump(const std::vector<uint32_t>& data, const List& list) {
std::mt19937 gen; std::mt19937 gen;
std::vector<size_t> is(data.size()); std::vector<size_t> is(data.size());
for (size_t i = 0; i < data.size(); ++i) { for (size_t i = 0; i < data.size(); ++i) {
...@@ -165,13 +165,45 @@ void testGoTo(const std::vector<uint32_t>& data, const List& list) { ...@@ -165,13 +165,45 @@ void testGoTo(const std::vector<uint32_t>& data, const List& list) {
} }
Reader reader(list); Reader reader(list);
EXPECT_TRUE(reader.goTo(0)); EXPECT_TRUE(reader.jump(0));
EXPECT_EQ(reader.value(), 0); EXPECT_EQ(reader.value(), 0);
for (auto i : is) { for (auto i : is) {
EXPECT_TRUE(reader.goTo(i + 1)); EXPECT_TRUE(reader.jump(i + 1));
EXPECT_EQ(reader.value(), data[i]); EXPECT_EQ(reader.value(), data[i]);
} }
EXPECT_FALSE(reader.goTo(data.size() + 1)); EXPECT_FALSE(reader.jump(data.size() + 1));
EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
}
template <class Reader, class List>
void testJumpTo(const std::vector<uint32_t>& data, const List& list) {
CHECK(!data.empty());
Reader reader(list);
std::mt19937 gen;
std::uniform_int_distribution<> values(0, data.back());
const size_t iters = Reader::EncoderType::skipQuantum == 0 ? 100 : 10000;
for (size_t i = 0; i < iters; ++i) {
const uint32_t value = values(gen);
auto it = std::lower_bound(data.begin(), data.end(), value);
CHECK(it != data.end());
EXPECT_TRUE(reader.jumpTo(value));
EXPECT_EQ(reader.value(), *it);
}
EXPECT_TRUE(reader.jumpTo(0));
EXPECT_EQ(reader.value(), 0);
if (data.front() > 0) {
EXPECT_TRUE(reader.jumpTo(1));
EXPECT_EQ(reader.value(), data.front());
}
EXPECT_TRUE(reader.jumpTo(data.back()));
EXPECT_EQ(reader.value(), data.back());
EXPECT_FALSE(reader.jumpTo(data.back() + 1));
EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max()); EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
} }
...@@ -188,10 +220,13 @@ void testEmpty() { ...@@ -188,10 +220,13 @@ void testEmpty() {
Reader reader(list); Reader reader(list);
EXPECT_FALSE(reader.skip(1)); EXPECT_FALSE(reader.skip(1));
EXPECT_FALSE(reader.skip(10)); EXPECT_FALSE(reader.skip(10));
EXPECT_FALSE(reader.jump(1));
EXPECT_FALSE(reader.jump(10));
} }
{ {
Reader reader(list); Reader reader(list);
EXPECT_FALSE(reader.skipTo(1)); EXPECT_FALSE(reader.skipTo(1));
EXPECT_FALSE(reader.jumpTo(1));
} }
} }
...@@ -201,7 +236,8 @@ void testAll(const std::vector<uint32_t>& data) { ...@@ -201,7 +236,8 @@ void testAll(const std::vector<uint32_t>& data) {
testNext<Reader>(data, list); testNext<Reader>(data, list);
testSkip<Reader>(data, list); testSkip<Reader>(data, list);
testSkipTo<Reader>(data, list); testSkipTo<Reader>(data, list);
testGoTo<Reader>(data, list); testJump<Reader>(data, list);
testJumpTo<Reader>(data, list);
list.free(); list.free();
} }
...@@ -214,8 +250,7 @@ void bmNext(const List& list, const std::vector<uint32_t>& data, ...@@ -214,8 +250,7 @@ void bmNext(const List& list, const std::vector<uint32_t>& data,
for (size_t i = 0, j; i < iters; ) { for (size_t i = 0, j; i < iters; ) {
Reader reader(list); Reader reader(list);
for (j = 0; reader.next(); ++j, ++i) { for (j = 0; reader.next(); ++j, ++i) {
const uint32_t value = reader.value(); CHECK_EQ(reader.value(), data[j]) << j;
CHECK_EQ(value, data[j]) << j;
} }
} }
} }
...@@ -230,8 +265,7 @@ void bmSkip(const List& list, const std::vector<uint32_t>& data, ...@@ -230,8 +265,7 @@ void bmSkip(const List& list, const std::vector<uint32_t>& data,
Reader reader(list); Reader reader(list);
for (j = skip - 1; j < data.size(); j += skip, ++i) { for (j = skip - 1; j < data.size(); j += skip, ++i) {
reader.skip(skip); reader.skip(skip);
const uint32_t value = reader.value(); CHECK_EQ(reader.value(), data[j]);
CHECK_EQ(value, data[j]);
} }
} }
} }
...@@ -246,14 +280,13 @@ void bmSkipTo(const List& list, const std::vector<uint32_t>& data, ...@@ -246,14 +280,13 @@ void bmSkipTo(const List& list, const std::vector<uint32_t>& data,
Reader reader(list); Reader reader(list);
for (j = 0; j < data.size(); j += skip, ++i) { for (j = 0; j < data.size(); j += skip, ++i) {
reader.skipTo(data[j]); reader.skipTo(data[j]);
const uint32_t value = reader.value(); CHECK_EQ(reader.value(), data[j]);
CHECK_EQ(value, data[j]);
} }
} }
} }
template <class Reader, class List> template <class Reader, class List>
void bmGoTo(const List& list, const std::vector<uint32_t>& data, void bmJump(const List& list, const std::vector<uint32_t>& data,
const std::vector<size_t>& order, size_t iters) { const std::vector<size_t>& order, size_t iters) {
CHECK(!data.empty()); CHECK(!data.empty());
CHECK_EQ(data.size(), order.size()); CHECK_EQ(data.size(), order.size());
...@@ -261,9 +294,24 @@ void bmGoTo(const List& list, const std::vector<uint32_t>& data, ...@@ -261,9 +294,24 @@ void bmGoTo(const List& list, const std::vector<uint32_t>& data,
Reader reader(list); Reader reader(list);
for (size_t i = 0; i < iters; ) { for (size_t i = 0; i < iters; ) {
for (size_t j : order) { for (size_t j : order) {
reader.goTo(j + 1); reader.jump(j + 1);
const uint32_t value = reader.value(); CHECK_EQ(reader.value(), data[j]);
CHECK_EQ(value, data[j]); ++i;
}
}
}
template <class Reader, class List>
void bmJumpTo(const List& list, const std::vector<uint32_t>& data,
const std::vector<size_t>& order, size_t iters) {
CHECK(!data.empty());
CHECK_EQ(data.size(), order.size());
Reader reader(list);
for (size_t i = 0; i < iters; ) {
for (size_t j : order) {
reader.jumpTo(data[j]);
CHECK_EQ(reader.value(), data[j]);
++i; ++i;
} }
} }
......
...@@ -131,16 +131,16 @@ BENCHMARK(Skip1000_ForwardQ128_1M) { ...@@ -131,16 +131,16 @@ BENCHMARK(Skip1000_ForwardQ128_1M) {
bmSkip<bm::Reader>(bm::list, bm::data, 1000, bm::k1M); bmSkip<bm::Reader>(bm::list, bm::data, 1000, bm::k1M);
} }
BENCHMARK(GoTo_ForwardQ128_1M) { BENCHMARK(Jump_ForwardQ128_1M) {
bmGoTo<bm::Reader>(bm::list, bm::data, bm::order, bm::k1M); bmJump<bm::Reader>(bm::list, bm::data, bm::order, bm::k1M);
} }
BENCHMARK_DRAW_LINE();
BENCHMARK(SkipTo1_SkipQ128_1M) { BENCHMARK(SkipTo1_SkipQ128_1M) {
bmSkipTo<bm::Reader>(bm::list, bm::data, 1, bm::k1M); bmSkipTo<bm::Reader>(bm::list, bm::data, 1, bm::k1M);
} }
BENCHMARK_DRAW_LINE();
BENCHMARK(SkipTo10_SkipQ128_1M) { BENCHMARK(SkipTo10_SkipQ128_1M) {
bmSkipTo<bm::Reader>(bm::list, bm::data, 10, bm::k1M); bmSkipTo<bm::Reader>(bm::list, bm::data, 10, bm::k1M);
} }
...@@ -153,6 +153,10 @@ BENCHMARK(SkipTo1000_SkipQ128_1M) { ...@@ -153,6 +153,10 @@ BENCHMARK(SkipTo1000_SkipQ128_1M) {
bmSkipTo<bm::Reader>(bm::list, bm::data, 1000, bm::k1M); bmSkipTo<bm::Reader>(bm::list, bm::data, 1000, bm::k1M);
} }
BENCHMARK(JumpTo_SkipQ128_1M) {
bmJumpTo<bm::Reader>(bm::list, bm::data, bm::order, bm::k1M);
}
BENCHMARK_DRAW_LINE(); BENCHMARK_DRAW_LINE();
BENCHMARK(Encode_10) { BENCHMARK(Encode_10) {
...@@ -173,20 +177,21 @@ Intel(R) Xeon(R) CPU E5-2660 @ 2.7GHz (turbo on), using instructions::Fast. ...@@ -173,20 +177,21 @@ Intel(R) Xeon(R) CPU E5-2660 @ 2.7GHz (turbo on), using instructions::Fast.
============================================================================ ============================================================================
folly/experimental/test/EliasFanoCodingTest.cpp relative time/iter iters/s folly/experimental/test/EliasFanoCodingTest.cpp relative time/iter iters/s
============================================================================ ============================================================================
Next_1M 4.86ms 205.97 Next_1M 4.61ms 216.70
Skip1_ForwarQ128_1M 5.17ms 193.36 Skip1_ForwarQ128_1M 5.33ms 187.71
Skip10_ForwarQ128_1M 13.69ms 73.03 Skip10_ForwarQ128_1M 14.23ms 70.26
Skip100_ForwardQ128_1M 26.76ms 37.37 Skip100_ForwardQ128_1M 29.10ms 34.37
Skip1000_ForwardQ128_1M 20.66ms 48.40 Skip1000_ForwardQ128_1M 21.15ms 47.28
GoTo_ForwardQ128_1M 43.75ms 22.86 Jump_ForwardQ128_1M 46.30ms 21.60
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
SkipTo1_SkipQ128_1M 9.74ms 102.70 SkipTo1_SkipQ128_1M 12.03ms 83.15
SkipTo10_SkipQ128_1M 30.62ms 32.66 SkipTo10_SkipQ128_1M 36.11ms 27.69
SkipTo100_SkipQ128_1M 37.70ms 26.53 SkipTo100_SkipQ128_1M 42.91ms 23.30
SkipTo1000_SkipQ128_1M 31.14ms 32.11 SkipTo1000_SkipQ128_1M 36.92ms 27.08
JumpTo_SkipQ128_1M 78.51ms 12.74
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
Encode_10 208.16ns 4.80M Encode_10 199.19ns 5.02M
Encode_1M 8.90ms 112.37 Encode_1M 8.82ms 113.37
============================================================================ ============================================================================
#endif #endif
......
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