Я рассматриваю возможность использования 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-ядерном MacBook Pro i7 с флагом -O3.
#include <range/v3/all.hpp>
#include <benchmark/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 = ranges::to<std::vector<double>>(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
view
s ленивы и поэтому не могут извлечь выгоду из векторизованных инструкций процессора, но я могу ошибаться. РЕДАКТИРОВАТЬ: @alfC - очень хороший звонок. Я предлагаю вам провести несколько тестов и, возможно, опубликовать ответ, если окажется, что это узкое место - person Fureeish   schedule 08.07.2019view
являются ленивыми и генерируют одно значение за один вызов*
, что означает, что значения не могут получить выгоду от векторизованных операций, поскольку одновременно присутствует не более одного значения. С другой стороны, я считаю, что если диапазоны принимаются, компиляторы могут (и должны) быть изменены, чтобы генерировать для них эффективный код. На данный момент я думаю, что они просто недостаточно умны, учитывая тот факт, что концентрат является новым дляC++
, что на самом деле довольно досадно. Кстати, какое задание? Я думаю, вы могли неправильно понять некоторые вещи. - person Fureeish   schedule 08.07.2019benchmark.h
? - person einpoklum   schedule 08.07.2019int
(например, строках или других объектах, которые выделяют память). - person Nicol Bolas   schedule 08.07.2019int
. Как видно из результата, разница между range-v3 и STD сохраняется. - person Enzo   schedule 10.07.2019ranges::to<std::vector<double>>(rng1)
создать временный вектор. Из godbolt.org/z/1dE8oxrME при использовании функцииto
выделяется ненужная память. Пожалуйста, просто используйте некоторые вещи, напримерstd::copy
илиranges::copy
, чтобы снова проверить производительность. Я не устанавливаю тест Google в руки. Согласно godbolt.org/z/1dE8oxrME, rangev3 может генерировать хорошие (но не самые оптимальные) инструкции. - person HarryLeong   schedule 14.07.2021