Cooking with Reductions

Please see the following section for overview discussion about RAJA reductions:

Reductions with RAJA::forall

Here is the setup for a simple reduction example:

const int N = 1000;

int vec[N];

for (int i = 0; i < N; ++i) {

  vec[i] = 1;

}

Here a simple sum reduction is performed in a for loop:

int vsum = 0;

// Run a kernel using the reduction objects
for (int i = 0; i < N; ++i) {

  vsum += vec[i];

}

The results of these operations will yield the following values:

  • vsum == 1000

RAJA uses policy types to specify how things are implemented.

The forall execution policy specifies how the loop is run by the RAJA::forall method. The following discussion includes examples of several other RAJA execution policies that could be applied. For example RAJA::seq_exec runs a C-style for loop sequentially on a CPU. The RAJA::cuda_exec_with_reduce<256> runs the loop as a CUDA GPU kernel with 256 threads per block and other CUDA kernel launch parameters, like the number of blocks, optimized for performance with reducers.:

using exec_policy = RAJA::seq_exec;
// using exec_policy = RAJA::omp_parallel_for_exec;
// using exec_policy = RAJA::omp_target_parallel_for_exec<256>;
// using exec_policy = RAJA::cuda_exec_with_reduce<256>;
// using exec_policy = RAJA::hip_exec_with_reduce<256>;
// using exec_policy = RAJA::sycl_exec<256>;

The reduction policy specifies how the reduction is done and must support the execution policy. For example RAJA::seq_reduce does a sequential reduction and can only be used with sequential execution policies. The RAJA::cuda_reduce_atomic policy uses atomics, if possible with the given data type, and supports CUDA execution policies as well as sequential loops and OpenMP loops when that support is enabled in RAJA. Similarly for other RAJA execution back-ends, such as HIP and OpenMP. Here are example RAJA reduction policies whose names are indicative of which execution policies they work with:

using reduce_policy = RAJA::seq_reduce;
// using reduce_policy = RAJA::omp_reduce;
// using reduce_policy = RAJA::omp_target_reduce;
// using reduce_policy = RAJA::cuda_reduce_atomic;
// using reduce_policy = RAJA::hip_reduce_atomic;
// using reduce_policy = RAJA::sycl_reduce;

Here a simple sum reduction is performed using RAJA:

RAJA::ReduceSum<reduce_policy, int> vsum(RAJA::policy_of<exec_policy>::value, 0);

RAJA::forall<exec_policy>( RAJA::RangeSegment(0, N),
  [=](RAJA::Index_type i) {

  vsum += vec[i];

});

The results of these operations will yield the following values:

  • vsum.get() == 1000

The RAJA::policy_of<exec_policy>::value constructor argument tells the reducer which execution-policy family will use this object at runtime. This is important when the reduction policy supports a broader set of execution policies than the loop you are about to run. For example, if reduce_policy is RAJA::cuda_reduce but the object will only be used with RAJA::seq_exec, construct or reset it with RAJA::Policy::sequential or RAJA::policy_of<RAJA::seq_exec>::value. This tells RAJA not to prepare CUDA reduction resources for that object.

If the same reducer object will later be used with CUDA loops, call reset(RAJA::Policy::cuda, ...) before that use. Use RAJA::Policy::undefined only when the object intentionally needs to support any loop policy supported by the reduction policy.

Another option for the execution policy when using the cuda or hip backends are the base policies which have a boolean parameter to choose between the general use cuda/hip_exec policy and the cuda/hip_exec_with_reduce policy.:

// static constexpr bool with_reduce = ...;
// using exec_policy = RAJA::cuda_exec_base<with_reduce, 256>;
// using exec_policy = RAJA::hip_exec_base<with_reduce, 256>;

Another option for the reduction policy when using the cuda or hip backends are the base policies which have a boolean parameter to choose between the atomic cuda/hip_reduce_atomic policy and the non-atomic cuda/hip_reduce policy.:

// static constexpr bool with_atomic = ...;
// using reduce_policy = RAJA::cuda_reduce_base<with_atomic>;
// using reduce_policy = RAJA::hip_reduce_base<with_atomic>;