crc_memcpy_test.cc (7042B)
1 // Copyright 2022 The Abseil Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/crc/internal/crc_memcpy.h" 16 17 #include <cstddef> 18 #include <cstdint> 19 #include <cstring> 20 #include <limits> 21 #include <memory> 22 #include <string> 23 #include <utility> 24 25 #include "gtest/gtest.h" 26 #include "absl/crc/crc32c.h" 27 #include "absl/memory/memory.h" 28 #include "absl/random/distributions.h" 29 #include "absl/random/random.h" 30 #include "absl/strings/str_cat.h" 31 #include "absl/strings/string_view.h" 32 33 namespace { 34 35 enum CrcEngine { 36 ACCELERATED = 0, 37 NONTEMPORAL = 1, 38 FALLBACK = 2, 39 }; 40 41 // Correctness tests: 42 // - Every source/destination byte alignment 0-15, every size 0-511 bytes 43 // - Arbitrarily aligned source, large size 44 template <size_t max_size> 45 class CrcMemcpyTest : public testing::Test { 46 protected: 47 CrcMemcpyTest() { 48 source_ = std::make_unique<char[]>(kSize); 49 destination_ = std::make_unique<char[]>(kSize); 50 } 51 static constexpr size_t kAlignment = 16; 52 static constexpr size_t kMaxCopySize = max_size; 53 static constexpr size_t kSize = kAlignment + kMaxCopySize; 54 std::unique_ptr<char[]> source_; 55 std::unique_ptr<char[]> destination_; 56 57 absl::BitGen gen_; 58 }; 59 60 // Small test is slightly larger 4096 bytes to allow coverage of the "large" 61 // copy function. The minimum size to exercise all code paths in that function 62 // would be around 256 consecutive tests (getting every possible tail value 63 // and 0-2 small copy loops after the main block), so testing from 4096-4500 64 // will cover all of those code paths multiple times. 65 typedef CrcMemcpyTest<4500> CrcSmallTest; 66 typedef CrcMemcpyTest<(1 << 24)> CrcLargeTest; 67 // Parametrize the small test so that it can be done with all configurations. 68 template <typename ParamsT> 69 class EngineParamTestTemplate : public CrcSmallTest, 70 public ::testing::WithParamInterface<ParamsT> { 71 protected: 72 EngineParamTestTemplate() { 73 if (GetParam().crc_engine_selector == FALLBACK) { 74 engine_ = std::make_unique<absl::crc_internal::FallbackCrcMemcpyEngine>(); 75 } else if (GetParam().crc_engine_selector == NONTEMPORAL) { 76 engine_ = 77 std::make_unique<absl::crc_internal::CrcNonTemporalMemcpyEngine>(); 78 } else { 79 engine_ = absl::crc_internal::CrcMemcpy::GetTestEngine( 80 GetParam().vector_lanes, GetParam().integer_lanes); 81 } 82 } 83 84 // Convenience method. 85 ParamsT GetParam() const { 86 return ::testing::WithParamInterface<ParamsT>::GetParam(); 87 } 88 89 std::unique_ptr<absl::crc_internal::CrcMemcpyEngine> engine_; 90 }; 91 struct TestParams { 92 CrcEngine crc_engine_selector = ACCELERATED; 93 int vector_lanes = 0; 94 int integer_lanes = 0; 95 }; 96 using EngineParamTest = EngineParamTestTemplate<TestParams>; 97 // SmallCorrectness is designed to exercise every possible set of code paths 98 // in the memcpy code, not including the loop. 99 TEST_P(EngineParamTest, SmallCorrectnessCheckSourceAlignment) { 100 constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize}; 101 102 for (size_t source_alignment = 0; source_alignment < kAlignment; 103 source_alignment++) { 104 for (auto size : kTestSizes) { 105 char* base_data = static_cast<char*>(source_.get()) + source_alignment; 106 for (size_t i = 0; i < size; i++) { 107 *(base_data + i) = 108 static_cast<char>(absl::Uniform<unsigned char>(gen_)); 109 } 110 SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",", 111 GetParam().integer_lanes, ">, ", "size=", size, 112 ", source_alignment=", source_alignment)); 113 absl::crc32c_t initial_crc = 114 absl::crc32c_t{absl::Uniform<uint32_t>(gen_)}; 115 absl::crc32c_t experiment_crc = 116 engine_->Compute(destination_.get(), source_.get() + source_alignment, 117 size, initial_crc); 118 // Check the memory region to make sure it is the same 119 int mem_comparison = 120 memcmp(destination_.get(), source_.get() + source_alignment, size); 121 SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size, 122 " with source alignment: ", source_alignment)); 123 ASSERT_EQ(mem_comparison, 0); 124 absl::crc32c_t baseline_crc = absl::ExtendCrc32c( 125 initial_crc, 126 absl::string_view( 127 static_cast<char*>(source_.get()) + source_alignment, size)); 128 ASSERT_EQ(baseline_crc, experiment_crc); 129 } 130 } 131 } 132 133 TEST_P(EngineParamTest, SmallCorrectnessCheckDestAlignment) { 134 constexpr size_t kTestSizes[] = {0, 100, 255, 512, 1024, 4000, kMaxCopySize}; 135 136 for (size_t dest_alignment = 0; dest_alignment < kAlignment; 137 dest_alignment++) { 138 for (auto size : kTestSizes) { 139 char* base_data = static_cast<char*>(source_.get()); 140 for (size_t i = 0; i < size; i++) { 141 *(base_data + i) = 142 static_cast<char>(absl::Uniform<unsigned char>(gen_)); 143 } 144 SCOPED_TRACE(absl::StrCat("engine=<", GetParam().vector_lanes, ",", 145 GetParam().integer_lanes, ">, ", "size=", size, 146 ", destination_alignment=", dest_alignment)); 147 absl::crc32c_t initial_crc = 148 absl::crc32c_t{absl::Uniform<uint32_t>(gen_)}; 149 absl::crc32c_t experiment_crc = 150 engine_->Compute(destination_.get() + dest_alignment, source_.get(), 151 size, initial_crc); 152 // Check the memory region to make sure it is the same 153 int mem_comparison = 154 memcmp(destination_.get() + dest_alignment, source_.get(), size); 155 SCOPED_TRACE(absl::StrCat("Error in memcpy of size: ", size, 156 " with dest alignment: ", dest_alignment)); 157 ASSERT_EQ(mem_comparison, 0); 158 absl::crc32c_t baseline_crc = absl::ExtendCrc32c( 159 initial_crc, 160 absl::string_view(static_cast<char*>(source_.get()), size)); 161 ASSERT_EQ(baseline_crc, experiment_crc); 162 } 163 } 164 } 165 166 INSTANTIATE_TEST_SUITE_P(EngineParamTest, EngineParamTest, 167 ::testing::Values( 168 // Tests for configurations that may occur in prod. 169 TestParams{ACCELERATED, 3, 0}, 170 TestParams{ACCELERATED, 1, 2}, 171 TestParams{ACCELERATED, 1, 0}, 172 // Fallback test. 173 TestParams{FALLBACK, 0, 0}, 174 // Non Temporal 175 TestParams{NONTEMPORAL, 0, 0})); 176 177 } // namespace