Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/view to simd #1190

Merged
merged 6 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/seqan3/core/simd/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <seqan3/core/simd/simd_algorithm.hpp>
#include <seqan3/core/simd/simd_traits.hpp>
#include <seqan3/core/simd/simd.hpp>
#include <seqan3/core/simd/view_to_simd.hpp>

/*!\namespace seqan3::simd
* \brief The SeqAn3 namespace for simd data types, algorithms and meta functions.
Expand Down
40 changes: 20 additions & 20 deletions include/seqan3/core/simd/concept.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,34 @@ namespace seqan3::detail
template <typename simd_t>
marehr marked this conversation as resolved.
Show resolved Hide resolved
SEQAN3_CONCEPT Simd = requires (simd_t a, simd_t b)
{
typename simd_traits<simd_t>::scalar_type;
typename simd_traits<simd_t>::mask_type;
typename simd_traits<simd_t>::swizzle_type;
typename simd_traits<std::remove_reference_t<simd_t>>::scalar_type;
typename simd_traits<std::remove_reference_t<simd_t>>::mask_type;
typename simd_traits<std::remove_reference_t<simd_t>>::swizzle_type;

// require that static member variables are defined
requires std::Integral<decltype(simd_traits<simd_t>::length)>;
requires std::Integral<decltype(simd_traits<simd_t>::max_length)>;
requires std::Integral<decltype(simd_traits<std::remove_reference_t<simd_t>>::length)>;
requires std::Integral<decltype(simd_traits<std::remove_reference_t<simd_t>>::max_length)>;

// assume array access that returns a scalar_type type
{ a[0] } -> typename simd_traits<simd_t>::scalar_type;
{ a[0] } -> typename simd_traits<std::remove_reference_t<simd_t>>::scalar_type;

// require comparison operators
requires std::Same<decltype(a == b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a != b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a < b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a > b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a <= b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a >= b), typename simd_traits<simd_t>::mask_type>;
requires std::Same<decltype(a == b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;
requires std::Same<decltype(a != b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;
requires std::Same<decltype(a < b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;
requires std::Same<decltype(a > b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;
requires std::Same<decltype(a <= b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;
requires std::Same<decltype(a >= b), typename simd_traits<std::remove_reference_t<simd_t>>::mask_type>;

// require arithmetic operators
requires std::Same<decltype(a + b), simd_t>;
requires std::Same<decltype(a - b), simd_t>;
requires std::Same<decltype(a * b), simd_t>;
requires std::Same<decltype(a / b), simd_t>;
requires std::Same<decltype(a += b), simd_t &>;
requires std::Same<decltype(a -= b), simd_t &>;
requires std::Same<decltype(a *= b), simd_t &>;
requires std::Same<decltype(a /= b), simd_t &>;
requires std::Same<decltype(a + b), std::remove_reference_t<simd_t>>;
requires std::Same<decltype(a - b), std::remove_reference_t<simd_t>>;
requires std::Same<decltype(a * b), std::remove_reference_t<simd_t>>;
requires std::Same<decltype(a / b), std::remove_reference_t<simd_t>>;
requires std::Same<decltype(a += b), std::remove_reference_t<simd_t> &>;
requires std::Same<decltype(a -= b), std::remove_reference_t<simd_t> &>;
requires std::Same<decltype(a *= b), std::remove_reference_t<simd_t> &>;
requires std::Same<decltype(a /= b), std::remove_reference_t<simd_t> &>;
};
//!\endcond

Expand Down
45 changes: 42 additions & 3 deletions include/seqan3/core/simd/detail/builtin_simd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,15 @@ struct builtin_simd_traits_helper<builtin_simd_t>

//!\brief Whether builtin_simd_t is a builtin type or not?
//!\hideinitializer
static constexpr bool value = is_power_of_two(length) && std::is_same_v<builtin_simd_t, transformation_trait_or_t<builtin_simd<scalar_type, length>, void>>;
static constexpr bool value = is_power_of_two(length) &&
std::is_same_v<builtin_simd_t,
transformation_trait_or_t<builtin_simd<scalar_type, length>, void>>;
marehr marked this conversation as resolved.
Show resolved Hide resolved
};

/*!\brief This class inherits from std::true_type, **iff**
* seqan3::detail::builtin_simd<scalar_t, length>::type is a builtin simd type.
* \ingroup simd
* \tparam scalar_type The underlying type of a simd vector
* \tparam length_v The number of packed values in a simd vector
* \tparam builtin_simd_t The type to check.
*
* \include test/snippet/core/simd/detail/is_builtin_simd.cpp
* \sa https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
Expand All @@ -117,6 +118,14 @@ template <typename builtin_simd_t>
struct is_builtin_simd : std::bool_constant<builtin_simd_traits_helper<builtin_simd_t>::value>
{};

/*!\brief Helper variable to test whether a type is a simd builtin type.
* \ingroup simd
* \tparam builtin_simd_t The type to check.
* \see seqan3::detail::is_builtin_simd
*/
template <typename builtin_simd_t>
constexpr bool is_builtin_simd_v = is_builtin_simd<builtin_simd_t>::value;

