In the previous tutorial, we managed to convert a sequential function into a function using SIMD types and functions. In general, such function is meant to be applied to a large set of data instead of a single register.
As for usual sequential computation, we want to lift ourselves from raw loops and think using algorithms. EVE provides such ready-to-use SIMD aware algorithms and this tutorial will take a look at how to handle them.
Let's try to apply our sequential conversion function over data stored in std::vector
using standard algorithms.
Nothing really special here, we apply a given function over some float
stored in a vector. Since C++20, you may be accustomed to the range-based version of this code:
Very similar code, except for the fact the input data are passed directly without using iterators.
We can turn this range-based code into a SIMD-aware call to one of the algorithms defined in eve::algo. All algorithms in EVE are range-based thus simplifying the transition from code using standard algorithms.
Let's unpack all the new components:
eve::algo::transform
. This is due to the fact that discriminating operation between two distinct ranges and in-place operations leads to better code generation and performances. If you need to perform in-place computation, you can replace eve::algo::transform_to by eve::algo::transform_inplace, its in-place variant.get
or structured bindings).In SIMD algorithms we by default assume that the provided operation is simple (a few instructions), since this is the common case. This means we use aligned reads and do unrolling, which is an important optimisation. However, for a complex case, like here, it is beneficial to opt out.
EVE provides various traits to customize algorithms behavior. The traits we're interested in are:
eve::algo::no_aligning
, eve::algo::unrolling
Algorithms in EVE being callable object, you can apply traits using their []
operators. For example, the following code let's EVE know that the loop body is heavy.
Best strategy is always to benchmark your code and tune algorithms accordingly.
Note that this kind of tuning is not reserved to algorithms. The callable eve::hypot
can also be tuned (here as eve::hypot[eve::pedantic]
) to enforce some behaviors regarding accuracy or standard compliance).
In this tutorial, we managed to:
In the next tutorial, we will complete this exercise by demonstrating how to use all elements seen this far to work directly on user-defined types.