02. Reduction: Sum of Squares and Maximum
Using the setup from the previous assignment, compute the sum of squares and the maximum of an array with reduce. You extend the sum example from Chapter 7 to two cases: the sum of squares and the maximum.
What to implement
Initialize an array x of length N with for_each as x[i] = util::sin(0.001 * i), and compute the following two quantities.
Sum of squares
Σ x[i]²Maximum
max x[i]
MISO’s reduce returns one value per call, so you call reduce twice. For each, decide and write the following three things.
Sum of squares |
Maximum |
|
|---|---|---|
A lambda that returns the value at each point ( |
|
|
A lambda that combines two values ( |
Sum |
The larger one |
Initial value ( |
|
|
Use an initial value that does not change the result when combined (the identity element). For std::numeric_limits, include <limits>. To pick the larger value, you can use util::fmax(a, b). util::max2 rounds a double to a float (MISO #189), so we do not use it here.
Check first with N = 1000, then with N = 1000000.
Checking the result
Copy x into a host-side array with copy_from, and compute the expected values from that same array with an ordinary for loop. The maximum only compares and selects values, so if this array contains no NaN (a value that cannot be represented as a number), you can check it for an exact match.
For the sum of squares, the last digits change with the order of addition. For the host-side expected value, convert each element to long double, square it, and add in long double. Whether long double has more digits than double depends on the environment. For this input and the N above, use a comparison that allows differences up to 1e-12 * max(1, |expected|), and also print the actual difference. When you change to another input or size, do not reuse the same tolerance unconditionally; decide it by considering the number of additions and the rounding error.
Once this is confirmed on the CPU, move on to 1D advection.
Running on the GPU
After reading Chapter 9, run the same assignment with CUDA. Copy the array built on the GPU to the host and compare it with the expected value computed from that array. Also record how much the sum of squares differs between the CPU and the GPU. Both the implementation of the math functions used for initialization and the order of addition in reduce can cause the difference.