/*!\brief This function specializes seqan3::detail::default_simd_max_length for
* seqan3::detail::builtin_simd.
* \ingroup simd
Expand All @@ -139,6 +148,36 @@ constexpr auto default_simd_max_length<builtin_simd> = []()
#endif
}();

/*!\brief This class inherits from std::true_type, **iff** the builtin simd type is supported by the current
* architecture.
* \ingroup simd
* \tparam builtin_simd_t The type to check.
*
* \details
*
* A builtin simd type is native if the following conditions are true:
* * the default simd max length is not equal to `0`.
* * the max length of the simd type is at least 16 (SSE4)
* * the max length of the simd type is at most 64 (AVX512)
*/
template <typename builtin_simd_t>
struct is_native_builtin_simd :
std::bool_constant<(default_simd_max_length<builtin_simd> != 0) &&
((builtin_simd_traits_helper<builtin_simd_t>::length *
sizeof(typename builtin_simd_traits_helper<builtin_simd_t>::scalar_type)) >= 16) &&
((builtin_simd_traits_helper<builtin_simd_t>::length *
sizeof(typename builtin_simd_traits_helper<builtin_simd_t>::scalar_type)) <= 64)>
marehr marked this conversation as resolved.
Show resolved Hide resolved
{};


/*!\brief Helper variable to test whether a type is a native simd builtin type.
* \ingroup simd
* \tparam builtin_simd_t The type to check.
* \see seqan3::detail::is_native_builtin_simd_v
*/
template <typename builtin_simd_t>
constexpr bool is_native_builtin_simd_v = is_native_builtin_simd<builtin_simd_t>::value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not evaluate it here as a lambda function? that would be more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also just define the bool constants if we don't need it as a type anyway. Otherwise it follows the STL way of providing unary type traits.


} // namespace seqan3::detail

namespace seqan3
Expand Down
102 changes: 102 additions & 0 deletions include/seqan3/core/simd/detail/simd_algorithm_sse4.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// -----------------------------------------------------------------------------------------------------
// Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
// Copyright (c) 2016-2019, Knut Reinert & MPI für molekulare Genetik
// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
// -----------------------------------------------------------------------------------------------------

/*!\file
* \brief Provides specific algorithm implementations for SSE4 instruction set.
* \author Rene Rahn <rene.rahn AT fu-berlin.de>
*/

#pragma once

#include <array>
#include <immintrin.h>

#include <seqan3/core/simd/concept.hpp>
#include <seqan3/core/simd/detail/builtin_simd.hpp>
#include <seqan3/core/simd/simd_traits.hpp>

