explain why the memory access pattern must depend on the architecture, and why pragmas cannot fix this
describe what Kokkos abstracts: execution spaces and memory spaces
decompose any parallel loop into pattern, execution policy and body
write Kokkos parallel_for and parallel_reduce kernels with range and MD-range policies
The problem: CPUs and GPUs want different access patterns
Figure 1: CPU: one thread visits consecutive addresses in time (caching). GPU: consecutive threads visit consecutive addresses (coalescing).
total += data[element*vectorSize + i];// CPU-fast: consecutive i per threadtotal += data[i*numElements + element];// GPU-fast: consecutive threads, same i
A CPU-friendly array layout drops GPU performance by >10× – and vice versa.
For performance, the memory access pattern must depend on the architecture.
What is Kokkos?
Figure 2: One C++ source, compiled through a backend of your choice, runs on any vendor’s hardware.
C++ hardware abstraction layer: single source → NVIDIA, AMD, Intel GPUs + CPUs
Battle-tested: developed at 5 US National Labs, used in Trilinos, LAMMPS, 100+ projects
Why not just OpenMP / OpenACC?
#pragma omp target data map(...)#pragma omp teams num_teams(...) num_threads(...) private(...)#pragma omp distributefor(int element =0; element < numElements;++element){// ...#pragma omp parallel forfor(int qp =0; qp < numQPs;++qp){/* ... */}}
Portable code ≠ performance-portable code.
The pragmas compile everywhere, but each backend behaves differently – and pragmas can’t change the data layout, which is the bigger problem. Kokkos can (next lecture).
Patterns, Policies, Bodies
The Kokkos vocabulary for any parallel loop:
Kokkos::parallel_for( RangePolicy<>(0, N),[=](constint i){/* work */});
Token
Role
Examples
parallel_for
Pattern – structure of the computation
for, reduce, scan, task graph
RangePolicy<>(0, N)
Execution policy – how work is scheduled
range, MD-range, team, dynamic
[=](const int i) {...}
Body – the per-iteration work
your code
You change where code runs by changing the policy, not the body.
“Parallel for”: same loop, three ways
// Serialfor(int64_t i =0; i < N;++i){/* body */}// OpenMP (CPU only)#pragma omp parallel forfor(int64_t i =0; i < N;++i){/* body */}// Kokkos (CPU and GPU)Kokkos::parallel_for(N,[=](constint64_t i){/* body */});
Same conceptual difficulty as OpenMP – the annotations just go in a different place. Unlike OpenMP, the same source runs on GPU.