/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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 <algorithm>
#include <map>
#include <folly/Benchmark.h>
#include <folly/Random.h>
#include <folly/container/Enumerate.h>
#include <folly/container/Foreach.h>
#include <folly/init/Init.h>
using namespace folly;
// 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.
// For use in benchmarks below.
std::map<int, std::string> bmMap;
std::vector<int> vec_one;
std::vector<int> vec_two;
// Smallest type to isolate iteration overhead.
std::vector<char> vec_char;
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);
}
void setupCharVecBenchmark(size_t iters) {
vec_char.resize(iters);
std::generate(
vec_char.begin(), vec_char.end(), [] { return Random::rand32(128); });
}
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) {
[[maybe_unused]] 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) {
[[maybe_unused]] int sumKeys = 0;
std::string sumValues;
BENCHMARK_SUSPEND {
setupBenchmark(iters);
}
FOR_EACH (iter, bmMap) {
sumKeys += iter->first;
sumValues += iter->second;
}
}
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(CharVecForRange, iters) {
BENCHMARK_SUSPEND {
setupCharVecBenchmark(iters);
}
size_t sum = 0;
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 init(&argc, &argv);
runBenchmarks();
return 0;
}