E.V.E
v2023.02.15
 
Loading...
Searching...
No Matches
Tuples

In the previous tutorials, we built a SIMD function to compute those cartesian-polar conversions and we used them in the context of a SIMD algorithm to apply them over an arbitrary set of data. While doing so, we introduced the eve::views::zip component that helped us gather multiple ranges into a single one. This worked by feeding eve::algo::transform_to's lambda a tuple of eve::wide.

This interaction between SIMD registers and tuple-like types is very interesting and EVE provides different ways to take advantage of these interactions. In this tutorial, we'll go over how EVE can help you design SIMD-aware tuple to write higher level SIMD code.

Tuple of SIMD registers

If we go back to the initial scalar conversion functions, we can actually merge both rho and theta functions in a single to_polar function that will return a tuple of float containing both results of the conversion.

#include <cmath>
#include <tuple>
std::tuple<float,float> to_polar(float x, float y)
{
auto rho = std::hypot(x, y);
auto theta = std::atan2(y, x);
return { rho, theta };
}

This is a classical use case of std::tuple to return multiple values from a single function.

The question now is how to turn this code into a SIMD compatible function. A trivial solution is to just return a std::tuple<eve::wide<float>, eve::wide<float>>.

#include <eve/wide.hpp>
#include <eve/module/math.hpp>
#include <tuple>
std::tuple<eve::wide<float>,eve::wide<float>> to_polar(eve::wide<float> x, eve::wide<float> y)
{
auto rho = eve::hypot(x, y);
auto theta = eve::atan2(y, x);
return { rho, theta };
}

This version of the code just works out of the box. eve::wide is just a C++ type and thus interacts normally with other standard components.

SIMD register of tuples

If the previous code is fine, it has the disadvantage of not being compatible to the general SIMD principle of EVE. Indeed, std::tuple is not a eve::simd_value even when it contains eve::wide.

A better model should allow us to use eve::wide of tuples. To do so, we use kumi::tuple instead of std::tuple. This change is due to the higher flexibility of kumi::tuple and its system of extension. kumi, as a library is integrated within EVE through the eve/product_type.hpp header file and does not need any special setup.

Therefore, we can write such a code:

#include <eve/module/math.hpp>
#include <eve/traits/product_type.hpp>
namespace simd
{
auto to_polar(eve::wide<float> x, eve::wide<float> y)
{
auto rho = eve::hypot(x, y);
auto theta = eve::atan2(y, x);
return eve::wide<kumi::tuple<float,float>>{ rho, theta };
}
}

The key element is the type of the return value of the function: eve::wide<kumi::tuple<float,float>>. By design, kumi::tuple is able to be used as a template parameter for eve::wide. How do we interpret this type ? It logically behaves a SIMD register where each lane is an instance of the kumi::tuple. Internally, it is stored as a kumi::tuple of eve::wide of each tuple's element. In other words, it performs automatic Array of Structure to Structure of Array conversion.

Now that our function is able to return a type compatible with EVE SIMD model, we can amend the SIMD algorithm call to use eve::views::zip for both the input and output ranges.

#include <vector>
#include <eve/module/algo.hpp>
namespace simd
{
auto to_polar( std::vector<float> const& xs, std::vector<float> const& ys)
{
std::vector<float> rho(xs.size());
std::vector<float> theta(xs.size());
auto ins = eve::views::zip(xs, ys);
auto outs = eve::views::zip(rho, theta);
( ins, outs
, [](auto in) { return to_polar( get<0>(in), get<1>(in)); }
);
return std::make_tuple(rho,theta);
}
}
Note
In this example we passed eve::algo::allow_frequency_scaling to simplify the example. We talk more about it in frequency scaling tutorial.

Conclusion

In this tutorial, we managed to:

  • handle tuple in SIMD context using eve::wide and kumi::tuple
  • process SIMD-aware tuple with EVE algorithms

In the next tutorial, we'll investigate how such tuples can be replaced by SIMD aware user-defined types which can then be efficiently stored and processed.