Эта функция вызывается, и мы вызываем функцию process для каждого элемента возвращаемого вектора.
Я пытался используйте std::ranges, чтобы оптимизировать создание этого вектора и вместо этого вернуть представление.
Исходный код:
auto getData() {
std::vector v;
for (T1 p: myData) {
if (filter(p))
v.push_back(transform(p));
}
return v;
}
Обновление-1: изменено для использования возврата лямбды.
Возвращаемая лямбда принимает вызываемый объект и вызывает его для каждого элемента. Фактически аналогично отложенному просмотру.
Это превосходит исходный код в 2,5 раза.
auto getDataCallback() {
return [this](auto func) {
for (T1 p: myData) {
if (filter(p))
func(transform(p));
}
};
}
Обновление 2: возвращает std::view вместо вектора
auto dataView() {
return myData | ranges::views::filter(filter) | ranges::views::transform([this](auto t) { return transform(t); });
}
Я ожидал, что это будет работать так же, как и Обновление-1.
Но тесты показывают, что оно в 1,7 раза медленнее Обновления-1.
Почему подход на основе std::view в 1,7 раза медленнее, чем подход на основе лямбда?
Вот ссылка на тест: https://quick-bench.com/q/3QMVqBVFU0ma7EN3q9_ADnIA_IM

Я ожидаю почти такое же время выполнения для 2-е и 3-е решения, но не уверен, что делаю здесь что-то не так.
Вот полный код:
#include
#include
#include
static inline constexpr size_t million = 1000; //1000000;
static inline size_t sum = 0;
using T1 = int;
using T2 = std::string;
bool filter(T1 t) { return 0 == t % 2; }
void process(const T2 &p) { sum += p.size(); };
T2 transform(T1 x) { return std::to_string(x); }
static inline std::vector data;
namespace Conventional {
class MyClass {
std::vector& myData = data;
T2 transform(T1 x) { return std::to_string(x); }
public:
auto getData() {
std::vector v;
for (T1 p: myData) {
if (filter(p))
v.push_back(transform(p));
}
return v;
}
};
void myFunc() {
sum = 0;
MyClass obj;
auto data = obj.getData();
std::for_each(data.begin(), data.end(), process);
}
};
namespace LazyCpp17 {
class MyClass {
std::vector& myData = data;
T2 transform(T1 x) { return std::to_string(x); }
public:
auto getDataCallback() {
return [this](auto func) {
for (T1 p: myData) {
if (filter(p))
func(transform(p));
}
};
}
};
void myFunc() {
sum = 0;
MyClass obj;
obj.getDataCallback()(process);
}
};
namespace LazyCpp20 {
class MyClass {
std::vector& myData = data;
T2 transform(T1 x) { return std::to_string(x); }
public:
auto dataView() {
return myData | std::views::filter(filter) | std::views::transform([this](auto t) { return transform(t); });
}
};
void myFunc() {
sum = 0;
MyClass obj;
std::ranges::for_each(obj.dataView(), process);
}
};
static void ConventionalB(benchmark::State& state) {
data.resize(million);
std::iota(data.begin(), data.end(), 0);
// Code inside this loop is measured repeatedly
for (auto _ : state) {
Conventional::myFunc();
// Make sure the variable is not optimized away by compiler
}
}
// Register the function as a benchmark
BENCHMARK(ConventionalB);
static void LazyCpp17B(benchmark::State& state) {
data.resize(million);
std::iota(data.begin(), data.end(), 0);
// Code before the loop is not measured
std::string x = "hello";
for (auto _ : state) {
LazyCpp17::myFunc();
}
}
BENCHMARK(LazyCpp17B);
static void LazyCpp20B(benchmark::State& state) {
data.resize(million);
std::iota(data.begin(), data.end(), 0);
// Code before the loop is not measured
std::string x = "hello";
for (auto _ : state) {
LazyCpp20::myFunc();
}
}
BENCHMARK(LazyCpp20B);
Подробнее здесь: https://stackoverflow.com/questions/790 ... c20-ranges