【发布时间】:2017-06-07 13:02:44
【问题描述】:
我正在使用 Range-v3 库来执行美化的 find_if 并且很好奇为什么 google-benchmark 始终将我的 Range-v3 代码排名比我的 std::find_if 方法更差。 g++ 和 clang 都给出了与-O3 和#define NDEBUG 相同的模式。
我想到的具体示例是以下使用 STL:
std::vector<int> lengths(large_number, random_number);
auto const to_find = std::accumulate(lengths.begin(), lengths.end(), 0l) / 2;
auto accumulated_length = 0l;
auto found = std::find_if(lengths.begin(), lengths.end(), [&](auto const &val) {
accumulated_length += val;
return to_find < accumulated_length;
});
auto found_index = std::distance(lengths.begin(), found);
出于本说明的目的,这有些人为设计,但通常会有一个随机生成器用于to_find 变量和lengths 向量中的随机值。
使用 Range-v3 库,我得到以下代码
using namespace ranges;
std::vector<int> lengths(large_number, random_number);
auto const to_find = accumulate(lengths, 0l) / 2;
auto found_index = distance(lengths | view::partial_sum()
| view::take_while([=](auto const i) {
return i < to_find;
}));
我的问题是为什么 Range-v3 比 STL 实现慢。我知道这仍然是一个实验性库,但代码示例可能有问题还是我滥用了范围概念?
编辑
一个示例 google-bench 驱动程序(不确定是否正确)
#define NDEBUG
#include <numeric>
#include <vector>
#include <benchmark/benchmark.h>
#include <range/v3/all.hpp>
static void stl_search(benchmark::State &state) {
using namespace ranges;
std::vector<long> lengths(state.range(0), 1l);
auto const to_find = std::accumulate(lengths.begin(), lengths.end(), 0l) / 2;
while (state.KeepRunning()) {
auto accumulated_length = 0l;
auto const found = std::find_if(lengths.begin(), lengths.end(), [&](auto const& val) {
accumulated_length += val;
return to_find < accumulated_length;
});
volatile long val = std::distance(lengths.begin(), found);
}
state.SetBytesProcessed(int64_t(state.iterations()) *
int64_t(state.range(0)) * sizeof(long));
}
static void ranges_search(benchmark::State &state) {
using namespace ranges;
std::vector<long> lengths(state.range(0), 1l);
auto const to_find = accumulate(lengths, 0l) / 2;
while (state.KeepRunning())
{
volatile long val = distance(lengths | view::partial_sum()
| view::take_while([=](auto const& i) {
return i <= to_find;
}));
}
state.SetBytesProcessed(int64_t(state.iterations()) *
int64_t(state.range(0)) * sizeof(long));
}
BENCHMARK(ranges_search)->Range(8 << 8, 8 << 16);
BENCHMARK(stl_search)->Range(8 << 8, 8 << 16);
BENCHMARK_MAIN();
给予
ranges_search/2048 756 ns 756 ns 902091 20.1892GB/s
ranges_search/4096 1495 ns 1494 ns 466681 20.4285GB/s
ranges_search/32768 11872 ns 11863 ns 58902 20.5801GB/s
ranges_search/262144 94982 ns 94892 ns 7364 20.5825GB/s
ranges_search/524288 189870 ns 189691 ns 3688 20.5927GB/s
stl_search/2048 348 ns 348 ns 2000964 43.8336GB/s
stl_search/4096 690 ns 689 ns 1008295 44.2751GB/s
stl_search/32768 5497 ns 5492 ns 126097 44.452GB/s
stl_search/262144 44725 ns 44681 ns 15882 43.7122GB/s
stl_search/524288 91027 ns 90936 ns 7616 42.9563GB/s
使用 clang 4.0.1 和
ranges_search/2048 2309 ns 2307 ns 298507 6.61496GB/s
ranges_search/4096 4558 ns 4554 ns 154520 6.70161GB/s
ranges_search/32768 36482 ns 36454 ns 19191 6.69726GB/s
ranges_search/262144 287072 ns 286801 ns 2438 6.81004GB/s
ranges_search/524288 574230 ns 573665 ns 1209 6.80928GB/s
stl_search/2048 299 ns 298 ns 2340691 51.1437GB/s
stl_search/4096 592 ns 591 ns 1176783 51.6363GB/s
stl_search/32768 4692 ns 4689 ns 149460 52.0711GB/s
stl_search/262144 37718 ns 37679 ns 18611 51.8358GB/s
stl_search/524288 75247 ns 75173 ns 9244 51.9633GB/s
使用 gcc 6.3.1。我的机器有一个 Haswell 代处理器。两者都是用
编译和执行的g++ -Wall -O3 -std=c++14 Ranges.cpp -lbenchmark -lpthread && ./a.out
clang++ -Wall -O3 -std=c++14 Ranges.cpp -lbenchmark -lpthread && ./a.out
【问题讨论】:
-
请提供minimal reproducible example、具体的性能测量结果、编译器和系统信息。
-
只是一个观察:通过查看许多范围代码,它非常强大,但是所有 lambdas 返回 lambdas 返回 lambdas 等,它似乎并没有针对性能进行优化,而不是语法的易用性.
-
@AndyG 为什么会出现性能问题,lambdas 返回 lambdas? Lambda 不是外部链接的函数:它们不必“物理地”存在于可执行文件中。它们可以很容易地在模板代码中被省略,因为 Range-v3 是
-
@KABoissonneault:这是一个公平的观点。我想看看实际上省略了什么等等。另一方面,我记得 Eric Neibler 在 CPPCon 的演讲中谈到,由于缺乏语言支持,范围库倾向于生成比必要更多的副本。
标签: c++ performance range-v3