为什么std :: none_of比手动循环更快?

时间:2016-10-02 17:36:16

标签: c++ stl-algorithm

我使用i)std::none_of循环,ii)基于范围的for循环和iii)迭代器,将for的性能与三种不同的手动实现进行基准测试。令我惊讶的是,我发现虽然所有三个手动实现大致相同,但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循环快。这是测试代码(仅包括用于简洁的循环手册):

#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 - th元素设置为i并检查是否包含值N + 1,我就会得到相同的结果。

1 个答案:

答案 0 :(得分:3)

经过一番调查后,我会尽力回答自己的问题。正如Kerrek SB所建议的,我查看了生成的汇编代码。最重要的是,与其他三个版本相比,GCC 6.2在展开std::none_of中隐含的循环方面做得更好。

GCC 6.2:

  • std::none_of展开4次 - &gt; 〜30微秒
  • 手动for,范围for和迭代器根本没有展开 - &gt; 〜45μs

根据Corristo的建议,结果是编译器依赖 - 这非常有意义。 Clang 3.9展开除for范围之外的所有循环,但程度不同。

Clang 3.9

  • `std :: none_of'展开8次 - &gt; 〜30微秒
  • 手动for展开5次 - &gt; 〜为35μs
  • 范围for根本没有展开 - &gt; 〜60μs的
  • 迭代器展开8次 - &gt; 〜28μs

所有代码都是使用-std=c++14 -O3编译的。