Commit e0122b40 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Split BitIterator test and bench

Summary: [Folly] Split BitIterator test and bench, improving the benchmark in some small ways, and fixing missing `std::iterator_traits` applications along the way.

Reviewed By: nbronson

Differential Revision: D8509129

fbshipit-source-id: 0fad0ce4d1c5abf76e33e04e98db36912e9cb651
parent ff9d6fee
......@@ -171,10 +171,10 @@ BitIterator<BaseIter> findFirstSet(
BitIterator<BaseIter> begin,
BitIterator<BaseIter> end) {
// shortcut to avoid ugly static_cast<>
static const typename BaseIter::value_type one = 1;
static const typename std::iterator_traits<BaseIter>::value_type one = 1;
while (begin.base() != end.base()) {
typename BaseIter::value_type v = *begin.base();
typename std::iterator_traits<BaseIter>::value_type v = *begin.base();
// mask out the bits that don't matter (< begin.bitOffset)
v &= ~((one << begin.bitOffset()) - 1);
size_t firstSet = findFirstSet(v);
......@@ -189,7 +189,7 @@ BitIterator<BaseIter> findFirstSet(
// now begin points to the same block as end
if (end.bitOffset() != 0) { // assume end is dereferenceable
typename BaseIter::value_type v = *begin.base();
typename std::iterator_traits<BaseIter>::value_type v = *begin.base();
// mask out the bits that don't matter (< begin.bitOffset)
v &= ~((one << begin.bitOffset()) - 1);
// mask out the bits that don't matter (>= end.bitOffset)
......
......@@ -71,8 +71,10 @@ class BitReference {
template <class BaseIter>
struct BitIteratorBase {
static_assert(std::is_integral<typename BaseIter::value_type>::value,
"BitIterator may only be used with integral types");
static_assert(
std::is_integral<
typename std::iterator_traits<BaseIter>::value_type>::value,
"BitIterator may only be used with integral types");
typedef boost::iterator_adaptor<
BitIterator<BaseIter>, // Derived
BaseIter, // Base
......
/*
* Copyright 2011-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/container/BitIterator.h>
#include <algorithm>
#include <glog/logging.h>
#include <folly/Benchmark.h>
#include <folly/init/Init.h>
#include <folly/small_vector.h>
using namespace folly;
namespace {
template <class BaseIter>
BitIterator<BaseIter> simpleFFS(
BitIterator<BaseIter> begin,
BitIterator<BaseIter> end) {
return std::find(begin, end, true);
}
template <class FFS>
void runFFSTest(FFS fn) {
constexpr size_t const maxblocks = 3;
constexpr size_t const bpb = 8 * sizeof(uint64_t);
for (size_t nblocks = 1; nblocks <= maxblocks; ++nblocks) {
small_vector<uint64_t, maxblocks> data(nblocks, 0);
size_t nbits = nblocks * bpb;
auto begin = makeBitIterator(data.cbegin());
auto end = makeBitIterator(data.cend());
DCHECK_EQ(nbits, end - begin);
DCHECK(begin != end);
// Try every possible combination of first bit set (including none),
// start bit, end bit
for (size_t firstSet = 0; firstSet <= nbits; ++firstSet) {
data.assign(nblocks, 0);
if (firstSet) {
size_t b = firstSet - 1;
data[b / bpb] |= (1ULL << (b % bpb));
}
for (size_t startBit = 0; startBit <= nbits; ++startBit) {
for (size_t endBit = startBit; endBit <= nbits; ++endBit) {
auto p = begin + startBit;
auto q = begin + endBit;
p = fn(p, q);
doNotOptimizeAway(p);
if (firstSet < startBit + 1 || firstSet >= endBit + 1) {
DCHECK_EQ(endBit, p - begin)
<< " firstSet=" << firstSet << " startBit=" << startBit
<< " endBit=" << endBit << " nblocks=" << nblocks;
} else {
DCHECK_EQ(firstSet - 1, p - begin)
<< " firstSet=" << firstSet << " startBit=" << startBit
<< " endBit=" << endBit << " nblocks=" << nblocks;
}
}
}
}
}
}
void runSimpleFFSTest(int iters) {
while (iters--) {
runFFSTest([](auto first, auto last) { return simpleFFS(first, last); });
}
}
void runRealFFSTest(int iters) {
while (iters--) {
runFFSTest([](auto first, auto last) { return findFirstSet(first, last); });
}
}
} // namespace
BENCHMARK(SimpleFFSTest, iters) {
runSimpleFFSTest(iters);
}
BENCHMARK(RealFFSTest, iters) {
runRealFFSTest(iters);
}
/* --bm_min_iters=10 --bm_max_iters=100
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
runSimpleFFSTest 10 4.82 s 482 ms 2.075
runRealFFSTest 19 2.011 s 105.9 ms 9.447
*/
int main(int argc, char** argv) {
folly::init(&argc, &argv);
folly::runBenchmarks();
return 0;
}
......@@ -16,13 +16,10 @@
#include <folly/container/BitIterator.h>
#include <algorithm>
#include <limits>
#include <type_traits>
#include <vector>
#include <folly/Benchmark.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
using namespace folly;
......@@ -87,101 +84,3 @@ TEST(BitIterator, Const) {
checkIt(0x10, bi);
checkIt(0x42, bi);
}
namespace {
template <class BaseIter>
BitIterator<BaseIter> simpleFFS(BitIterator<BaseIter> begin,
BitIterator<BaseIter> end) {
return std::find(begin, end, true);
}
template <class FFS>
void runFFSTest(FFS fn) {
static const size_t bpb = 8 * sizeof(uint64_t);
std::vector<uint64_t> data;
for (size_t nblocks = 1; nblocks <= 3; ++nblocks) {
size_t nbits = nblocks * bpb;
data.resize(nblocks);
auto begin = makeBitIterator(data.cbegin());
auto end = makeBitIterator(data.cend());
EXPECT_EQ(nbits, end - begin);
EXPECT_FALSE(begin == end);
// Try every possible combination of first bit set (including none),
// start bit, end bit
for (size_t firstSet = 0; firstSet <= nbits; ++firstSet) {
data.assign(nblocks, 0);
if (firstSet) {
size_t b = firstSet - 1;
data[b / bpb] |= (1ULL << (b % bpb));
}
for (size_t startBit = 0; startBit <= nbits; ++startBit) {
for (size_t endBit = startBit; endBit <= nbits; ++endBit) {
auto p = begin + startBit;
auto q = begin + endBit;
p = fn(p, q);
if (firstSet < startBit + 1 || firstSet >= endBit + 1) {
EXPECT_EQ(endBit, p - begin)
<< " firstSet=" << firstSet << " startBit=" << startBit
<< " endBit=" << endBit << " nblocks=" << nblocks;
} else {
EXPECT_EQ(firstSet - 1, p - begin)
<< " firstSet=" << firstSet << " startBit=" << startBit
<< " endBit=" << endBit << " nblocks=" << nblocks;
}
}
}
}
}
}
void runSimpleFFSTest(int iters) {
auto fn = simpleFFS<std::vector<uint64_t>::const_iterator>;
while (iters--) {
runFFSTest(fn);
}
}
void runRealFFSTest(int iters) {
auto fn = findFirstSet<std::vector<uint64_t>::const_iterator>;
while (iters--) {
runFFSTest(fn);
}
}
} // namespace
TEST(BitIterator, SimpleFindFirstSet) {
runSimpleFFSTest(1);
}
TEST(BitIterator, FindFirstSet) {
runRealFFSTest(1);
}
BENCHMARK(SimpleFFSTest, iters) {
runSimpleFFSTest(iters);
}
BENCHMARK(RealFFSTest, iters) {
runRealFFSTest(iters);
}
/* --bm_min_iters=10 --bm_max_iters=100
Benchmark Iters Total t t/iter iter/sec
------------------------------------------------------------------------------
runSimpleFFSTest 10 4.82 s 482 ms 2.075
runRealFFSTest 19 2.011 s 105.9 ms 9.447
*/
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
auto ret = RUN_ALL_TESTS();
if (!ret && FLAGS_benchmark) {
folly::runBenchmarks();
}
return ret;
}
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