copy_test.cc (6466B)
1 // Copyright 2022 Google LLC 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include <stddef.h> 17 18 #include "hwy/aligned_allocator.h" 19 20 // clang-format off 21 #undef HWY_TARGET_INCLUDE 22 #define HWY_TARGET_INCLUDE "hwy/contrib/algo/copy_test.cc" 23 #include "hwy/foreach_target.h" // IWYU pragma: keep 24 #include "hwy/highway.h" 25 #include "hwy/contrib/algo/copy-inl.h" 26 #include "hwy/tests/test_util-inl.h" 27 // clang-format on 28 29 // If your project requires C++14 or later, you can ignore this and pass lambdas 30 // directly to Transform, without requiring an lvalue as we do here for C++11. 31 #if __cplusplus < 201402L 32 #define HWY_GENERIC_LAMBDA 0 33 #else 34 #define HWY_GENERIC_LAMBDA 1 35 #endif 36 37 HWY_BEFORE_NAMESPACE(); 38 namespace hwy { 39 namespace HWY_NAMESPACE { 40 namespace { 41 42 // Returns random integer in [0, 128), which fits in any lane type. 43 template <typename T> 44 T Random7Bit(RandomState& rng) { 45 return ConvertScalarTo<T>(Random32(&rng) & 127); 46 } 47 48 // In C++14, we can instead define these as generic lambdas next to where they 49 // are invoked. 50 #if !HWY_GENERIC_LAMBDA 51 52 struct IsOdd { 53 template <class D, class V> 54 Mask<D> operator()(D d, V v) const { 55 return TestBit(v, Set(d, TFromD<D>{1})); 56 } 57 }; 58 59 #endif // !HWY_GENERIC_LAMBDA 60 61 // Invokes Test (e.g. TestCopyIf) with all arg combinations. T comes from 62 // ForFloatTypes. 63 template <class Test> 64 struct ForeachCountAndMisalign { 65 template <typename T, class D> 66 HWY_NOINLINE void operator()(T /*unused*/, D d) const { 67 RandomState rng; 68 const size_t N = Lanes(d); 69 const size_t misalignments[3] = {0, N / 4, 3 * N / 5}; 70 71 for (size_t count = 0; count < 2 * N; ++count) { 72 for (size_t ma : misalignments) { 73 for (size_t mb : misalignments) { 74 Test()(d, count, ma, mb, rng); 75 } 76 } 77 } 78 } 79 }; 80 81 struct TestFill { 82 template <class D> 83 void operator()(D d, size_t count, size_t misalign_a, size_t misalign_b, 84 RandomState& rng) { 85 using T = TFromD<D>; 86 // HWY_MAX prevents error when misalign == count == 0. 87 AlignedFreeUniquePtr<T[]> pa = 88 AllocateAligned<T>(HWY_MAX(1, misalign_a + count)); 89 AlignedFreeUniquePtr<T[]> pb = AllocateAligned<T>(misalign_b + count + 1); 90 HWY_ASSERT(pa && pb); 91 T* expected = pa.get() + misalign_a; 92 const T value = Random7Bit<T>(rng); 93 for (size_t i = 0; i < count; ++i) { 94 expected[i] = value; 95 } 96 T* actual = pb.get() + misalign_b; 97 98 actual[count] = ConvertScalarTo<T>(0); // sentinel 99 Fill(d, value, count, actual); 100 HWY_ASSERT_EQ(ConvertScalarTo<T>(0), actual[count]); // no write past end 101 102 const auto info = hwy::detail::MakeTypeInfo<T>(); 103 const char* target_name = hwy::TargetName(HWY_TARGET); 104 hwy::detail::AssertArrayEqual(info, expected, actual, count, target_name, 105 __FILE__, __LINE__); 106 } 107 }; 108 109 void TestAllFill() { 110 ForAllTypes(ForPartialVectors<ForeachCountAndMisalign<TestFill>>()); 111 } 112 113 struct TestCopy { 114 template <class D> 115 void operator()(D d, size_t count, size_t misalign_a, size_t misalign_b, 116 RandomState& rng) { 117 using T = TFromD<D>; 118 // Prevents error if size to allocate is zero. 119 AlignedFreeUniquePtr<T[]> pa = 120 AllocateAligned<T>(HWY_MAX(1, misalign_a + count)); 121 AlignedFreeUniquePtr<T[]> pb = 122 AllocateAligned<T>(HWY_MAX(1, misalign_b + count)); 123 HWY_ASSERT(pa && pb); 124 T* a = pa.get() + misalign_a; 125 for (size_t i = 0; i < count; ++i) { 126 a[i] = Random7Bit<T>(rng); 127 } 128 T* b = pb.get() + misalign_b; 129 130 Copy(d, a, count, b); 131 132 const auto info = hwy::detail::MakeTypeInfo<T>(); 133 const char* target_name = hwy::TargetName(HWY_TARGET); 134 hwy::detail::AssertArrayEqual(info, a, b, count, target_name, __FILE__, 135 __LINE__); 136 } 137 }; 138 139 void TestAllCopy() { 140 ForAllTypes(ForPartialVectors<ForeachCountAndMisalign<TestCopy>>()); 141 } 142 143 struct TestCopyIf { 144 template <class D> 145 void operator()(D d, size_t count, size_t misalign_a, size_t misalign_b, 146 RandomState& rng) { 147 using T = TFromD<D>; 148 const size_t padding = Lanes(ScalableTag<T>()); 149 150 // Prevents error if size to allocate is zero. 151 AlignedFreeUniquePtr<T[]> pa = 152 AllocateAligned<T>(HWY_MAX(1, misalign_a + count)); 153 AlignedFreeUniquePtr<T[]> pb = 154 AllocateAligned<T>(HWY_MAX(1, misalign_b + count + padding)); 155 AlignedFreeUniquePtr<T[]> expected = AllocateAligned<T>(HWY_MAX(1, count)); 156 HWY_ASSERT(pa && pb && expected); 157 158 T* a = pa.get() + misalign_a; 159 for (size_t i = 0; i < count; ++i) { 160 a[i] = Random7Bit<T>(rng); 161 } 162 T* b = pb.get() + misalign_b; 163 164 size_t num_odd = 0; 165 for (size_t i = 0; i < count; ++i) { 166 if (a[i] & 1) { 167 expected[num_odd++] = a[i]; 168 } 169 } 170 171 #if HWY_GENERIC_LAMBDA 172 const auto is_odd = [](const auto d2, const auto v) HWY_ATTR { 173 return TestBit(v, Set(d2, TFromD<decltype(d2)>{1})); 174 }; 175 #else 176 const IsOdd is_odd; 177 #endif 178 T* end = CopyIf(d, a, count, b, is_odd); 179 const size_t num_written = static_cast<size_t>(end - b); 180 HWY_ASSERT_EQ(num_odd, num_written); 181 182 const auto info = hwy::detail::MakeTypeInfo<T>(); 183 const char* target_name = hwy::TargetName(HWY_TARGET); 184 hwy::detail::AssertArrayEqual(info, expected.get(), b, num_odd, target_name, 185 __FILE__, __LINE__); 186 } 187 }; 188 189 void TestAllCopyIf() { 190 ForUI163264(ForPartialVectors<ForeachCountAndMisalign<TestCopyIf>>()); 191 } 192 193 } // namespace 194 // NOLINTNEXTLINE(google-readability-namespace-comments) 195 } // namespace HWY_NAMESPACE 196 } // namespace hwy 197 HWY_AFTER_NAMESPACE(); 198 199 #if HWY_ONCE 200 namespace hwy { 201 namespace { 202 HWY_BEFORE_TEST(CopyTest); 203 HWY_EXPORT_AND_TEST_P(CopyTest, TestAllFill); 204 HWY_EXPORT_AND_TEST_P(CopyTest, TestAllCopy); 205 HWY_EXPORT_AND_TEST_P(CopyTest, TestAllCopyIf); 206 HWY_AFTER_TEST(); 207 } // namespace 208 } // namespace hwy 209 HWY_TEST_MAIN(); 210 #endif // HWY_ONCE