When you call a function on one or more SIMD values, you expect the computation to be performed on every elements of its parameters. Sometimes, you may want to make the application of a given function dependent on some condition.
Let's explore the functionalities EVE provides for achieving those results.
Let's say the function we want to write computes the product of two values a
and b
if a
is equal to b
and their difference otherwise.
The scalar code is looking like:
The SIMD version of this code can't use if
nor the ternary operator directly. The correct approach is to use the eve::if_else function.
The eve::if_else call explicitly requests we pass:
a
and b
true
, here the product of a
and b
false
, here the difference of a
and b
if ... else
statement, eve::if_else will evaluates all its arguments before performing its selection even if potential short-cut can be applied later on.Let's define a sqrt_positive
function that computes the square root of its argument if it's positive or returns it unchanged otherwise. One can write:
This code is perfectly valid and will produce the correct result. However, it has some issues:
To go beyond those limitations, EVE functions supports – whenever it makes sense – a conditional call syntax:
The code of sqrt_positive
now works differently:
a >= 0
expression is evaluateda
The fact the conditional syntax builds a new callable object is interesting because it ensures that any optimization over the conditional computation can be captured and that this new callable can be passed as-if to algorithms without having to worry about changing the number of arguments requested.
If required, the callable object produced by the conditional syntax can be stored into a variable:
If passing a simple logical expression is the most common use-case of the conditional syntax, one may requires more flexibility. To do so, EVE provides various objects to express more elaborated conditions.
One may want to use the conditional syntax to call a function but instead of returning the first argument if the condition is false, one may want to return an arbitrary value. This use case is handled by the eve::if_ helper by wrapping logical expression so that an alternative value can be specified.
Let's modify sqrt_positive
so that, if the argument is not positive, 0 is returned instead.
Some algorithms require conditional function calls but use logical expression relative to the element index inside a eve::simd_value rather than its value. One may want for example to not compute an expression on the first and last element of such eve::simd_value.
A frequent example is trying to load data from memory while ignoring trailing garbage or out of bounds values:
Here, the eve::ignore_first and eve::ignore_last conditionals take a number of elements as parameter that describe which zone of the eve::simd_value won't be affected. By default, the value of the not loaded lanes are undefined. As for other eve::conditional_expr, we can affix them with an alternative (99 and 42 respectively) to replace the not loaded pieces.
But what if we want to apply our operation to every element but the first and last one ? Clearly, calling two operations with two different conditional masks is sub-optimal and EVE provides some more conditional expressions to express this need.
The first is to use the eve::keep_between helper:
eve::keep_between uses ad-hoc indexes, which makes the code a bit too size dependent. One can also use the same conditional but use a similar interface to eve::ignore_first.
The output is obviously the same.
Conditional operations on SIMD values is a good way to keep a high level code over some complex computations. EVE provides different levels of abstraction for such operations as well as various helpers to specify how the conditions can be computed based either on values or indexes.