--- /dev/null
+/*
+ * Copyright 2017 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/Foreach.h>
+
+#include <folly/Benchmark.h>
+#include <folly/portability/GTest.h>
+
+#include <map>
+
+using namespace folly;
+using namespace folly::detail;
+
+// Benchmarks:
+// 1. Benchmark iterating through the man with FOR_EACH, and also assign
+// iter->first and iter->second to local vars inside the FOR_EACH loop.
+// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and
+// iter->second as is, without assigning to local variables.
+// 3. Use FOR_EACH_KV loop to iterate through the map.
+
+std::map<int, std::string> bmMap; // For use in benchmarks below.
+std::vector<int> vec_one;
+std::vector<int> vec_two;
+
+void setupBenchmark(size_t iters) {
+ bmMap.clear();
+ for (size_t i = 0; i < iters; ++i) {
+ bmMap[i] = "teststring";
+ }
+
+ vec_one.clear();
+ vec_two.clear();
+ vec_one.resize(iters);
+ vec_two.resize(iters);
+}
+
+BENCHMARK(ForEachFunctionNoAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ folly::for_each(bmMap, [&](auto& key_val_pair) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ });
+ doNotOptimizeAway(sumKeys);
+ });
+}
+
+BENCHMARK(StdForEachFunctionNoAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ });
+ doNotOptimizeAway(sumKeys);
+ });
+}
+
+BENCHMARK(RangeBasedForLoopNoAssign, iters) {
+ BenchmarkSuspender suspender;
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ for (auto& key_val_pair : bmMap) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ }
+ doNotOptimizeAway(sumKeys);
+ });
+}
+
+BENCHMARK(ManualLoopNoAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
+ sumKeys += iter->first;
+ sumValues += iter->second;
+ }
+ doNotOptimizeAway(sumKeys);
+ });
+}
+
+BENCHMARK(ForEachFunctionAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ folly::for_each(bmMap, [&](auto& key_val_pair) {
+ const int k = key_val_pair.first;
+ const std::string v = key_val_pair.second;
+ sumKeys += k;
+ sumValues += v;
+ });
+ });
+}
+
+BENCHMARK(StdForEachFunctionAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+ const int k = key_val_pair.first;
+ const std::string v = key_val_pair.second;
+ sumKeys += k;
+ sumValues += v;
+ });
+ });
+}
+
+BENCHMARK(RangeBasedForLoopAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ for (auto& key_val_pair : bmMap) {
+ const int k = key_val_pair.first;
+ const std::string v = key_val_pair.second;
+ sumKeys += k;
+ sumValues += v;
+ }
+ });
+}
+
+BENCHMARK(ManualLoopAssign, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
+ const int k = iter->first;
+ const std::string v = iter->second;
+ sumKeys += k;
+ sumValues += v;
+ }
+ });
+}
+
+BENCHMARK(ForEachFunctionNoAssignWithIndexManipulation, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ sumValues += index;
+ });
+ });
+}
+
+BENCHMARK(StdForEachFunctionNoAssignWithIndexManipulation, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ auto index = std::size_t{0};
+ std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ sumValues += index;
+ ++index;
+ });
+ });
+}
+
+BENCHMARK(RangeBasedForLoopNoAssignWithIndexManipulation, iters) {
+ BenchmarkSuspender suspender;
+
+ int sumKeys = 0;
+ std::string sumValues;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ auto index = std::size_t{0};
+ for (auto& key_val_pair : bmMap) {
+ sumKeys += key_val_pair.first;
+ sumValues += key_val_pair.second;
+ sumValues += index;
+ }
+ });
+}
+
+BENCHMARK(ForEachFunctionFetch, iters) {
+ BenchmarkSuspender suspender;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ folly::for_each(bmMap, [&](auto& key_val_pair, auto index) {
+ folly::fetch(vec_one, index) = key_val_pair.first;
+ });
+ });
+}
+
+BENCHMARK(StdForEachFunctionFetch, iters) {
+ BenchmarkSuspender suspender;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ auto index = std::size_t{0};
+ std::for_each(bmMap.begin(), bmMap.end(), [&](auto& key_val_pair) {
+ *(vec_one.begin() + index++) = key_val_pair.first;
+ });
+ });
+}
+
+BENCHMARK(ForLoopFetch, iters) {
+ BenchmarkSuspender suspender;
+ setupBenchmark(iters);
+
+ suspender.dismissing([&]() {
+ auto index = std::size_t{0};
+ for (auto& key_val_pair : bmMap) {
+ *(vec_one.begin() + index++) = key_val_pair.first;
+ }
+ });
+}
+
+BENCHMARK(ForEachKVNoMacroAssign, iters) {
+ int sumKeys = 0;
+ std::string sumValues;
+
+ BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+ FOR_EACH(iter, bmMap) {
+ const int k = iter->first;
+ const std::string v = iter->second;
+ sumKeys += k;
+ sumValues += v;
+ }
+}
+
+BENCHMARK(ForEachKVNoMacroNoAssign, iters) {
+ int sumKeys = 0;
+ std::string sumValues;
+
+ BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+ FOR_EACH(iter, bmMap) {
+ sumKeys += iter->first;
+ sumValues += iter->second;
+ }
+}
+
+BENCHMARK(ForEachKVMacro, iters) {
+ int sumKeys = 0;
+ std::string sumValues;
+
+ BENCHMARK_SUSPEND { setupBenchmark(iters); }
+
+ FOR_EACH_KV(k, v, bmMap) {
+ sumKeys += k;
+ sumValues += v;
+ }
+}
+
+BENCHMARK(ForEachManual, iters) {
+ int sum = 1;
+ for (size_t i = 1; i < iters; ++i) {
+ sum *= i;
+ }
+ doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachRange, iters) {
+ int sum = 1;
+ FOR_EACH_RANGE(i, 1, iters) { sum *= i; }
+ doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachDescendingManual, iters) {
+ int sum = 1;
+ for (size_t i = iters; i-- > 1;) {
+ sum *= i;
+ }
+ doNotOptimizeAway(sum);
+}
+
+BENCHMARK(ForEachRangeR, iters) {
+ int sum = 1;
+ FOR_EACH_RANGE_R(i, 1U, iters) { sum *= i; }
+ doNotOptimizeAway(sum);
+}
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ auto r = RUN_ALL_TESTS();
+ if (r) {
+ return r;
+ }
+ runBenchmarks();
+ return 0;
+}