E.V.E
v2023.02.15
Loading...
Searching...
No Matches
SIMD Specific Operations

Detailed Description

Proper SIMD operations as shuffling, splitting and merging SIMD vectors

Variables

constexpr auto eve::broadcast = functor<broadcast_t>
 Computes the.
constexpr auto eve::combine = functor<combine_t>
 Combines two SIMD values in a larger one.
constexpr auto eve::gather = functor<gather_t>
 Load a SIMD value with values selected from a memory region at the given offsets.
constexpr auto eve::has_equal_in = functor<has_equal_in_t>
 Given two simd_values: x, match_against returns a logical mask. The res[i] == eve::any(x[i] == match_against);.
constexpr auto eve::iterate_selected = functor<iterate_selected_t>
 a utility to do scalar iteration over all true elements in a logical.
constexpr auto eve::scan = functor<scan_t>
 Computes the generalized prefix sum over a simd value.
constexpr auto eve::scatter = functor<scatter_t>
 Store a SIMD register to memory using scattered indexes.
constexpr auto eve::sort = functor<sort_t>
 sorts a register in a accedning order according to a comparator.
constexpr auto eve::zip = functor<zip_t>
 Callable for SoA value constructions.
constexpr callable_broadcast_group_ eve::broadcast_group = {}
 Computes the TODO.
constexpr callable_deinterleave_groups_shuffle_ eve::deinterleave_groups_shuffle = {}
 Callable object for a deinterleave groups shuffle.

Variable Documentation

◆ broadcast

auto eve::broadcast = functor<broadcast_t>
inlineconstexpr

Computes the.

Defined in Header

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<eve::scalar_value T> eve::as_wide_t<T> broadcast(T v) noexcept; // 1
template<eve::scalar_value T, std::ptrdiff_t N> as_wide_t<T> broadcast(T v, fixed<N> sz) noexcept; // 2
template<eve::simd_value T, std::size_t I> T broadcast(T v, index_t<I> i) noexcept; // 3
template<eve::simd_value T, std::size_t I, std::ptrdiff_t N>
eve::as_wide_t<T,fixed<N>> broadcast(T v, index_t<I> i, fixed<N> sz) noexcept; // 4
}
constexpr auto broadcast
Computes the.
Definition broadcast.hpp:79
EVE Main Namespace.
Definition abi.hpp:19
SIMD register cardinal type.
Definition cardinals.hpp:39

Parameters

  • v: A value.
  • i: An eve::index instance indicating which lane of x to broadcast.
  • sz: An eve::lane instance indicating how many lanes to broadcast to.

Return value

  1. A SIMD value equals to eve::as_wide_t<T>{v}.
  2. A SIMD value equals to eve::as_wide_t<T,eve::fixed<N>>{v}.
  3. A SIMD value equals to T{v.get(I)}.
  4. A SIMD value equals to eve::as_wide_t<T,eve::fixed<N>>{v.get(I)}.

Example

#include <eve/module/core.hpp>
#include <eve/wide.hpp>
#include <iostream>
int main()
{
wide_ft pf = {1.2f,0.34f,0.056f,0.0078f};
wide_it pi = {-1, 2,-3, 4,-5, 6,-7, 8};
std::cout << eve::broadcast( 7.6 ) << "\n";
std::cout << eve::broadcast(7.6, eve::lane<8> ) << "\n";
std::cout << "\n";
std::cout << pf << "\n"
<< eve::broadcast(pf, eve::index<3> ) << "\n";
std::cout << "\n";
std::cout << pi << "\n"
<< eve::broadcast(pi, eve::index<5>, eve::lane<4> ) << "\n";
}
constexpr auto pi
Callable object computing the constant .
Definition pi.hpp:79
Wrapper for SIMD registers.
Definition wide.hpp:94

◆ broadcast_group

callable_broadcast_group_ eve::broadcast_group = {}
inlineconstexpr

Computes the TODO.

Defined in Header

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template< eve::value T >
T broadcast_group(T x) noexcept;
}
constexpr callable_broadcast_group_ broadcast_group
Computes the TODO.
Definition broadcast_group.hpp:53

Parameters

Return value

The value of TODO is returned.

Example

TODO

◆ combine

auto eve::combine = functor<combine_t>
inlineconstexpr

Combines two SIMD values in a larger one.

Defined in Header

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<simd_value W>
typename W::combined_type combine(W a, W b) noexcept;
}
constexpr auto combine
Combines two SIMD values in a larger one.
Definition combine.hpp:72

