鲲鹏社区首页
中文
注册
开发者
我要评分
获取效率
正确性
完整性
易理解
在线提单
论坛求助

kupl::parallel_for

创建parallel for并行循环。相较于kupl_parallel_for接口而言,该接口通过lambda函数特性捕获parallel for回调函数的入参, 避免用户入参封装行为,提高接口易用性。

接口定义

kupl::parallel_for(kupl_parallel_for_desc_t *desc, const pf_lambda &func);

类型定义

using pf_lambda = std::function<void(const kupl_nd_range_t *nd_range, const int tid, const int tnum)>;

表1 参数定义

参数名

类型

描述

输入/输出

desc

kupl_parallel_for_desc_t *

parallel for循环任务的描述,指向kupl_parallel_for_desc_t结构体的指针,具体见下方kupl_parallel_for_desc_t数据结构表

输入

func

pf_lambda

parallel for循环任务的函数,该函数必须定义为如下形式: std::function<void(const kupl_nd_range_t *nd_range, const int tid, const int tnum)>;其中nd_range表示经过kupl_parallel_for内部处理后该函数实际执行的循环区域,tid与tnum表示当前函数执行所在线程编号与总线程数

输入

表2 kupl_parallel_for_desc_t的数据结构定义

参数名

类型

描述

field_mask

uint64_t

结构体中有效字段的掩码,使用kupl_parallel_for_desc_field中的位标识。此掩码中未指定的字段将被忽略。当前所有字段都为必填项,可以使用KUPL_PARALLEL_FOR_DESC_FIELD_DEFAULT表示所有字段都生效。

具体可设置的掩码:

  • KUPL_PARALLEL_FOR_DESC_FIELD_RANGE:range生效
  • KUPL_PARALLEL_FOR_DESC_FIELD_EGROUP:egroup生效
  • KUPL_PARALLEL_FOR_DESC_FIELD_CONCURRENCY:concurrency生效
  • KUPL_PARALLEL_FOR_DESC_FIELD_POLICY: policy生效
  • KUPL_PARALLEL_FOR_DESC_FIELD_DEFAULT:上述所有字段都生效

range

kupl_nd_range_t *

parallel for范围,指向kupl_nd_range_t结构体的指针,具体见下方kupl_nd_range_t数据结构表。

egroup

kupl_egroup_h

执行for循环任务的egroup,即能在哪个egroup中的executor执行器上执行;可设置为空指针,即不指定egroup。

concurrency

int

for循环任务的并发度;可设置为KUPL_CONCURRENCY_DEFAULT,即不指定并发度。

policy

kupl_loop_policy_type_t

parallel for任务遵循的切分策略,当前可设置为

  • KUPL_LOOP_POLICY_STATIC,表示静态切分策略:平均切。
  • KUPL_LOOP_POLICY_DYNAMIC,表示动态切分策略。
  • KUPL_LOOP_POLICY_TASK,表示所有任务会被静态切分但会被以task形式提交用于动态调度。
表3 kupl_nd_range_t的数据结构定义

参数名

类型

描述

dim

int

parallel for范围的维度,最大值为KUPL_MAX_DIM_SIZE,当前为3

nd_range

kupl_range_t[]

每个维度的具体范围,例如nd_range[0]表示维度0的范围;nd_range数组大小为KUPL_MAX_DIM_SIZE;kupl_range_t数据结构说明具体见下表

表4 kupl_nd_range_t的数据结构定义

参数名

类型

描述

dim

int

parallel for范围的维度,最大值为KUPL_MAX_DIM_SIZE,当前为3

nd_range

kupl_range_t[]

每个维度的具体范围,例如nd_range[0]表示维度0的范围;nd_range数组大小为KUPL_MAX_DIM_SIZE;kupl_range_t数据结构说明具体见下表

表5 kupl_range_t的数据结构定义

参数名

类型

描述

lower

size_t

parallel for循环的起始值,即范围的下限

upper

size_t

parallel for循环的结束值,即范围的上限

step

size_t

parallel for循环的步长,即每次循环增大的值

blocksize

size_t

。任务拆分后,每次执行一个block,每个block包含的最小迭代次数是blocksize,可以使用KUPL_BLOCKSIZE_DEFAULT表示默认blocksize。当前静态切分策略下blocksize不生效,kupl会按线程数均分数据。只有动态切分和task切分策略下生效,blocksize默认值为1。

由于kupl_nd_range_t的数据结构较为复杂,因此在允许用户自行配置数据结构的同时,也提供了相应的宏,以供用户便捷地配置维度为1的kupl_nd_range_t。具体的宏如表 kupl_nd_range_t的宏定义所示。

注:大于一维的任务总数要保证小于int上限,即总blocks < 2^31 - 1

表6 kupl_nd_range_t的宏定义

描述

KUPL_1D_RANGE_INIT(_range, _col_begin, _col_end)

配置维度为1、步长为1的parallel for范围

具体功能:

