我使用i)for
循環,ii)基於範圍的for
循環和iii)迭代器針對三種不同的手動實現對std::none_of
的性能進行基準測試。令我驚訝的是,我發現雖然所有三個手動實施大致相同,但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
在鐺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
時,我會得到相同的結果。
爲什麼不比較a)實現和b)生成的代碼? –
我不知道爲什麼在你的情況下,但由於庫是由編譯器實現提供的,他們能夠使用特定於平臺的技巧(例如分支預測提示?)來提供標準C++行爲。所以這個結果(如果有效的話)不會讓我感到驚訝。 – Galik
這似乎與您使用無符號整數的事實有關:編譯器可以假定對於有符號整數,從來沒有溢出(因爲它是根據標準的未定義行爲),並使用這個事實進行積極優化。顯然'std :: none_of'對無符號整數有特殊的含義,這是你在手寫函數中沒有得到的。如果你換成'long'而不是'size_t'和'unsigned',手動版本實際上會更快。 – Corristo