From d3accbd3c061e41d8e27832dc5ecb1281891fa57 Mon Sep 17 00:00:00 2001 From: Lam Pham-Sy Date: Fri, 14 Jun 2019 06:07:12 +0200 Subject: [PATCH 1/2] Add dependent header for vectorized radix2 FFT --- src/simd_radix2_fft.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/simd_radix2_fft.h b/src/simd_radix2_fft.h index 66dafc17..72769b1e 100644 --- a/src/simd_radix2_fft.h +++ b/src/simd_radix2_fft.h @@ -33,6 +33,8 @@ #include +#include "vec_buffers.h" + namespace quadiron { namespace simd { From 50539ad63fecbebb6706c2966372f2cc7ec3663e Mon Sep 17 00:00:00 2001 From: Lam Pham-Sy Date: Fri, 14 Jun 2019 06:07:59 +0200 Subject: [PATCH 2/2] Add unit tests for vectorised FNT operations --- test/CMakeLists.txt | 4 + test/simd/test_simd_fnt.cpp | 310 ++++++++++++++++++++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 test/simd/test_simd_fnt.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b97ac468..753ddf08 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -134,6 +134,10 @@ set(TEST_SRC FORCE ) +if (USE_SIMD STREQUAL "ON" OR USE_SIMD STREQUAL "SSE" OR USE_SIMD STREQUAL "AVX") + list(APPEND TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/simd/test_simd_fnt.cpp) +endif() + add_executable(${UNIT_TESTS} ${TEST_SRC} ) diff --git a/test/simd/test_simd_fnt.cpp b/test/simd/test_simd_fnt.cpp new file mode 100644 index 00000000..ef5df083 --- /dev/null +++ b/test/simd/test_simd_fnt.cpp @@ -0,0 +1,310 @@ +/* + * Copyright 2017-2019 Scality + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include + +#include + +#include "arith.h" +#include "core.h" +#include "misc.h" + +#ifdef QUADIRON_USE_SIMD + +#include "simd.h" +#include "simd/simd.h" +#include "simd_fnt.h" + +namespace simd = quadiron::simd; + +template +class SimdTestFnt : public ::testing::Test { + public: + SimdTestFnt() + { + if (sizeof(T) == 2) { + this->q = 257; + } else if (sizeof(T) == 4) { + this->q = static_cast(65537); + } else { + throw "Wrong TypeParam for SimdTestFnt tests"; + } + + this->distribution = + std::make_unique>(0, q - 1); + } + + simd::VecType rand_vec(T lower = 0, T upper_bound = 0) + { + const size_t n = simd::countof(); + T buf[n]; + simd::VecType* vec = reinterpret_cast(buf); + + T max = upper_bound ? upper_bound : q - 1; + std::uniform_int_distribution rand(lower, max); + + for (unsigned i = 0; i < n; i++) { + buf[i] = rand(quadiron::prng()); + } + + return vec[0]; + } + + simd::VecType copy(simd::VecType x) + { + const size_t n = simd::countof(); + T buf[n]; + T val[n]; + simd::VecType* vec = reinterpret_cast(buf); + + simd::store_to_mem(reinterpret_cast(val), x); + std::copy_n(val, n, buf); + + return vec[0]; + } + + bool is_equal(simd::VecType x, simd::VecType y) + { + return simd::is_zero(simd::bit_xor(x, y)); + } + + simd::VecType mod_mul(simd::VecType x, simd::VecType y) + { + const size_t n = simd::countof(); + T _x[n]; + T _y[n]; + T _z[n]; + simd::store_to_mem(reinterpret_cast(_x), x); + simd::store_to_mem(reinterpret_cast(_y), y); + for (unsigned i = 0; i < n; i++) { + _z[i] = (quadiron::DoubleSizeVal(_x[i]) * _y[i]) % q; + } + + simd::VecType* vec = reinterpret_cast(_z); + + return vec[0]; + } + + /* Butterfly Cooley-Tukey operation + * x <- x + c * y + * y <- x - c * y + */ + void butterfly_ct(simd::VecType c, simd::VecType& x, simd::VecType& y) + { + const size_t n = simd::countof(); + T c_buf[n]; + T x_buf[n]; + T y_buf[n]; + + simd::store_to_mem(reinterpret_cast(c_buf), c); + simd::store_to_mem(reinterpret_cast(x_buf), x); + simd::store_to_mem(reinterpret_cast(y_buf), y); + + for (unsigned i = 0; i < n; ++i) { + T mul = (quadiron::DoubleSizeVal(c_buf[i]) * y_buf[i]) % q; + T u = (x_buf[i] + mul) % q; + T v = x_buf[i] >= mul ? x_buf[i] - mul : q + x_buf[i] - mul; + + x_buf[i] = u; + y_buf[i] = v; + } + + x = simd::load_to_reg(reinterpret_cast(x_buf)); + y = simd::load_to_reg(reinterpret_cast(y_buf)); + } + + /* Butterfly Genteleman-Sande operation + * x <- x + y + * y <- c * (x - y) + */ + void butterfly_gs(simd::VecType c, simd::VecType& x, simd::VecType& y) + { + const size_t n = simd::countof(); + T c_buf[n]; + T x_buf[n]; + T y_buf[n]; + + simd::store_to_mem(reinterpret_cast(c_buf), c); + simd::store_to_mem(reinterpret_cast(x_buf), x); + simd::store_to_mem(reinterpret_cast(y_buf), y); + + for (unsigned i = 0; i < n; ++i) { + T sub = x_buf[i] >= y_buf[i] ? x_buf[i] - y_buf[i] + : q + x_buf[i] - y_buf[i]; + T u = (x_buf[i] + y_buf[i]) % q; + T v = (quadiron::DoubleSizeVal(c_buf[i]) * sub) % q; + x_buf[i] = u; + y_buf[i] = v; + } + + x = simd::load_to_reg(reinterpret_cast(x_buf)); + y = simd::load_to_reg(reinterpret_cast(y_buf)); + } + + /* Butterfly Genteleman-Sande simple operation where y = 0 + * y <- c * x + */ + void butterfly_simple_gs(simd::VecType c, simd::VecType& x) + { + const size_t n = simd::countof(); + T c_buf[n]; + T x_buf[n]; + + simd::store_to_mem(reinterpret_cast(c_buf), c); + simd::store_to_mem(reinterpret_cast(x_buf), x); + + for (unsigned i = 0; i < n; ++i) { + x_buf[i] = (quadiron::DoubleSizeVal(c_buf[i]) * x_buf[i]) % q; + } + + x = simd::load_to_reg(reinterpret_cast(x_buf)); + } + + T q; + std::unique_ptr> distribution; +}; + +using AllTypes = ::testing::Types; +TYPED_TEST_CASE(SimdTestFnt, AllTypes); + +TYPED_TEST(SimdTestFnt, TestModAddSub) // NOLINT +{ + for (unsigned i = 0; i < 100; ++i) { + simd::VecType x = this->rand_vec(this->q / 2); + simd::VecType y = this->rand_vec(this->q / 2); + + simd::VecType u = simd::mod_add(x, y); + simd::VecType v = simd::mod_sub(u, x); + simd::VecType z = simd::mod_add(v, x); + + ASSERT_TRUE(this->is_equal(y, v)); + ASSERT_TRUE(this->is_equal(u, z)); + } +} + +TYPED_TEST(SimdTestFnt, TestModNeg) // NOLINT +{ + for (unsigned i = 0; i < 100; ++i) { + simd::VecType x = this->rand_vec(); + + simd::VecType y = simd::mod_neg(x); + simd::VecType u = simd::mod_sub(simd::zero(), x); + + ASSERT_TRUE(this->is_equal(u, y)); + } +} + +TYPED_TEST(SimdTestFnt, TestModMul) // NOLINT +{ + for (unsigned i = 0; i < 100; ++i) { + simd::VecType x = this->rand_vec(0, this->q - 1); + simd::VecType y = this->rand_vec(); + + // check mod_mul + simd::VecType u = simd::mod_mul(x, y); + simd::VecType v = this->mod_mul(x, y); + ASSERT_TRUE(this->is_equal(v, u)); + + // check mod_mul_safe + simd::VecType a = simd::card_minus_one(); + simd::VecType b = simd::card_minus_one(); + simd::VecType c = this->mod_mul(a, b); + simd::VecType d = simd::mod_mul(a, b); + simd::VecType e = simd::mod_mul_safe(a, b); + + ASSERT_FALSE(this->is_equal(c, d)); + ASSERT_TRUE(this->is_equal(c, e)); + } +} + +TYPED_TEST(SimdTestFnt, TestButterflyCt) // NOLINT +{ + for (unsigned i = 0; i < 100; ++i) { + std::vector r_values = { + 1, + static_cast( + this->distribution->operator()(quadiron::prng())), + static_cast(this->q - 1)}; + + for (const TypeParam r : r_values) { + const simd::CtGsCase ct_case = + simd::get_case(r, this->q); + + simd::VecType c = simd::set_one(r); + + simd::VecType x = this->rand_vec(); + simd::VecType y = this->rand_vec(); + simd::VecType x_expected = this->copy(x); + simd::VecType y_expected = this->copy(y); + + this->butterfly_ct(c, x_expected, y_expected); + simd::butterfly_ct(ct_case, c, x, y); + + ASSERT_TRUE(this->is_equal(x_expected, x)); + ASSERT_TRUE(this->is_equal(y_expected, y)); + } + } +} + +TYPED_TEST(SimdTestFnt, TestButterflyGs) // NOLINT +{ + for (unsigned i = 0; i < 100; ++i) { + std::vector r_values = { + 1, + static_cast( + this->distribution->operator()(quadiron::prng())), + static_cast(this->q - 1)}; + + for (const TypeParam r : r_values) { + const simd::CtGsCase ct_case = + simd::get_case(r, this->q); + + simd::VecType c = simd::set_one(r); + + simd::VecType x = this->rand_vec(); + simd::VecType y = this->rand_vec(); + simd::VecType x_expected = this->copy(x); + simd::VecType y_expected = this->copy(y); + + this->butterfly_gs(c, x_expected, y_expected); + simd::butterfly_gs(ct_case, c, x, y); + + ASSERT_TRUE(this->is_equal(x_expected, x)); + ASSERT_TRUE(this->is_equal(y_expected, y)); + + this->butterfly_simple_gs(c, x_expected); + simd::butterfly_simple_gs(ct_case, c, x); + + ASSERT_TRUE(this->is_equal(x_expected, x)); + } + } +} + +#endif