Parameters

  • a, b: two SIMD values of the same type and cardinal

Return value

  • A SIMD value contains the parameters concatenation, thus producing a register of twice as many elements.

Example

#include <eve/wide.hpp>
#include <iostream>
int main()
{
wide_ft pf = {-1.0f, 2.0f, -3.0f, -32768.0f};
auto pf2 = eve::combine(pf,pf);
std::cout << "pf ---> " << pf << "\n";
std::cout << "eve::combine(pf,pf) ---> " << pf2 << "\n";
}

◆ deinterleave_groups_shuffle

callable_deinterleave_groups_shuffle_ eve::deinterleave_groups_shuffle = {}
inlineconstexpr

Callable object for a deinterleave groups shuffle.

deinterleaves elements from one or two registers

Accepts either a register and a group size or two registers and a group size group is how many elements are treated as one chunk, example:

01234567, group_size = 1 ==> 02461235 0123 4567, group_size = 2 ==> 01452367

◆ gather

auto eve::gather = functor<gather_t>
inlineconstexpr

Load a SIMD value with values selected from a memory region at the given offsets.

Header file

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<arithmetic_value T, integral_value U>
as_wide_as_t<T, U> gather(T const* ptr, U idx) noexcept;
template<arithmetic_value T, integral_value U, typename N>
as_wide_as_t<T, U> gather(aligned_ptr<T, N> ptr, U idx) noexcept;
}
constexpr auto gather
Load a SIMD value with values selected from a memory region at the given offsets.
Definition gather.hpp:79
Wrapper for non-owning aligned pointers.
Definition aligned_ptr.hpp:50

Parameters

  • idx: An instance of an integral value.
  • ptr: A pointer to the memory region to load from.

Return value

  • A value equivalent to:
    as_wide_as_t<T, U> res = { ptr[idx[0]], ptr[idx[1]], ..., ptr[idx[N-1]] };

Example

#include <iostream>
#include <vector>
#include <eve/module/core.hpp>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
eve::wide<int, eve::fixed<4>> res = eve::gather(v.data(), eve::wide<unsigned char, eve::fixed<4>>{0, 2, 2, 11});
std::cout << "res -> " << res << std::endl;
}

◆ has_equal_in

auto eve::has_equal_in = functor<has_equal_in_t>
inlineconstexpr

Given two simd_values: x, match_against returns a logical mask. The res[i] == eve::any(x[i] == match_against);.

Optional last parameter allows to overwrite the equality from eve::is_equal to an arbitrary simd binary predicate.

We took the idea for the operation from: "Faster-Than-Native Alternatives for x86 VP2INTERSECT Instructions" by Guillermo Diez-Canas. Link: https://arxiv.org/abs/2112.06342

