Rate This Document
Findability
Accuracy
Completeness
Readability

kupl_parallel_for

Create a "parallel for" loop.

Interface Definition

int kupl_parallel_for(kupl_parallel_for_desc_t *desc, kupl_pf_func_t func, void *args);

Parameters

Table 1 Parameter definition

Parameter

Type

Description

Input/Output

desc

kupl_parallel_for_desc_t *

Description of the "parallel for" loop, which is a pointer to the kupl_parallel_for_desc_t structure. For details, see the description in the following table.

Input

func

kupl_pf_func_t

Function of the "parallel for" loop. The function must be defined in the format of void (*kupl_pf_func_t)(kupl_nd_range_t *nd_range, void *args, int tid, int tnum). args indicates parameters passed to the structure. nd_range indicates the actual loop range executed by the function, as determined by the internal logic of the kupl_parallel_for function. tid and tnum indicate the ID of the thread where the current function is executed and the total number of threads, respectively.

Input

args

void *

Arguments to be passed to func.

Input

Table 2 kupl_parallel_for_desc_t data structure definition

Parameter

Type

Description

field_mask

uint64_t

Mask of valid fields in the structure, similar to the bit flag of kupl_parallel_for_desc_field. Fields not specified in the mask will be ignored. This parameter is mandatory for all fields.

range

kupl_nd_range_t *

Range of the "parallel for" loop, which is a pointer to the kupl_nd_range_t structure. For details, see Table 3.

egroup

kupl_egroup_h

egroup within which the "for" loop is executed, that is, the specific egroup whose executors will run the loop. This parameter can be set to a null pointer, meaning that the egroup is not specified.

concurrency

int

Concurrency of the "for" loop. The value can be KUPL_CONCURRENCY_DEFAULT, meaning that no concurrency is specified.

policy

kupl_loop_policy_type_t

Splitting policy followed by the "parallel for" loop. Currently, this parameter can be set to KUPL_LOOP_POLICY_STATIC, indicating the static splitting policy (even splitting);

KUPL_LOOP_POLICY_DYNAMIC, indicating the dynamic splitting policy; or

KUPL_LOOP_POLICY_TASK, indicating that all tasks are statically split but submitted for dynamic scheduling.

Table 3 kupl_nd_range_t data structure definition

Parameter

Type

Description

dim

int

Dimension of the "parallel for" log range. The maximum value is KUPL_MAX_DIM_SIZE. The current value is 3.

nd_range

kupl_range_t[]

Specific range of each dimension. For example, nd_range[0] indicates the range of dimension 0. The size of the nd_range array is KUPL_MAX_DIM_SIZE. For details about the kupl_range_t data structure, see the following table.

Table 4 kupl_range_t data structure definition

Parameter

Type

Description

lower

int64_t

Start value of the "parallel for" loop, that is, the lower limit of the range.

upper

int64_t

End value of the "parallel for" loop, that is, the upper limit of the range.

step

int64_t

Step of the "parallel for" loop, that is, the value increased in each loop.

blocksize

int64_t

After a task is split, one block is executed at a time. The minimum number of iterations contained in each block is the block size. You can use KUPL_BLOCKSIZE_DEFAULT to specify the default block size. In the current static splitting policy, the block size does not take effect. KUPL evenly distributes data based on the number of threads. This parameter takes effect only in dynamic splitting and task splitting policies. The default block size is 1.

The data structure of kupl_nd_range_t is complex. In addition to allowing users to configure the data structure, corresponding macros are provided for users to easily configure kupl_nd_range_t with a dimension of 1. For details, see Table 5.

Note: The total number of tasks with more than one dimension must be less than the upper limit of int, that is, the number of total blocks is less than 2^31 – 1.

Table 5 Macro definitions of kupl_nd_range_t

Macro

Description

KUPL_1D_RANGE_INIT(_range, _col_begin, _col_end)

Configures the range of the "parallel for" loop with the dimension of 1 and the step of 1.

Details:

Sets the dimension of _range to 1.

Sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively.

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

Configures the range of the "parallel for" loop with the dimension of 1.

Details:

Sets the dimension of _range to 1.

Sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, _col_step, and _col_blocksize, respectively.

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

Configures the range of the "parallel for" loop with the dimension of 2 and the step of 1.

Details:

Sets the dimension of _range to 2; sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively; and sets the upper limit, lower limit, step, and block size of (_range).nd_range[1] to _row_begin, _row_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively.

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

Configures the range of the "parallel for" loop with the dimension of 2.

Details:

Sets the dimension of _range to 2; sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, _col_step, and _col_blocksize, respectively; and sets the upper limit, lower limit, step, and block size of (_range).nd_range[1] to _row_begin, _row_end, _row_step, and _row_blocksize, respectively.

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

Configures the range of the "parallel for" loop with the dimension of 3 and the step of 1.

Details:

Sets the dimension of _range to 3; sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively; sets the lower limit, upper limit, step, and block size of (_range).nd_range[1] to _row_begin, _row_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively; and sets the lower limit, upper limit, step, and block size of (_range).nd_range[2] to _page_begin, _page_end, 1, and KUPL_BLOCKSIZE_DEFAULT, respectively.

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)

