Я рассматриваю возможность использования rangev3 в моей библиотеке. Мне нравится синтаксис rangev3, но приоритет - производительность. Библиотека выполняет множество векторных умножений и сложений, в основном длиной 128 сэмплов. Я использовал тест Google, чтобы оценить, например, сложение двух векторов. Версия диапазонов намного медленнее, чем версия STD (почти в 10 раз медленнее для коротких векторов). Это несколько удивительно, поскольку rangev3 (и будущие std :: range в C ++ 20) часто претендуют на хорошую производительность.
Есть ли проблема с тем, как я здесь использую rangev3? Или это как-то связано с тем, что компилятор не может хорошо развернуть код rangev3? Или увеличение производительности rangev3 проявляется только для многих последовательных операций?
Примечания: назначение output = rng1;
не должно выделять память, поскольку длина вектора одинакова (я пытался использовать range :: copy, но она становится в 100 раз медленнее). Я пытался предварительно инициализировать и рандомизировать векторы A и B, но не увидел никакой разницы. Я заметил, что если бы у меня было больше операций в конвейере, разрыв между STL и ragesv3 сузился, но только для длинных векторов (выше 32000 для 5 последовательных операций).
Ниже приведен отдельный пример с показателями производительности. Я запускаю C ++ 17 LLVM libc ++ на 4-ядерном i7 MacBook Pro с флагом -O3.
#include <range/v3/all.hpp>
#include "benchmark.h"
static void AddBenchmark(benchmark::State& state) {
const size_t length = state.range(0);
std::vector<double> B(length);
std::vector<double> A(length);
std::vector<double> output(length);
while (state.KeepRunning()) {
std::transform(A.begin( ), A.end( ), B.begin( ), output.begin(), std::plus<>( ));
benchmark::ClobberMemory(); // Force output to be written to memory.
}
}
BENCHMARK(AddBenchmark)->RangeMultiplier(8)->Range(1<<7, 1<<20);
static void AddRangesBenchmark(benchmark::State& state) {
const size_t length = state.range(0);
std::vector<double> B(length);
std::vector<double> A(length);
std::vector<double> output(length);
while (state.KeepRunning()) {
auto rng1 = ranges::view::transform(A, B, std::plus<>( ));
output = rng1;
benchmark::ClobberMemory(); // Force output to be written to memory.
}
}
BENCHMARK(AddRangesBenchmark)->RangeMultiplier(8)->Range(1<<7, 1<<20);
BENCHMARK_MAIN();
который выводит
AddBenchmark/128 30.3 ns 30.2 ns 23194091
AddBenchmark/512 121 ns 121 ns 5758094
AddBenchmark/4096 1917 ns 1906 ns 417300
AddBenchmark/32768 25054 ns 24795 ns 28182
AddBenchmark/262144 385913 ns 382803 ns 1718
AddBenchmark/1048576 2100095 ns 2096442 ns 328
AddRangesBenchmark/128 218 ns 218 ns 3131249
AddRangesBenchmark/512 579 ns 579 ns 1169688
AddRangesBenchmark/4096 5071 ns 5069 ns 123231
AddRangesBenchmark/32768 50702 ns 50649 ns 14382
AddRangesBenchmark/262144 482216 ns 481333 ns 1288
AddRangesBenchmark/1048576 3349331 ns 3347475 ns 200