Defined in Header

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<simd_value T>
constexpr auto has_equal_in(T x, T match_against) noexcept; // 1
template<simd_value T, simd_value U, simd_predicate<T, U> Op>
constexpr auto has_equal_in(T x, U match_against, Op op) noexcept; // 2
}
constexpr auto has_equal_in
Given two simd_values: x, match_against returns a logical mask. The res[i] == eve::any(x[i] == match_...
Definition has_equal_in.hpp:88

Parameters

  • x : argument.
  • match_against argument.
  • op : The binary predicate to use for the comparison.

Return value

  1. A logical SIMD value built as described previously.
  2. Same as 1. but uses a custom predicate instead of eve::is_equal.

Example

#include <iostream>
#include <eve/module/core.hpp>
int main()
{
wide_it x = {2, 1, 2, 4};
wide_it y = {0, 2, 3, 1};
std::cout << "---- simd" << '\n'
<< "<- x = " << x << '\n'
<< "<- y = " << y << '\n'
<< "-> has_equal_in(x, y) = " << eve::has_equal_in(x, y) << '\n'
<< "-> has_equal_in(y, x) = " << eve::has_equal_in(y, x) << '\n';
}

◆ iterate_selected

auto eve::iterate_selected = functor<iterate_selected_t>
inlineconstexpr

a utility to do scalar iteration over all true elements in a logical.

See also
eve::algo::for_each_selected

Header file

#include <eve/module/core.hpp>

Sometimes (for example in parsing) you need to perform scalar operations for each element that matches a predicate. This is a low level utility to help you do that. The predicate for iteration should return true if you want to break, false otherwise.

Callable Signatures

namespace eve
{
<logical_simd_value L>(L l, irregular_predicate<std::ptrdiff_t> auto&& f) -> bool; // (1)
(bool l, irregular_predicate<std::ptrdiff_t> auto&& f) -> bool; // (3)
}
std::predicate but doesn't require regularity
Definition invocable.hpp:97
Specify that a type represents a logical SIMD value. The concept logical_simd_value<T> is satisfied i...
Definition simd.hpp:46
Specifies that a type is a Conditional Expression using relative mask.
Definition conditional.hpp:52
constexpr auto iterate_selected
a utility to do scalar iteration over all true elements in a logical.
Definition iterate_selected.hpp:82
The cheapest to get bitset for simd logical.
Definition top_bits.hpp:80

Parameters

  • ignore - ignored elements are considered false
  • l - logical value (either logical_simd_value, top_bits or just 1 bool).
  • f - callback, that's invoked for every true index. Return false to break.

Return value

  • true iff the user broke the execution (f returned true).

◆ scan

auto eve::scan = functor<scan_t>
inlineconstexpr

Computes the generalized prefix sum over a simd value.

Header file

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<simd_value Wide, eve::abelian_monoid<Wide> Op, eve::substitute_for<Wide> Zero>
constexpr Wide scan(Wide auto x, Op op, Zero zero) noexcept; // 1
template<simd_value Wide>
constexpr Wide scan(Wide x) noexcept; // 2
}
constexpr auto zero
Computes the constant 0.
Definition zero.hpp:78
constexpr auto scan
Computes the generalized prefix sum over a simd value.
Definition scan.hpp:82

Parameters

  • x: An instance of an SIMD value
  • op: The commutative and associative binary operation to apply.
  • zero: The identity/neutral element used by the operation.

Return value

  1. Returns the generalized prefix sum over x using the binary operation op and the identity element zero.
  2. Equivalent to eve::scan(x, eve::add, eve::zero).
Note
Given a binary operation op, a call to eve::scan is defined only if op is associative, commutative, and pure.

Example

#include <eve/module/core.hpp>
#include <iostream>
int main()
{
eve::wide x = {1.0, 2.0, 3.0, 4.0};
std::cout << "x: " << x << "\n";
std::cout << "eve::scan(x): " << eve::scan(x) << "\n";
std::cout << "eve::scan(x, eve::mul, eve::one): " << eve::scan(x, eve::mul, eve::one) << "\n";
}
constexpr auto mul
tuple_callable computing the product of its arguments.
Definition mul.hpp:128
constexpr auto one
Computes the constant .
Definition one.hpp:66

◆ scatter

auto eve::scatter = functor<scatter_t>
inlineconstexpr

Store a SIMD register to memory using scattered indexes.

Store each element of a given SIMD value vin different memory address computed form a base SIMD compatible iterator ptr and a SIMD integral value idx used as indexes.

A call to eve::scatter(v,ptr,idx) is semantically equivalent to:

for(std::size_t i=0;i<v.size();++i)
ptr[idx.get(i)] = v.get(i);

Header file

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
template<simd_value T, integral_simd_value Idx, simd_compatible_ptr<T> Ptr>
requires(T::size() == Idx::size())
void scatter(T const& v, Ptr ptr, Idx const& idx) noexcept;
}
constexpr auto scatter
Store a SIMD register to memory using scattered indexes.
Definition scatter.hpp:90

Parameters

Semantic Modifiers

  • Masked Call

eve::scatter can be masked using Relative conditionals to skip scattering of certain elements.

The call eve::scatter[cond](v,p,i) is semantically equivalent to:

auto m = cond.mask( as<as_logical_t<T>>{} );
for(std::size_t n=0;n<v.size();++n)
{
if(m.get(n)) write(v.get(n),p+idx.get(n));
}
constexpr auto write
Callable object writing a scalar value to memory.
Definition write.hpp:78
Lightweight type-wrapper.
Definition as.hpp:29

Example

#include <eve/module/core.hpp>
#include <iostream>
int main()
{
float data[2*eve::wide<float>::size()] = {};
eve::wide<int> indexes = [](auto i) { return 2*i; };
eve::wide<float> values = [](auto i) { return 1.5f * (1+i); };
eve::scatter(values, data, indexes);
for(auto e : data)
std::cout << e << " ";
std::cout << "\n";
}
static constexpr size_type size() noexcept
Size of the wide in number of lanes.
Definition wide.hpp:443

