Commit 66c782bb authored by Giuseppe Ottaviano's avatar Giuseppe Ottaviano Committed by Facebook Github Bot

Improve performance of enumerate() with optimization disabled

Reviewed By: yfeldblum, WillerZ

Differential Revision: D6682606

fbshipit-source-id: 5a203a849e96d3020cf9ad2669451122954c2199
parent 80d1c0e1
/* /*
* Copyright 2017 Facebook, Inc. * Copyright 2017-present Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <folly/CPortability.h>
#include <folly/portability/SysTypes.h> #include <folly/portability/SysTypes.h>
/** /**
...@@ -64,11 +65,13 @@ struct MakeConst<T*> { ...@@ -64,11 +65,13 @@ struct MakeConst<T*> {
// second overload will be SFINAEd out in that case. Otherwise, the // second overload will be SFINAEd out in that case. Otherwise, the
// second is preferred in the partial order for getPointer(_, 0). // second is preferred in the partial order for getPointer(_, 0).
template <class Iterator> template <class Iterator>
auto getPointer(const Iterator& it, long) -> decltype(std::addressof(*it)) { FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, long)
-> decltype(std::addressof(*it)) {
return std::addressof(*it); return std::addressof(*it);
} }
template <class Iterator> template <class Iterator>
auto getPointer(const Iterator& it, int) -> decltype(it.operator->()) { FOLLY_ALWAYS_INLINE auto getPointer(const Iterator& it, int)
-> decltype(it.operator->()) {
return it.operator->(); return it.operator->();
} }
...@@ -85,21 +88,22 @@ class Enumerator { ...@@ -85,21 +88,22 @@ class Enumerator {
using pointer = typename std::iterator_traits<Iterator>::pointer; using pointer = typename std::iterator_traits<Iterator>::pointer;
using iterator_category = std::input_iterator_tag; using iterator_category = std::input_iterator_tag;
explicit Proxy(const Enumerator* e) : it_(e->it_), index(e->idx_) {} FOLLY_ALWAYS_INLINE explicit Proxy(const Enumerator& e)
: it_(e.it_), index(e.idx_) {}
// Non-const Proxy: Forward constness from Iterator. // Non-const Proxy: Forward constness from Iterator.
reference operator*() { FOLLY_ALWAYS_INLINE reference operator*() {
return *it_; return *it_;
} }
pointer operator->() { FOLLY_ALWAYS_INLINE pointer operator->() {
return getPointer(it_, 0); return getPointer(it_, 0);
} }
// Const Proxy: Force const references. // Const Proxy: Force const references.
typename MakeConst<reference>::type operator*() const { FOLLY_ALWAYS_INLINE typename MakeConst<reference>::type operator*() const {
return *it_; return *it_;
} }
typename MakeConst<pointer>::type operator->() const { FOLLY_ALWAYS_INLINE typename MakeConst<pointer>::type operator->() const {
return getPointer(it_, 0); return getPointer(it_, 0);
} }
...@@ -110,24 +114,24 @@ class Enumerator { ...@@ -110,24 +114,24 @@ class Enumerator {
const size_t index; const size_t index;
}; };
Proxy operator*() const { FOLLY_ALWAYS_INLINE Proxy operator*() const {
return Proxy(this); return Proxy(*this);
} }
Enumerator& operator++() { FOLLY_ALWAYS_INLINE Enumerator& operator++() {
++it_; ++it_;
++idx_; ++idx_;
return *this; return *this;
} }
template <typename OtherIterator> template <typename OtherIterator>
bool operator==(const Enumerator<OtherIterator>& rhs) { FOLLY_ALWAYS_INLINE bool operator==(const Enumerator<OtherIterator>& rhs) {
return it_ == rhs.it_; return it_ == rhs.it_;
} }
template <typename OtherIterator> template <typename OtherIterator>
bool operator!=(const Enumerator<OtherIterator>& rhs) { FOLLY_ALWAYS_INLINE bool operator!=(const Enumerator<OtherIterator>& rhs) {
return !(*this == rhs); return !(it_ == rhs.it_);
} }
private: private:
......
/* /*
* Copyright 2017 Facebook, Inc. * Copyright 2017-present Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -121,9 +121,9 @@ FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index); ...@@ -121,9 +121,9 @@ FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index);
} // namespace folly } // namespace folly
/** /**
* Everything below this is deprecated. Use the folly::for_each algorithm above * Everything below is deprecated.
* instead
*/ */
/* /*
* Form a local variable name from "FOR_EACH_" x __LINE__, so that * Form a local variable name from "FOR_EACH_" x __LINE__, so that
* FOR_EACH can be nested without creating shadowed declarations. * FOR_EACH can be nested without creating shadowed declarations.
...@@ -159,9 +159,9 @@ FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index); ...@@ -159,9 +159,9 @@ FOLLY_CPP14_CONSTEXPR decltype(auto) fetch(Sequence&& sequence, Index&& index);
i != _FE_ANON(s2_).rend(); ++i) i != _FE_ANON(s2_).rend(); ++i)
/* /*
* If you just want the element values, please use this (ranges-v3) construct: * If you just want the element values, please use this construct:
* *
* for (auto&& element : collection | view::zip(view::ints)) * for (auto&& element : folly::enumerate(collection))
* *
* If you need access to the iterators please write an explicit iterator loop * If you need access to the iterators please write an explicit iterator loop
* and use a counter variable * and use a counter variable
......
/* /*
* Copyright 2017 Facebook, Inc. * Copyright 2017-present Facebook, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -14,12 +14,14 @@ ...@@ -14,12 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
#include <folly/container/Foreach.h> #include <algorithm>
#include <map>
#include <folly/Benchmark.h> #include <folly/Benchmark.h>
#include <folly/portability/GTest.h> #include <folly/Random.h>
#include <folly/container/Enumerate.h>
#include <map> #include <folly/container/Foreach.h>
#include <folly/init/Init.h>
using namespace folly; using namespace folly;
using namespace folly::detail; using namespace folly::detail;
...@@ -31,10 +33,14 @@ using namespace folly::detail; ...@@ -31,10 +33,14 @@ using namespace folly::detail;
// iter->second as is, without assigning to local variables. // iter->second as is, without assigning to local variables.
// 3. Use FOR_EACH_KV loop to iterate through the map. // 3. Use FOR_EACH_KV loop to iterate through the map.
std::map<int, std::string> bmMap; // For use in benchmarks below. // For use in benchmarks below.
std::map<int, std::string> bmMap;
std::vector<int> vec_one; std::vector<int> vec_one;
std::vector<int> vec_two; std::vector<int> vec_two;
// Smallest type to isolate iteration overhead.
std::vector<char> vec_char;
void setupBenchmark(size_t iters) { void setupBenchmark(size_t iters) {
bmMap.clear(); bmMap.clear();
for (size_t i = 0; i < iters; ++i) { for (size_t i = 0; i < iters; ++i) {
...@@ -47,6 +53,12 @@ void setupBenchmark(size_t iters) { ...@@ -47,6 +53,12 @@ void setupBenchmark(size_t iters) {
vec_two.resize(iters); vec_two.resize(iters);
} }
void setupCharVecBenchmark(size_t iters) {
vec_char.resize(iters);
std::generate(
vec_char.begin(), vec_char.end(), [] { return Random::rand32(128); });
}
BENCHMARK(ForEachFunctionNoAssign, iters) { BENCHMARK(ForEachFunctionNoAssign, iters) {
BenchmarkSuspender suspender; BenchmarkSuspender suspender;
...@@ -330,13 +342,61 @@ BENCHMARK(ForEachRangeR, iters) { ...@@ -330,13 +342,61 @@ BENCHMARK(ForEachRangeR, iters) {
doNotOptimizeAway(sum); doNotOptimizeAway(sum);
} }
int main(int argc, char** argv) { BENCHMARK(CharVecForRange, iters) {
testing::InitGoogleTest(&argc, argv); BENCHMARK_SUSPEND {
gflags::ParseCommandLineFlags(&argc, &argv, true); setupCharVecBenchmark(iters);
auto r = RUN_ALL_TESTS(); }
if (r) { size_t sum = 0;
return r; for (auto& c : vec_char) {
sum += c;
}
doNotOptimizeAway(sum);
}
BENCHMARK(CharVecForRangeExplicitIndex, iters) {
BENCHMARK_SUSPEND {
setupCharVecBenchmark(iters);
}
size_t sum = 0;
size_t index = 0;
for (auto& c : vec_char) {
sum += c * index;
++index;
}
doNotOptimizeAway(sum);
}
BENCHMARK(CharVecForEach, iters) {
BENCHMARK_SUSPEND {
setupCharVecBenchmark(iters);
}
size_t sum = 0;
folly::for_each(vec_char, [&](auto& c) { sum += c; });
doNotOptimizeAway(sum);
}
BENCHMARK(CharVecForEachIndex, iters) {
BENCHMARK_SUSPEND {
setupCharVecBenchmark(iters);
}
size_t sum = 0;
folly::for_each(vec_char, [&](auto& c, auto index) { sum += c * index; });
doNotOptimizeAway(sum);
}
BENCHMARK(CharVecForRangeEnumerate, iters) {
BENCHMARK_SUSPEND {
setupCharVecBenchmark(iters);
}
size_t sum = 0;
for (auto it : enumerate(vec_char)) {
sum += *it * it.index;
} }
doNotOptimizeAway(sum);
}
int main(int argc, char** argv) {
folly::init(&argc, &argv);
runBenchmarks(); runBenchmarks();
return 0; return 0;
} }
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