Husky中文文档-C++ Husky Aggregator 指南

Aggregator在list_executes过程中聚合数值。一个aggregator就像是整个集群中的一个全局变量。

使用介绍

  1. 头文件: #include "lib/dcaggregator.hpp"

  2. 创建一个aggregator: Husky::Aggregator<ValueType> agg(InitValue, [](ValueType & old, const ValueType & new) { ... });. 这里的 ValueType 是将被聚合的数值的数据类型。Aggregator的构造函数需要提供两个参数: InitValue 和一个 lambda. InitValue 是需要聚合的原始数据而lambda是表明要以什么样的方式来聚合(即聚合规则)。 Lambda 必须包含两个参数, 其一是ValueType的引用,另一个是它的const引用。在lambda函数体内,你需要指明旧的数据怎么变化获得新的数值。(注意:请在你的job函数中创建你的aggregators)

  3. 你可以使用aggregator的成员函数update:agg.updata(V)来聚合一个值。其中这里的V指的是一个'Valuetype'类型的值。

  4. 需要知道的是,仅仅使用updata的话只会在本地做聚合。你还需要一个全局的aggregation来得到最后的聚合值。然而在这之前,请确保你已经至少使用了一次list_execute运算。

  5. 最后你可以使用agg.get_value()来得到你最后的聚合值。(注意:成员函数'get_value()'返回的是一个线程共享的变量引用,这样如果其中的一个worker改变了这个引用值,其他workers就可能会受到影响。)

  6. [额外] Aggregator提供两种选择: 即AGG_RESET_EACH_ROUNDAGG_KEEP_AGGREGATE. (它们是代码中全局定义的const变量) AGG_RESET_EACH_ROUND 意味着, 在每次迭代中, 这个aggregator将在接受任何新的更新之前先重置它的值。而AGG_KEEP_AGGREGATE则意味着这个aggregator值是每次迭代更新后的aggregation。 在默认情况下,系统使用的是AGG_KEEP_AGGREGATE,但是我们可以通过使用`agg.set_agg_option(AGG_RESET_EACH_ROUND)' 来改变这个选项。

一个简单的例子

//0. 头文件
#include "lib/dcaggregator.hpp"

void job() {
  //1. 创建 aggregator
  Husky::Aggregator<int> sum(0, [](int & a, const int & b){ a += b; });

  //6. [可选] 重置 agg 选项
  sum.set_agg_option(AGG_RESET_EACH_ROUND);

  //3. 确保操作了一次 list_execute 以便进行全局的aggregation
  worker.list_execute(obj_list, [&](OBJ & obj) {
     //2. 更新 aggregator
     sum.update(1);
  });

  //4. 获得 aggregation 值
  Husky::log_msg(std::to_string(sum.get_value()));
}

高级用法

有时候你并不想让aggregator每一轮都保持更新,因为这样需要为聚合增加额外的通信。这里提供了一个交换器可以让你控制aggregation的打开与闭合。

Husky::AggregatorWorker::get().AGG_enable(AGG_ON);  // 打开
// Or ...
Husky::AggregatorWorker::get().AGG_enable(AGG_OFF);  // 关闭

注意:一旦你关闭了交换器,在list_execute中的全局aggregation过程都将被忽略,所有的aggregators都将不会被更新。如果你没有创建aggregators,那么即使交换器是打开的,在每一次list_execute时都没有执行全局aggregation的过程。

如果你想要对std::vector使用aggregator,请注意如下要点:

  • 一个aggregator的构造函数需要一个初始值,在这里是需要一个std::vector。在你将std::vector输入构造函数之前,请确保向量已被resize。
std::vector<double> init_para;  // 默认大小是0!
init_para.resize(10);  // 假设向量的长度是10
// 或者: std::vector<double> init_para(10);
Husky::Aggregator<std::vector<double> > para(init_para, 
  [](std::vector<double> &a, const std::vector<double> &b) { 
    for (int i = a.size() - 1;i >= 0;i--) a[i] += b[i];
  }
);
  • 在某些情况下,你可能只需要更新向量的某个元素,你可以使用如下的update函数:
int idx = 3;  // 在指标3 下面的值将被更新
para.update(std::make_pair(idx, val),
  [](std::vector<double> &a, const std::pair<int, double> & update) {
    int idx = update.first;
    double val = update.second;
    a[idx] += val;  // you know these three lines can be written in one line, right
  }
};  // 假设我仅想要更新在指标`idx`下面的值

这个updata函数需要第二个参数,是用来指明怎么使用所给的值来更新向量的一个lambda.

  • 如果你像上面那般使用update函数,那么你就可能需要给aggregator的构造函数赋予个zero value,也就是一个lambda:
std::vector<double> init_para(10);
Husky::Aggregator<std::vector<double> > para(
  init_para,  // initial value
  [](std::vector<double> &a, const std::vector<double> &b) {  // aggregation 规则
    for (int i = a.size() - 1;i >= 0;i--) a[i] += b[i];
  },
  [=](std::vector<double> &a) {  // 怎么设置一个向量为零
    a = init_para;
  }
);

你可能注意到了在构造函数中还提供了第三个参数。 这个lambda指明怎么改变一个向量为zero。什么是'zero', 这跟aggregation的过程有关。

Aggregation过程

简单来说,aggregation包括本地的aggregation和全局的aggregation.

在列表执行期间,每一个worker遍历它所拥有的对象,更新aggregator的值。 实际上,每一个worker会保存一个aggregation的副本(本地agg),并且对象导致的更新也会首先写入到本地的agg中。所以如果在job函数当中定义了一个aggregator并且存在有10个workers,那么便会有10份副本分别保存来自每台本地worker上的对象得到的更新值。

在所有的对象都被迭代后,所有本地aggs的更新都将会被发送至本地某个特定的agg中,并由这个agg来做全局的aggregation。完成全局aggregation后, aggregation的结果就被发送回每一台机器。这个结果将被同一台机器上的workers共享。

这里我们看到有本地aggregation和全局aggregation。在agg构造函数中给的初始值用于全局aggregation,而零值则是用于本地的aggregation。

你可能感兴趣的:(Husky中文文档-C++ Husky Aggregator 指南)