基础介绍
reuduce的含义是规约,即将一系列元素通过某种操作组合成单个结果的过程。c++17引入了std::reduce函数。std::reduce与std::accumulate类似,不同的是std::reduce采用的并行方式进行运算,所谓并行就是在多个线程同时进行运算得到的结果的过程;std::accumulate是串行方式进行得到一个累积和。所以从这个角度来看,当数据量比较小时优先采用std::accumulate完成求和运算。当数据量比较大时,推荐采用std::reduce来完成计算。
std::reduce函数特点
- 默认是求和运算,初始值为0
- 可以指定初始值
- 可以指定运算方法
- 可以指定计算策略
依赖的头文件:#include <numeric>
函数原型
最简单的形式
template<class InputIt>
typename iterator_traits<InputIt>::value_type
reduce(InputIt first, InputIt last);
特征:
- 该重载函数仅有2个参数,这两个参数一般为容器迭代器
- 该重载函数的初始值为0
- 该重载函数的操作符为加法操作符(+)
示例:
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;
void example()
{
vector<int> vec{1,2,3};
int sum = std::reduce(vec.begin(), vec.end());
cout <<"sum = " <<sum<<endl; //输出为6 1+2+3=6
}
带初始值版本
template<class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);
特征:
- 在前面简单版本的基础上多了一个参数初始值,其余与简单版本相同:初始值的含义就是提供一个起始值,简单版本的初始值是0,请看下面的例子
示例:
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;
void example()
{
vector<int> vec{1,2,3};
int sum = std::reduce(vec.begin(), vec.end(), 5);
cout <<"sum = " <<sum<<endl; //输出为11,5+1+2+3=11
}
完整版本
template<class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp binary_op);
特征:
- 在带初始值版本的基础上,允许指定二元操作符,前面的版本默认都是加法操作符,而这个八本可以指定操作符,但是不能随意指定二元操作符,指定的二元操作符必须符合交换律和结合律。减法不满足交换律(a-b != b-a)
示例:
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;
void example()
{
vector<int> vec{1,2,3};
int result = std::reduce(vec.begin(), vec.end(), 5, std::multiplies<>());
cout <<"result = " <<result <<endl; //输出为30,5x1x2x3=30
}
执行策略重载版本
除上面外,还存在指定执行策略的重载版本,与前面类似,仅仅是在第一个参数指定执行策略。请看下面的函数原型:
//基本执行策略,除可以指定执行策略外,其余功能与简单版本相同
template<class ExecutionPolicy, class ForwardIt>
typename iterator_traits<ForwardIt>::value_type
reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last);
//带初始值的执行策略版本,其余功能与带初始值版本相同
template<class ExecutionPolicy, class ForwardIt, class T>
T reduce(ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last, T init);
//完整执行策略版本,相对于完整版本多了一个参数,该参数用于指定执行策略,其余相同
template<class ExecutionPolicy,
class ForwardIt, class T, class BinaryOp>
T reduce(ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last,
T init, BinaryOp binary_op);
执行策略种类如下:
// 可用的执行策略
auto sum1 = std::reduce(std::execution::seq, // 顺序执行
v.begin(), v.end());
auto sum2 = std::reduce(std::execution::par, // 并行执行
v.begin(), v.end());
auto sum3 = std::reduce(std::execution::par_unseq,// 并行+向量化
v.begin(), v.end());
高级用法
前一个章节的示例代码都是采用基本数据类型int,std::reduce也支持自定义类型的进行运算。请看下面的示例:
struct Number {
int value;
// 必须定义加法操作符
Number operator+(const Number& other) const {
return Number{value + other.value};
}
};
std::vector<Number> numbers = {{1}, {2}, {3}};
Number result = std::reduce(numbers.begin(), numbers.end(),
Number{0});
从上面的例子可以看出,自定义类型Number,容器中的元素都是Number类型,初始值也是Nmber{0},通过重载操作符+实现数据折叠。
应用场景分析
- 数值计算场景:大数据量的数值运算推荐使用std::reduce,例如向量点积运算
- 并行运算场景
- 科学计算运用:矩阵计算
总而言之,reduce函数的应用场景一般都是在大数据量的情况下的使用。