| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| #include "main.h" |
| #include <Eigen/Core> |
| #include <cfenv> |
| |
| #ifndef EIGEN_VECTORIZE_SME |
| #error "vector_sme requires an SME2 target" |
| #endif |
| |
| template <typename Lhs, typename Rhs> |
| void sme_check_dot(const Lhs& lhs, const Rhs& rhs) { |
| using Scalar = typename Lhs::Scalar; |
| long double expected = 0, magnitude = 0; |
| for (Index i = 0; i < lhs.size(); ++i) { |
| const long double term = static_cast<long double>(lhs[i]) * static_cast<long double>(rhs[i]); |
| expected += term; |
| magnitude += numext::abs(term); |
| } |
| // gamma_(2*n+4) bounds multiplication and accumulation in both the kernel and reference. |
| const long double rounding = |
| (2 * static_cast<long double>(lhs.size()) + 4) * static_cast<long double>(NumTraits<Scalar>::epsilon()); |
| const long double bound = rounding / (1 - rounding) * magnitude; |
| const Scalar actual = lhs.dot(rhs); |
| VERIFY(rounding < 1 && (numext::isfinite)(bound)); |
| VERIFY((numext::isfinite)(actual)); |
| VERIFY(numext::abs(static_cast<long double>(actual) - expected) <= bound); |
| } |
| |
| template <typename Scalar> |
| void sme_vectors() { |
| using Vec = Vector<Scalar, Dynamic>; |
| using Row = Matrix<Scalar, 1, Dynamic>; |
| using Strided = Map<Vec, 0, InnerStride<Dynamic>>; |
| STATIC_CHECK((internal::sme_dot_supported<Vec, Row>::value)); |
| STATIC_CHECK((internal::sme_dot_supported<Strided, Vec>::value)); |
| STATIC_CHECK((!internal::sme_dot_supported<decltype(std::declval<Vec>() + std::declval<Vec>()), Vec>::value)); |
| STATIC_CHECK((!internal::sme_vector_scalar<std::complex<Scalar>>::value)); |
| STATIC_CHECK((!internal::sme_vector_scalar<int>::value)); |
| |
| std::vector<Index> sizes; |
| for (Index n = 0; n <= 260; ++n) { |
| sizes.push_back(n); |
| sizes.push_back(32768 + n); |
| } |
| for (Index n : {511, 512, 513, 1023, 1024, 1025, 2047, 2048, 2049, 4095, 4096, |
| 4097, 8191, 8192, 8193, 16383, 16384, 16385, 32767, 32768, 32769}) |
| sizes.push_back(n); |
| for (Index n : sizes) { |
| Vec x = Vec::Ones(n), storage = Vec::Ones(n + 2); |
| VERIFY_IS_EQUAL(x.dot(storage.head(n)), Scalar(n)); |
| x.setRandom(); |
| storage.setRandom(); |
| const Vec before = storage; |
| auto y = storage.segment(1, n); |
| sme_check_dot(x, y); |
| sme_check_dot(x.transpose(), y); |
| for (int side = 0; side < 2; ++side) { |
| storage = before; |
| if (side == 0) |
| y += Scalar(0.75) * x; |
| else |
| y += x * Scalar(0.75); |
| for (Index i = 0; i < n; ++i) { |
| const long double expected = static_cast<long double>(before[i + 1]) + 0.75L * static_cast<long double>(x[i]); |
| const long double magnitude = |
| numext::abs(static_cast<long double>(before[i + 1])) + 0.75L * numext::abs(static_cast<long double>(x[i])); |
| VERIFY(numext::abs(static_cast<long double>(y[i]) - expected) <= |
| 4 * static_cast<long double>(NumTraits<Scalar>::epsilon()) * magnitude); |
| } |
| VERIFY_IS_EQUAL(storage[0], before[0]); |
| VERIFY_IS_EQUAL(storage[n + 1], before[n + 1]); |
| } |
| Vec alias = x; |
| alias += Scalar(0.75) * alias; |
| for (Index i = 0; i < n; ++i) { |
| const long double expected = static_cast<long double>(x[i]) * 1.75L; |
| VERIFY(numext::abs(static_cast<long double>(alias[i]) - expected) <= |
| 4 * static_cast<long double>(NumTraits<Scalar>::epsilon()) * numext::abs(expected)); |
| } |
| Strided contiguous(x.data(), n, InnerStride<Dynamic>(1)); |
| sme_check_dot(contiguous, y); |
| } |
| Vec x = Vec::Random(65538), y = Vec::Random(32769), expected = x; |
| Strided strided(x.data(), y.size(), InnerStride<Dynamic>(2)); |
| Strided negative(x.data() + x.size() - 2, y.size(), InnerStride<Dynamic>(-2)); |
| sme_check_dot(strided, y); |
| sme_check_dot(negative, y); |
| for (Index i = 0; i < y.size(); ++i) expected[2 * i] += Scalar(0.75) * y[i]; |
| strided += Scalar(0.75) * y; |
| VERIFY_IS_APPROX(x, expected); |
| sme_check_dot(-y, strided); |
| sme_check_dot(y + y, strided); |
| sme_check_dot(y.reverse(), strided); |
| VERIFY_RAISES_ASSERT(y.dot(x)); |
| |
| for (int pattern = 0; pattern < 3; ++pattern) { |
| Vec a = Vec::Random(65537), b = Vec::Random(65537); |
| for (Index i = 0; i < a.size(); ++i) { |
| if (pattern == 0) b[i] = (i & 1) ? -a[i] : a[i]; |
| if (pattern == 1) a[i] = numext::ldexp(a[i], int(i % 81) - 40); |
| if (pattern == 2) { |
| a[i] = Scalar(1); |
| b[i] = i % 3 == 0 ? Scalar(1) / NumTraits<Scalar>::epsilon() |
| : i % 3 == 1 ? Scalar(1) |
| : -Scalar(1) / NumTraits<Scalar>::epsilon(); |
| } |
| } |
| sme_check_dot(a, b); |
| } |
| for (Index n : {524287, 524288, 524289, 1048575, 1048576, 1048577}) { |
| Vec a = Vec::Ones(n), b = Vec::Ones(n); |
| VERIFY_IS_EQUAL(a.dot(b), Scalar(n)); |
| b += Scalar(0.75) * a; |
| VERIFY((b.array() == Scalar(1.75)).all()); |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_vector_cache_sizes() { |
| using Vec = Vector<Scalar, Dynamic>; |
| const std::ptrdiff_t old_l1 = l1CacheSize(), old_l2 = l2CacheSize(), old_l3 = l3CacheSize(); |
| const Index n = 32769; |
| const std::ptrdiff_t bytes = n * sizeof(Scalar); |
| const Vec x = Vec::Ones(n); |
| Vec y(n); |
| for (std::ptrdiff_t l2 : {bytes, bytes - 1, std::ptrdiff_t(0), std::ptrdiff_t(-1), bytes + 1}) { |
| setCpuCacheSizes(old_l1, l2, old_l3); |
| VERIFY_IS_EQUAL(internal::sme_vector_size_suitable<Scalar>(n), l2 >= bytes); |
| VERIFY(!internal::sme_vector_size_suitable<Scalar>((std::numeric_limits<Index>::max)())); |
| VERIFY_IS_EQUAL(x.dot(x), Scalar(n)); |
| y.setOnes(); |
| y += Scalar(0.75) * x; |
| VERIFY((y.array() == Scalar(1.75)).all()); |
| y.setOnes(); |
| y += x * Scalar(0.75); |
| VERIFY((y.array() == Scalar(1.75)).all()); |
| } |
| for (std::ptrdiff_t l1 : {bytes, bytes - 1, std::ptrdiff_t(0), std::ptrdiff_t(-1), bytes + 1}) { |
| setCpuCacheSizes(l1, bytes, old_l3); |
| const bool suitable = l1 > 0 && l1 <= bytes; |
| VERIFY_IS_EQUAL(internal::sme_vector_size_suitable<Scalar>(n), suitable); |
| y.setOnes(); |
| VERIFY_IS_EQUAL(internal::sme_try_axpy(y, x, Scalar(0.75)), suitable); |
| VERIFY((y.array() == (suitable ? Scalar(1.75) : Scalar(1))).all()); |
| VERIFY_IS_EQUAL(x.dot(x), Scalar(n)); |
| } |
| for (std::ptrdiff_t l1 : {2 * bytes - 1, 2 * bytes, 2 * bytes + 1}) { |
| setCpuCacheSizes(l1, bytes, old_l3); |
| VERIFY_IS_EQUAL((internal::sme_vector_size_suitable<Scalar, 2>(n)), l1 <= 2 * bytes); |
| VERIFY_IS_EQUAL(x.dot(x), Scalar(n)); |
| } |
| #ifndef EIGEN_USE_BLAS |
| const std::ptrdiff_t matrix_bytes = 128 * 4 * sizeof(Scalar); |
| for (std::ptrdiff_t l1 : {matrix_bytes - 1, matrix_bytes, matrix_bytes + 1, std::ptrdiff_t(0)}) { |
| setCpuCacheSizes(l1, old_l2, old_l3); |
| VERIFY_IS_EQUAL(internal::sme_gemv_size_suitable<Scalar>(Index(128), Index(4)), l1 > 0 && l1 <= matrix_bytes); |
| VERIFY(!internal::sme_gemv_size_suitable<Scalar>(Index(0), Index(4))); |
| } |
| #endif |
| setCpuCacheSizes(old_l1, old_l2, old_l3); |
| } |
| |
| template <typename Scalar> |
| void sme_vector_scalar_factors() { |
| using Vec = Vector<Scalar, Dynamic>; |
| const Index n = 32769; |
| Vec x = Vec::Constant(n, Scalar(0.25)), y = Vec::Zero(n); |
| y += x.cwiseProduct(x); |
| VERIFY((y.array() == Scalar(0.0625)).all()); |
| y.setZero(); |
| y += Vec::Constant(n, Scalar(0.75)).cwiseProduct(x); |
| VERIFY((y.array() == Scalar(0.1875)).all()); |
| |
| // Folding the two scale factors would overflow even though every output is finite. |
| const Scalar large = (std::numeric_limits<Scalar>::max)() / Scalar(2); |
| y.setZero(); |
| y += large * (Scalar(4) * x); |
| VERIFY((y.array() == large).all()); |
| y.setZero(); |
| y += (x * Scalar(4)) * large; |
| VERIFY((y.array() == large).all()); |
| |
| Array<Scalar, Dynamic, 1> a = x.array(), b = Array<Scalar, Dynamic, 1>::Zero(n); |
| b += Scalar(0.75) * a; |
| VERIFY((b == Scalar(0.1875)).all()); |
| b.setZero(); |
| b += a * Scalar(0.75); |
| VERIFY((b == Scalar(0.1875)).all()); |
| } |
| |
| template <typename Scalar> |
| void sme_matrix_vectors() { |
| using Vec = Vector<Scalar, Dynamic>; |
| using Mat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>; |
| for (Index rows : {127, 128, 129, 255, 256, 257, 513}) { |
| for (Index cols : {3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65, 129}) { |
| for (Index padding : {0, 3}) { |
| Mat storage = Mat::Random(rows + padding, cols); |
| auto a = storage.topRows(rows); |
| Vec x = Vec::Random(cols), original = Vec::Random(rows), result(rows); |
| for (Scalar alpha : {Scalar(0), Scalar(0.75), Scalar(-1)}) { |
| result = original; |
| result.noalias() += alpha * a * x; |
| for (Index i = 0; i < rows; ++i) { |
| long double expected = static_cast<long double>(original[i]), magnitude = numext::abs(expected); |
| for (Index j = 0; j < cols; ++j) { |
| const long double term = |
| static_cast<long double>(alpha) * static_cast<long double>(a(i, j)) * static_cast<long double>(x[j]); |
| expected += term; |
| magnitude += numext::abs(term); |
| } |
| const long double bound = |
| 8 * (cols + 1) * static_cast<long double>(NumTraits<Scalar>::epsilon()) * magnitude; |
| VERIFY((numext::isfinite)(bound)); |
| VERIFY(numext::abs(static_cast<long double>(result[i]) - expected) <= bound); |
| } |
| } |
| Vec strided_storage = Vec::Random(2 * rows), untouched = strided_storage; |
| Map<Vec, 0, InnerStride<2>> strided(strided_storage.data(), rows); |
| Vec reference = strided; |
| reference.noalias() += a * x; |
| strided.noalias() += a * x; |
| VERIFY_IS_APPROX(strided, reference); |
| for (Index i = 0; i < rows; ++i) VERIFY_IS_EQUAL(strided_storage[2 * i + 1], untouched[2 * i + 1]); |
| } |
| } |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_vector_alignment() { |
| using Vec = Vector<Scalar, Dynamic>; |
| const Index padding = 64 / sizeof(Scalar); |
| for (Index tail : {0, 1, 15, 31, 63, 127, 255, 256}) { |
| const Index n = 32768 + tail; |
| Vec x_storage = Vec::Ones(n + padding), y_storage(n + 2 * padding); |
| for (Index offset = 0; offset < padding; ++offset) { |
| y_storage.setConstant(Scalar(2)); |
| const auto x = x_storage.segment(offset, n); |
| auto y = y_storage.segment(padding + offset, n); |
| VERIFY(internal::sme_try_axpy(y, x, Scalar(0.75))); |
| VERIFY((y.array() == Scalar(2.75)).all()); |
| VERIFY((y_storage.head(padding + offset).array() == Scalar(2)).all()); |
| VERIFY((y_storage.tail(padding - offset).array() == Scalar(2)).all()); |
| VERIFY_IS_EQUAL(x.dot(y), Scalar(2.75) * Scalar(n)); |
| } |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_gemv_tails() { |
| using Vec = Vector<Scalar, Dynamic>; |
| using Mat = Matrix<Scalar, Dynamic, Dynamic>; |
| // Every predicated chunk pattern of a row block, including at the largest architectural SVL. |
| for (Index rows = 128; rows <= 1100; ++rows) { |
| const Mat a = Mat::Ones(rows, 65); |
| const Vec x = Vec::Ones(65); |
| Vec storage = Vec::Ones(rows + 2); |
| auto y = storage.segment(1, rows); |
| y.noalias() += Scalar(0.75) * a * x; |
| VERIFY((y.array() == Scalar(49.75)).all()); |
| VERIFY_IS_EQUAL(storage[0], Scalar(1)); |
| VERIFY_IS_EQUAL(storage[rows + 1], Scalar(1)); |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_vector_special_values() { |
| using Vec = Vector<Scalar, Dynamic>; |
| using Mat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>; |
| const Scalar inf = NumTraits<Scalar>::infinity(), nan = NumTraits<Scalar>::quiet_NaN(); |
| const Scalar tiny = (std::numeric_limits<Scalar>::denorm_min)(); |
| for (Index pos : {0, 7, 31, 63, 127, 255, 256, 32768}) { |
| Vec x = Vec::Zero(32769), y = Vec::Ones(32769), result(32769); |
| for (Scalar special : {inf, -inf, nan, tiny, -tiny, Scalar(-0.0)}) { |
| x[pos] = special; |
| const Scalar actual = x.dot(y); |
| if ((numext::isnan)(special)) |
| VERIFY((numext::isnan)(actual)); |
| else |
| VERIFY_IS_EQUAL(actual, special); |
| result.setZero(); |
| result += Scalar(1) * x; |
| for (Index i = 0; i < result.size(); ++i) { |
| const Scalar expected = x[i] + Scalar(0); |
| if ((numext::isnan)(expected)) |
| VERIFY((numext::isnan)(result[i])); |
| else { |
| VERIFY_IS_EQUAL(result[i], expected); |
| VERIFY_IS_EQUAL(std::signbit(result[i]), std::signbit(expected)); |
| } |
| } |
| } |
| } |
| for (Scalar special : {inf, -inf, nan, tiny, -tiny}) { |
| Mat a = Mat::Zero(257, 65); |
| Vec x = Vec::Ones(65), y = Vec::Zero(257); |
| a(0, 0) = a(256, 64) = special; |
| y.noalias() += a * x; |
| for (Index i = 0; i < y.size(); ++i) { |
| const Scalar expected = i == 0 || i == 256 ? special : Scalar(0); |
| if ((numext::isnan)(expected)) |
| VERIFY((numext::isnan)(y[i])); |
| else |
| VERIFY_IS_EQUAL(y[i], expected); |
| } |
| y.setOnes(); |
| y.noalias() += Scalar(0) * a * x; |
| VERIFY((y.array() == Scalar(1)).all()); |
| } |
| Vec x = Vec::Zero(32769), y = Vec::Zero(32769); |
| x[0] = inf; |
| VERIFY((numext::isnan)(x.dot(y))); |
| } |
| |
| template <typename Scalar> |
| void sme_gemv_scaled_range() { |
| using Mat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>; |
| using Vec = Vector<Scalar, Dynamic>; |
| const Scalar largest = (std::numeric_limits<Scalar>::max)(); |
| const std::ptrdiff_t l1 = l1CacheSize(), l2 = l2CacheSize(), l3 = l3CacheSize(); |
| for (std::ptrdiff_t cache : {l1, std::ptrdiff_t(0)}) { |
| setCpuCacheSizes(cache, l2, l3); |
| for (Index rows : {256, 257}) { |
| for (Index cols : {128, 129}) { |
| Mat matrix = Mat::Constant(rows, cols, largest / Scalar(64)); |
| Vec rhs = Vec::Ones(cols), result = Vec::Zero(rows); |
| result.noalias() += Scalar(0.25) * matrix * rhs; |
| const Scalar expected = (largest / Scalar(256)) * Scalar(cols); |
| const Scalar bound = Scalar(4 * cols) * NumTraits<Scalar>::epsilon(); |
| for (Index i = 0; i < rows; ++i) { |
| VERIFY((numext::isfinite)(result[i])); |
| VERIFY(numext::abs(result[i] / expected - Scalar(1)) <= bound); |
| } |
| } |
| } |
| } |
| setCpuCacheSizes(l1, l2, l3); |
| } |
| |
| template <typename Scalar> |
| void sme_dot_signed_zero() { |
| using Vec = Vector<Scalar, Dynamic>; |
| const Scalar tiny = (std::numeric_limits<Scalar>::denorm_min)(); |
| for (Index n : {32768, 32769, 32799, 33791}) { |
| Vec x = Vec::Constant(n, Scalar(-0.0)), y = Vec::Ones(n); |
| const Scalar negative = x.dot(y); |
| VERIFY_IS_EQUAL(negative, Scalar(0)); |
| VERIFY((numext::signbit)(negative)); |
| x.setZero(); |
| const Scalar positive = x.dot(y); |
| VERIFY_IS_EQUAL(positive, Scalar(0)); |
| VERIFY(!(numext::signbit)(positive)); |
| // A single positive-zero product, in the first group or the last element, gives +0. |
| for (Index pos : {Index(0), n - 1}) { |
| x.setConstant(Scalar(-0.0)); |
| x[pos] = Scalar(0); |
| const Scalar mixed = x.dot(y); |
| VERIFY_IS_EQUAL(mixed, Scalar(0)); |
| VERIFY(!(numext::signbit)(mixed)); |
| } |
| // Exact cancellation, across lanes (adjacent terms) or within a fused lane (halves), rounds to +0. |
| for (int within_lane = 0; within_lane < 2; ++within_lane) { |
| x.setConstant(Scalar(-0.0)); |
| const Index half = n / 2; |
| for (Index i = 0; i < half; ++i) { |
| x[within_lane ? i : 2 * i] = Scalar(1); |
| x[within_lane ? half + i : 2 * i + 1] = Scalar(-1); |
| } |
| const Scalar cancelled = x.dot(y); |
| VERIFY_IS_EQUAL(cancelled, Scalar(0)); |
| VERIFY(!(numext::signbit)(cancelled)); |
| } |
| // Products that underflow to -0 keep the sign of every fused partial sum. |
| x.setConstant(-tiny); |
| const Scalar underflow = x.dot(Vec::Constant(n, Scalar(0.25))); |
| VERIFY_IS_EQUAL(underflow, Scalar(0)); |
| VERIFY((numext::signbit)(underflow)); |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_dot_directed_rounding() { |
| #if EIGEN_COMP_CLANG |
| #pragma STDC FENV_ACCESS ON |
| #endif |
| using Vec = Vector<Scalar, Dynamic>; |
| const int saved = std::fegetround(); |
| const Index n = 32769; |
| const Vec y = Vec::Ones(n); |
| for (int mode : {FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO}) { |
| VERIFY_IS_EQUAL(std::fesetround(mode), 0); |
| // IEEE 754 6.3: x + x keeps the sign of x; an exact zero sum of opposite signs is -0 only when rounding down. |
| Vec x = Vec::Zero(n); |
| const Scalar positive = x.dot(y); |
| x.setConstant(Scalar(-0.0)); |
| const Scalar negative = x.dot(y); |
| x[0] = Scalar(0); |
| const Scalar mixed = x.dot(y); |
| std::fesetround(saved); |
| VERIFY(positive == Scalar(0) && !std::signbit(positive)); |
| VERIFY(negative == Scalar(0) && std::signbit(negative)); |
| VERIFY(mixed == Scalar(0) && std::signbit(mixed) == (mode == FE_DOWNWARD)); |
| } |
| } |
| |
| template <typename Scalar> |
| void sme_vector_exception_state() { |
| #if EIGEN_COMP_CLANG |
| #pragma STDC FENV_ACCESS ON |
| #endif |
| using Vec = Vector<Scalar, Dynamic>; |
| using Mat = Matrix<Scalar, Dynamic, Dynamic, ColMajor>; |
| std::fenv_t saved; |
| std::fegetenv(&saved); |
| const Scalar inf = NumTraits<Scalar>::infinity(), largest = (std::numeric_limits<Scalar>::max)(); |
| // The kernels leave the caller's flags as they were, whether or not an SVE tail or row remainder follows the |
| // ZA loop: exact inputs, then inf * 0 (invalid), then max * 2 (overflow). |
| for (Index n : {32768, 32769}) { |
| Vec x(n), y(n); |
| Mat matrix(n == 32768 ? 256 : 257, 128); |
| Vec rhs(128), result(matrix.rows()); |
| for (int input = 0; input < 3; ++input) { |
| const Scalar value = input == 0 ? Scalar(1) : input == 1 ? inf : largest; |
| const Scalar factor = input == 0 ? Scalar(1) : input == 1 ? Scalar(0) : Scalar(2); |
| for (int prior : {0, FE_DIVBYZERO}) { |
| for (int operation = 0; operation < 3; ++operation) { |
| x.setConstant(value); |
| y.setConstant(factor); |
| matrix.setConstant(value); |
| rhs.setConstant(factor); |
| result.setZero(); |
| std::feclearexcept(FE_ALL_EXCEPT); |
| std::feraiseexcept(prior); |
| Scalar actual; |
| if (operation == 0) { |
| actual = x.dot(y); |
| } else if (operation == 1) { |
| y += factor * x; |
| actual = y[n - 1]; |
| } else { |
| result.noalias() += matrix * rhs; |
| actual = result[result.size() - 1]; |
| } |
| const int flags = std::fetestexcept(FE_ALL_EXCEPT); |
| VERIFY_IS_EQUAL(flags, prior); |
| if (input == 1) { |
| VERIFY((numext::isnan)(actual)); |
| } else if (input == 2) { |
| VERIFY_IS_EQUAL(actual, inf); |
| } else { |
| VERIFY_IS_EQUAL(actual, operation == 0 ? Scalar(n) : operation == 1 ? Scalar(2) : Scalar(128)); |
| } |
| } |
| } |
| } |
| } |
| // Longer calls are more likely to be preempted while streaming. |
| Vec x = Vec::Ones(Index(1) << 20), y = Vec::Ones(Index(1) << 20); |
| for (int repetition = 0; repetition < 64; ++repetition) { |
| std::feclearexcept(FE_ALL_EXCEPT); |
| const Scalar dot = x.dot(y); |
| y += Scalar(repetition % 2 ? -0.75 : 0.75) * x; |
| VERIFY_IS_EQUAL(std::fetestexcept(FE_ALL_EXCEPT), 0); |
| VERIFY_IS_EQUAL(dot, Scalar(repetition % 2 ? 1.75 : 1) * Scalar(x.size())); |
| } |
| std::fesetenv(&saved); |
| } |
| |
| EIGEN_DECLARE_TEST(vector_sme) { |
| const std::ptrdiff_t l1 = l1CacheSize(), l2 = l2CacheSize(), l3 = l3CacheSize(); |
| setCpuCacheSizes(128 * 1024, 4 * 1024 * 1024, l3); |
| CALL_SUBTEST_1(sme_vector_cache_sizes<float>()); |
| CALL_SUBTEST_1(sme_vector_scalar_factors<float>()); |
| CALL_SUBTEST_1(sme_vectors<float>()); |
| CALL_SUBTEST_1(sme_matrix_vectors<float>()); |
| CALL_SUBTEST_1(sme_vector_alignment<float>()); |
| CALL_SUBTEST_1(sme_gemv_tails<float>()); |
| CALL_SUBTEST_1(sme_vector_special_values<float>()); |
| CALL_SUBTEST_3(sme_gemv_scaled_range<float>()); |
| CALL_SUBTEST_3(sme_dot_signed_zero<float>()); |
| CALL_SUBTEST_3(sme_dot_directed_rounding<float>()); |
| CALL_SUBTEST_3(sme_vector_exception_state<float>()); |
| #ifdef EIGEN_VECTORIZE_SME_F64F64 |
| CALL_SUBTEST_2(sme_vector_cache_sizes<double>()); |
| CALL_SUBTEST_2(sme_vector_scalar_factors<double>()); |
| CALL_SUBTEST_2(sme_vectors<double>()); |
| CALL_SUBTEST_2(sme_matrix_vectors<double>()); |
| CALL_SUBTEST_2(sme_vector_alignment<double>()); |
| CALL_SUBTEST_2(sme_gemv_tails<double>()); |
| CALL_SUBTEST_2(sme_vector_special_values<double>()); |
| CALL_SUBTEST_4(sme_gemv_scaled_range<double>()); |
| CALL_SUBTEST_4(sme_dot_signed_zero<double>()); |
| CALL_SUBTEST_4(sme_dot_directed_rounding<double>()); |
| CALL_SUBTEST_4(sme_vector_exception_state<double>()); |
| #endif |
| setCpuCacheSizes(l1, l2, l3); |
| } |