Practice: Arrays and Parallel Loops
Using the arrays, views, for_each, and reduce that you learned in Chapter 7, you build the following three programs in order. First build all three on the CPU (backend::Host). Once you have read Chapter 9 and can use a GPU, run the same sources with CUDA as well.
Vector addition — Allocate arrays, compute each element with
for_each, and check the result in a host-side array.Reduction — Use
reduceto compute the sum of squares and the maximum of an array.1D advection — Compute the time evolution using ghost cells, a periodic boundary, and two arrays for before and after the update.
You write main.cpp and CMakeLists.txt yourself. You do not need to build MISO itself or run its demos.
CMake configuration
Use the “minimal CMake configuration for using MISO from your own program” in Chapter 1. It loads MISO’s miso/ with add_subdirectory and links MISO::MISO to your executable. Pass the location of MISO when you run CMake, with -DMISO_DIR=/path/to/MISO (replace /path/to/MISO with the location where you placed MISO).
cmake -S . -B build -DMISO_DIR=/path/to/MISO
cmake --build build -j
./build/vector_add
You may list all three executables in a single CMakeLists.txt, or use a separate folder for each assignment. Linking MISO::MISO also links MPI and yaml-cpp, but the headers you include in this Practice do not use MPI, so you can run the programs without mpiexec.
Headers to use
<miso/core.hpp> includes all the common headers at once, but in this Practice you include only the headers for the features you use.
Feature |
Header |
|---|---|
The 1D arrays |
|
|
|
Math functions such as |
|
As in MISO’s demos, you may write using namespace miso; for the namespace.
Writing code that can switch to the GPU
So that you can later run the same sources with CUDA, follow these two points.
Decide the place of computation in one spot. Instead of writing backend::Host in many places, define a single type alias Backend and use it.
#ifdef __CUDACC__
using Backend = backend::CUDA;
#else
using Backend = backend::Host;
#endif
__CUDACC__ is defined only when the CUDA compiler is compiling (MISO_LAMBDA in Chapter 7). In a CPU build, Backend is backend::Host. Write arrays as Array1D<double, Backend> and loops as for_each(Backend{}, ...).
Copy the result to a host-side array before reading it. An array placed on the GPU cannot be read directly from a CPU loop. To check the result, prepare a separate Array1D<double, backend::Host>, copy into it with copy_from, and then read it.
Array1D<double, backend::Host> h(n);
h.copy_from(z); // copy the elements of z to h
In a CPU build, z is also on the host, so copy_from is just a copy. In a CUDA build, the same single line becomes a transfer from the GPU to the CPU (Chapter 9).