为什么 std::none_of 比手动循环快?
Why is std::none_of faster than a hand rolled loop?
我使用 i) for
循环,ii) 基于范围的 for
循环和 iii) 迭代器,针对三种不同的手动实现对 std::none_of
的性能进行了基准测试。令我惊讶的是,我发现虽然所有三个手动实现花费的时间大致相同,但 std::none_of
明显更快。我的问题是 - 为什么会这样?
我使用了 Google 基准库并用 -std=c++14 -O3
编译。 运行 测试时,我将进程的亲和性限制为单个处理器。我使用 GCC 6.2 得到以下结果:
Benchmark Time CPU Iterations
--------------------------------------------------------
benchmarkSTL 28813 ns 28780 ns 24283
benchmarkManual 46203 ns 46191 ns 15063
benchmarkRange 48368 ns 48243 ns 16245
benchmarkIterator 44732 ns 44710 ns 15698
在 Clang 3.9 上,std::none_of
也比手动 for
循环更快,尽管速度差异较小。下面是测试代码(为简洁起见,只包括手册 for 循环):
#include <algorithm>
#include <array>
#include <benchmark/benchmark.h>
#include <functional>
#include <random>
const size_t N = 100000;
const unsigned value = 31415926;
template<size_t N>
std::array<unsigned, N> generateData() {
std::mt19937 randomEngine(0);
std::array<unsigned, N> data;
std::generate(data.begin(), data.end(), randomEngine);
return data;
}
void benchmarkSTL(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = std::none_of(
data.begin(),
data.end(),
std::bind(std::equal_to<unsigned>(), std::placeholders::_1, value));
assert(result);
}
}
void benchmarkManual(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = true;
for (size_t i = 0; i < N; i++) {
if (data[i] == value) {
result = false;
break;
}
}
assert(result);
}
}
BENCHMARK(benchmarkSTL);
BENCHMARK(benchmarkManual);
BENCHMARK_MAIN();
请注意,使用随机数生成器生成数据是无关紧要的。将第 i
个元素设置为 i
并检查是否包含值 N + 1
时,我得到相同的结果。
经过更多调查,我将尝试回答我自己的问题。正如 Kerrek SB 所建议的,我查看了生成的汇编代码。最重要的是,与其他三个版本相比,GCC 6.2 在展开 std::none_of
中隐含的循环方面做得更好。
海湾合作委员会 6.2:
std::none_of
展开 4 次 -> ~30µs
- 手动
for
、范围 for
和迭代器根本没有展开 -> ~45µs
正如 Corristo 所建议的,结果依赖于编译器——这非常有道理。 Clang 3.9 展开了除范围 for
循环之外的所有内容,尽管程度不同。
铿锵声 3.9
- `std::none_of' 展开 8 次 -> ~30µs
- 手动
for
展开 5 次 -> ~35µs
- 范围
for
根本没有展开 -> ~60µs
- 迭代器展开 8 次 -> ~28µs
所有代码都是用-std=c++14 -O3
编译的。
我使用 i) for
循环,ii) 基于范围的 for
循环和 iii) 迭代器,针对三种不同的手动实现对 std::none_of
的性能进行了基准测试。令我惊讶的是,我发现虽然所有三个手动实现花费的时间大致相同,但 std::none_of
明显更快。我的问题是 - 为什么会这样?
我使用了 Google 基准库并用 -std=c++14 -O3
编译。 运行 测试时,我将进程的亲和性限制为单个处理器。我使用 GCC 6.2 得到以下结果:
Benchmark Time CPU Iterations
--------------------------------------------------------
benchmarkSTL 28813 ns 28780 ns 24283
benchmarkManual 46203 ns 46191 ns 15063
benchmarkRange 48368 ns 48243 ns 16245
benchmarkIterator 44732 ns 44710 ns 15698
在 Clang 3.9 上,std::none_of
也比手动 for
循环更快,尽管速度差异较小。下面是测试代码(为简洁起见,只包括手册 for 循环):
#include <algorithm>
#include <array>
#include <benchmark/benchmark.h>
#include <functional>
#include <random>
const size_t N = 100000;
const unsigned value = 31415926;
template<size_t N>
std::array<unsigned, N> generateData() {
std::mt19937 randomEngine(0);
std::array<unsigned, N> data;
std::generate(data.begin(), data.end(), randomEngine);
return data;
}
void benchmarkSTL(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = std::none_of(
data.begin(),
data.end(),
std::bind(std::equal_to<unsigned>(), std::placeholders::_1, value));
assert(result);
}
}
void benchmarkManual(benchmark::State & state) {
auto data = generateData<N>();
while (state.KeepRunning()) {
bool result = true;
for (size_t i = 0; i < N; i++) {
if (data[i] == value) {
result = false;
break;
}
}
assert(result);
}
}
BENCHMARK(benchmarkSTL);
BENCHMARK(benchmarkManual);
BENCHMARK_MAIN();
请注意,使用随机数生成器生成数据是无关紧要的。将第 i
个元素设置为 i
并检查是否包含值 N + 1
时,我得到相同的结果。
经过更多调查,我将尝试回答我自己的问题。正如 Kerrek SB 所建议的,我查看了生成的汇编代码。最重要的是,与其他三个版本相比,GCC 6.2 在展开 std::none_of
中隐含的循环方面做得更好。
海湾合作委员会 6.2:
std::none_of
展开 4 次 -> ~30µs- 手动
for
、范围for
和迭代器根本没有展开 -> ~45µs
正如 Corristo 所建议的,结果依赖于编译器——这非常有道理。 Clang 3.9 展开了除范围 for
循环之外的所有内容,尽管程度不同。
铿锵声 3.9
- `std::none_of' 展开 8 次 -> ~30µs
- 手动
for
展开 5 次 -> ~35µs - 范围
for
根本没有展开 -> ~60µs - 迭代器展开 8 次 -> ~28µs
所有代码都是用-std=c++14 -O3
编译的。