namespace seqan3::detail
{

/*!\brief Transposes the given simd vector matrix using SSE4 intrinsics.
* \ingroup simd
* \tparam simd_t The simd vector type; must model seqan3::detail::Simd and must be a simd built-in and native type.
* \param[in,out] matrix The matrix that is transposed in place.
*
* \details
*
* Does inplace transformation using SSE4 intrinsics.
*/
template <Simd simd_t>
inline void transpose_matrix_sse4(std::array<simd_t, simd_traits<simd_t>::length> & matrix)
marehr marked this conversation as resolved.
Show resolved Hide resolved
{
static_assert(simd_traits<simd_t>::length == simd_traits<simd_t>::max_length, "Expects byte scalar type.");
static_assert(is_native_builtin_simd_v<simd_t>, "The passed simd vector is not a native SSE4 simd vector type.");
static_assert(is_builtin_simd_v<simd_t>, "The passed simd vector is not a builtin vector type.");

// we need a look-up table to reverse the lowest 4 bits
// in order to place the permute the transposed rows
constexpr std::array<char, 16> bit_reverse{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};

// transpose a 16x16 byte matrix
//
// matrix =
// A0 A1 A2 ... Ae Af
// B0 B1 B2 ... Be Bf
// ...
// P0 P1 P2 ... Pe Pf
__m128i tmp1[16];
for (int i = 0; i < 8; ++i)
{
tmp1[i] = _mm_unpacklo_epi8(reinterpret_cast<__m128i &>(matrix[2*i]),
reinterpret_cast<__m128i &>(matrix[2*i+1]));
tmp1[i+8] = _mm_unpackhi_epi8(reinterpret_cast<__m128i &>(matrix[2*i]),
reinterpret_cast<__m128i &>(matrix[2*i+1]));
}
// tmp1[0] = A0 B0 A1 B1 ... A7 B7
// tmp1[1] = C0 D0 C1 D1 ... C7 D7
// ...
// tmp1[7] = O0 P0 O1 P1 ... O7 P7
// tmp1[8] = A8 B8 A9 B9 ... Af Bf
// ...
// tmp1[15] = O8 P8 O9 P9 ... Of Pf
__m128i tmp2[16];
for (int i = 0; i < 8; ++i)
{
tmp2[i] = _mm_unpacklo_epi16(tmp1[2*i], tmp1[2*i+1]);
tmp2[i+8] = _mm_unpackhi_epi16(tmp1[2*i], tmp1[2*i+1]);
}
// tmp2[0] = A0 B0 C0 D0 ... A3 B3 C3 D3
// tmp2[1] = E0 F0 G0 H0 ... E3 F3 G3 H3
// ...
// tmp2[3] = M0 N0 O0 P0 ... M3 N3 O3 P3
// tmp2[4] = A8 B8 C8 D8 ... Ab Bb Cb Db
// ...
// tmp2[7] = M8 N8 O8 P8 ... Mb Nb Ob Pb
// tmp2[8] = A4 B4 C4 D4 ... A7 B7 C7 D7
// ..
// tmp2[12] = Ac Bc Cc Dc ... Af Bf Cf Df
// ...
// tmp2[15] = Mc Nc Oc Pc ... Mf Nf Of Pf
for (int i = 0; i < 8; ++i)
{
tmp1[i] = _mm_unpacklo_epi32(tmp2[2*i], tmp2[2*i+1]);
tmp1[i+8] = _mm_unpackhi_epi32(tmp2[2*i], tmp2[2*i+1]);
}
// tmp1[0] = A0 B0 .... H0 A1 B1 .... H1
// tmp1[1] = I0 J0 .... P0 I1 J1 .... P1
// ...
// tmp1[4] = A0 B0 .... H0 A1 B1 .... H1
// tmp1[1] = I0 J0 .... P0 I1 J1 .... P1
for (int i = 0; i < 8; ++i)
{
matrix[bit_reverse[i]] = reinterpret_cast<simd_t>(_mm_unpacklo_epi64(tmp1[2*i], tmp1[2*i+1]));
matrix[bit_reverse[i+8]] = reinterpret_cast<simd_t>(_mm_unpackhi_epi64(tmp1[2*i], tmp1[2*i+1]));
}
}

} // namespace seqan3::detail
Loading