◆ sort

auto eve::sort = functor<sort_t>
inlineconstexpr

sorts a register in a accedning order according to a comparator.

Header file

#include <eve/module/core.hpp>

Sorting algorithm, based on sorting networks.

Note
this sort is unstable.

Also our implementation is not directly based on any specific one, people we are definitely not the first people to do this. Here is a list of previous work that was looked at.

Callable Signatures

namespace eve
{
// Regular overloads
constexpr auto sort(value auto x); noexcept; // 1
constexpr auto sort(value auto x, ordering auto less) noexcept; // 1
}
The concept value<T> is satisfied if and only if T satisfies either eve::scalar_value or eve::simd_va...
Definition value.hpp:34
constexpr auto sort
sorts a register in a accedning order according to a comparator.
Definition sort.hpp:92

Parameters

Return value

  1. sorted x.

Example

// revision 1
#include <eve/module/core.hpp>
#include <iostream>
int main()
{
auto myless = [](auto z1, auto z2){ return z1 > z2; };
eve::wide wf0{0.0, 1.0, 2.0, 3.0, -1.0, -2.0, -3.0, -4.0};
eve::wide wi0{0, 1, 2, 3, -1, -2, -3, -4};
eve::wide wu0{0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u};
std::cout << "<- wf0 = " << wf0 << "\n";
std::cout << "<- wi0 = " << wi0 << "\n";
std::cout << "<- wu0 = " << wu0 << "\n";
std::cout << "-> sort(wf0) = " << eve::sort(wf0) << "\n";
std::cout << "-> sort(wf0, myless) = " << eve::sort(wf0, myless) << "\n";
std::cout << "-> sort(wu0) = " << eve::sort(wu0) << "\n";
std::cout << "-> sort(wu0, myless) = " << eve::sort(wu0, myless) << "\n";
std::cout << "-> sort(wi0) = " << eve::sort(wi0) << "\n";
std::cout << "-> sort(wi0, myless) = " << eve::sort(wi0, myless) << "\n";
}

◆ zip

auto eve::zip = functor<zip_t>
inlineconstexpr

Callable for SoA value constructions.

Defined in Header

#include <eve/module/core.hpp>

Callable Signatures

namespace eve
{
constexpr auto zip(scalar_value auto... parts) noexcept; //1
constexpr auto zip(as<Target> t, scalar_value auto... parts) noexcept; //2
constexpr auto zip()(simd_value auto... parts) noexcept; //3
constexpr auto zip(as<Target> t, simd_value auto... parts) noexcept; //4
}
Specify that a type represents a scalar value The concept scalar_value<T> is satisfied if and only if...
Definition scalar.hpp:138
Specifies that a type is a SIMD type. The concept simd_value<T> is satisfied if and only if T satisfi...
Definition vectorized.hpp:34
constexpr auto zip
Callable for SoA value constructions.
Definition zip.hpp:85

Parameters

  • parts: Variadic list of value to zip together.
  • t: Type wrapper instance embedding the type to construct from parts.

Return value

  1. a kumi::tuple made from all the scalars passed as argument.
  2. a Target instance made from all the scalars passed as argument.
  3. a kumi::tuple made from all the SIMD values passed as argument.
  4. a Target instance made from all the SIMD values passed as argument.

Example

// revision 1
#include <eve/module/core.hpp>
#include <iostream>
struct data_block : eve::struct_support<data_block, float, std::int16_t,double>
{
friend std::ostream& operator<<(std::ostream& os, data_block const& d)
{
return os << "{" << get<0>(d) << " x " << get<1>(d) << " - " << get<2>(d) << "}";
}
};
int main()
{
using card_t = eve::cardinal_t<eve::wide<double>>;
eve::wide<double> wd = [](auto i) { return 1.25 * (i+1); };
eve::wide<float , card_t> wf = [](auto i) { return 1.f/(1+i); };
eve::wide<std::int16_t, card_t> wi = [](auto i) { return i+1; };
std::cout << "-> zip(wf0,wi,wd) = " << eve::zip(wf,wi,wd) << std::endl;
std::cout << "-> zip(eve::as<data_block>(),wf,wi,wd)) = " << eve::zip(eve::as<data_block>(),wf,wi,wd) << std::endl;
}
friend friend std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, field const &w) noexcept
CRTP base-class to declare operators for user-defined product type.
Definition product_type.hpp:148