将_range的维度设置为1;

将(_range).nd_range[0]的下限,上限,步长,块数分别设置为_col_begin、_col_end、1、KUPL_BLOCKSIZE_DEFAULT

KUPL_STRIDE_1D_RANGE_INIT(_range, _col_begin, _col_end, _col_step, _col_blocksize)

配置维度为1的parallel for范围

具体功能:

将_range的维度设置为1;

将(_range).nd_range[0]的下限,上限,步长,块数分别设置为_col_begin、_col_end、_col_step、_col_blocksize

KUPL_2D_RANGE_INIT(_range, _row_begin, _row_end, _col_begin, _col_end)

配置维度为2、步长为1的parallel for范围

具体功能:

将_range的维度设置为2;将(_range).nd_range[0]的下限、上限、步长、块数分别设置为_col_begin、_col_end、1、KUPL_BLOCKSIZE_DEFAULT;将(_range).nd_range[1]的上限、下限、步长、块数分别设置为_row_begin、_row_end、1、KUPL_BLOCKSIZE_DEFAULT

KUPL_STRIDE_2D_RANGE_INIT(_range, _row_begin, _row_end, _row_step, _row_blocksize, _col_begin, _col_end, _col_step, _col_blocksize)

配置维度为2的parallel for范围

具体功能:

将_range的维度设置为2;将(_range).nd_range[0]的下限、上限、步长、块数分别设置为_col_begin、_col_end、_col_step、_col_blocksize;将(_range).nd_range[1]的上限、下限、步长、块数分别设置为_row_begin、_row_end、_row_step、_row_blocksize

KUPL_3D_RANGE_INIT(_range, _page_begin, _page_end, _row_begin, _row_end, _col_begin, _col_end)

配置维度为3、步长为1的parallel for范围

具体功能:

将_range的维度设置为3;将(_range).nd_range[0]的下限、上限、步长、块数分别设置为_col_begin、_col_end、1、KUPL_BLOCKSIZE_DEFAULT;将(_range).nd_range[1]的下限、上限、步长、块数分别设置为_row_begin、_row_end、1、KUPL_BLOCKSIZE_DEFAULT;将(_range).nd_range[2]的下限、上限、步长、块数分别设置为_page_begin、_page_end、1、KUPL_BLOCKSIZE_DEFAULT

KUPL_STRIDE_3D_RANGE_INIT(_range, _page_begin, _page_end, _page_step, _page_blocksize, _row_begin, _row_end, _row_step, _row_blocksize, _col_begin, _col_end, _col_step, _col_blocksize)

配置维度为3的parallel for范围

具体功能:

将_range的维度设置为3;将(_range).nd_range[0]的下限、上限、步长、块数分别设置为_col_begin、_col_end、_col_step、_col_blocksize; 将(_range).nd_range[1]的下限、上限、步长、块数分别设置为_row_begin、_row_end、_row_step、_row_blocksize; 将(_range).nd_range[2]的下限、上限、步长、块数分别设置为_page_begin、_page_end、_page_step、_page_blocksize

返回值

  • 成功:返回KUPL_OK
  • 失败:返回KUPL_ERROR

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <atomic>
#include <assert.h>
#include "kupl.h"

int main()
{
    int count = 10;
    kupl_nd_range_t range;
    KUPL_1D_RANGE_INIT(range, 0, count);
    kupl_parallel_for_desc_t desc = {
        .field_mask = KUPL_PARALLEL_FOR_DESC_FIELD_DEFAULT,
        .range = &range,
        .egroup = nullptr,
        .concurrency = count,
        .policy = KUPL_LOOP_POLICY_STATIC,
    };

    std::atomic<size_t> sum(0);
    const size_t loop_size = 1000000;
    size_t sum_cal = (1 + loop_size) * loop_size / 2;
    size_t *data = (size_t *)malloc(loop_size * sizeof(loop_size));
    for (size_t i = 0; i < loop_size; i++) {
        data[i] = i + 1;
    }

    int ret = kupl::parallel_for(&desc, [&](const kupl_nd_range_t *nd_range, int tid, int tnum) {
        int start_index = nd_range->nd_range[0].lower * (loop_size / count);
        size_t local_sum = 0;
        for (size_t i = 0; i < loop_size / count; i++) {
            local_sum += data[start_index + i];
        }
        sum += local_sum;
    });
    assert(ret == KUPL_OK);
    assert(sum.load() == sum_cal);
    printf("sum: %lu\n", sum.load());
}

运行结果如下。

sum: 500000500000

上述示例演示了使用kupl::parallel_for并行计算1-1000000的和。首先通过KUPL_1D_RANGE_INIT宏配置了parallel for范围,定义了1维的step步长为1,范围从0到count的for循环描述;其次配置了parallel for任务描述,并发度为count、使用所有执行器,使用静态切分策略;最终通过kupl_parallel_for函数执行for循环。注:以上运行结果以实际为准,上述结果仅供参考。