Configures the range of the "parallel for" loop with the dimension of 3.

Details:

Sets the dimension of _range to 3; sets the lower limit, upper limit, step, and block size of (_range).nd_range[0] to _col_begin, _col_end, _col_step, and _col_blocksize, respectively; sets the lower limit, upper limit, step, and block size of (_range).nd_range[1] to _row_begin, _row_end, _row_step, and _row_blocksize, respectively; and sets the lower limit, upper limit, step, and block size of (_range).nd_range[2] to _page_begin, _page_end, _page_step, and _page_blocksize, respectively.

Return Value

  • Success: KUPL_OK
  • Failure: KUPL_ERROR

Examples

One-dimensional loop:

 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
#include <stdio.h>
#include <pthread.h>
#include "kupl.h"
static inline void task_in_loop(kupl_nd_range_t *nd_range, void *args, int tid, int tnum)
{
    for (int64_t i = nd_range->nd_range[0].lower; i < nd_range->nd_range[0].upper; i += nd_range->nd_range[0].step) {
        printf("pthread %lu: task_in_loop exe %d job\n", pthread_self(), i);
    }
}
int main()
{
    const int num_threads = kupl_get_num_executors();
    int count = num_threads;
    kupl_nd_range_t range;
    KUPL_1D_RANGE_INIT(range, 0, count);
    int executors[num_threads];
    for (int i = 0; i < num_threads; i++) {
        executors[i] = i;
    }
    kupl_egroup_h eg = kupl_egroup_create(executors, num_threads);
    kupl_parallel_for_desc_t desc = {
        .field_mask = KUPL_PARALLEL_FOR_DESC_FIELD_DEFAULT,
        .range = &range,
        .egroup = eg,
        .concurrency = num_threads,
        .policy = KUPL_LOOP_POLICY_STATIC
    };
    kupl_parallel_for(&desc, task_in_loop, nullptr);
    kupl_egroup_destroy(eg);
}

The execution result is as follows:

pthread 281473368858656: task_in_loop exe 0 job
pthread 281473335971872: task_in_loop exe 2 job
pthread 281473344426016: task_in_loop exe 1 job
pthread 281473327517728: task_in_loop exe 3 job
Three-dimensional loop:
 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
#include <stdio.h>
#include <pthread.h>
#include "kupl.h"
static inline void task_in_loop(kupl_nd_range_t *nd_range, void *args, int tid, int tnum)
{
    for (int64_t i = nd_range->nd_range[0].lower; i < nd_range->nd_range[0].upper; i += nd_range->nd_range[0].step) {
        for (int64_t j = nd_range->nd_range[1].lower; j < nd_range->nd_range[1].upper; j += nd_range->nd_range[1].step) {
            for (int64_t k = nd_range->nd_range[2].lower; k < nd_range->nd_range[2].upper; k += nd_range->nd_range[2].step) {
                printf("pthread %lu: task_in_loop exe [%d : %d : %d] job\n", pthread_self(), i, j, k);
            }
        }
    }
}
int main()
{
    const int num_threads = kupl_get_num_executors();
    int count = num_threads;
    kupl_nd_range_t range;
    KUPL_3D_RANGE_INIT(range, 0, count, 0, count, 0, count);
    int executors[num_threads];
    for (int i = 0; i < num_threads; i++) {
        executors[i] = i;
    }
    kupl_egroup_h eg = kupl_egroup_create(executors, num_threads);
    kupl_parallel_for_desc_t desc = {
        .field_mask = KUPL_PARALLEL_FOR_DESC_FIELD_DEFAULT,
        .range = &range,
        .egroup = eg,
        .concurrency = num_threads,
        .policy = KUPL_LOOP_POLICY_STATIC
    };
    kupl_parallel_for(&desc, task_in_loop, nullptr);
    kupl_egroup_destroy(eg);
}

The execution result is as follows:

pthread 281472966852640: task_in_loop exe [0 : 0 : 0] job
pthread 281472966852640: task_in_loop exe [0 : 0 : 1] job
pthread 281472966852640: task_in_loop exe [0 : 1 : 0] job
pthread 281472966852640: task_in_loop exe [0 : 1 : 1] job
pthread 281472950931488: task_in_loop exe [1 : 0 : 0] job
pthread 281472950931488: task_in_loop exe [1 : 0 : 1] job
pthread 281472950931488: task_in_loop exe [1 : 1 : 0] job
pthread 281472950931488: task_in_loop exe [1 : 1 : 1] job

The preceding examples demonstrate the process of executing a "parallel for" loop using KUPL. First, the KUPL_1D_RANGE_INIT macro is used to configure the "parallel for" range for a single dimension with one step, spanning from 0 to count. Then, the "parallel for" loop description is configured, designating task_int_loop as the task function and concurrency as the number of executors, leaving parameters empty, and employing all executors. Finally, the "for" loop is executed using the kupl_parallel_for function. Note that the preceding results are for reference only.