1 // Copyright 2020 Google LLC 2 // 3 // This source code is licensed under the BSD-style license found in the 4 // LICENSE file in the root directory of this source tree. 5 6 #pragma once 7 8 #include <gtest/gtest.h> 9 10 #include <algorithm> 11 #include <cassert> 12 #include <cstddef> 13 #include <cstdlib> 14 #include <limits> 15 #include <random> 16 #include <vector> 17 18 #include <xnnpack.h> 19 20 21 class CopyOperatorTester { 22 public: channels(size_t channels)23 inline CopyOperatorTester& channels(size_t channels) { 24 assert(channels != 0); 25 this->channels_ = channels; 26 return *this; 27 } 28 channels()29 inline size_t channels() const { 30 return this->channels_; 31 } 32 input_stride(size_t input_stride)33 inline CopyOperatorTester& input_stride(size_t input_stride) { 34 assert(input_stride != 0); 35 this->input_stride_ = input_stride; 36 return *this; 37 } 38 input_stride()39 inline size_t input_stride() const { 40 if (this->input_stride_ == 0) { 41 return this->channels_; 42 } else { 43 assert(this->input_stride_ >= this->channels_); 44 return this->input_stride_; 45 } 46 } 47 output_stride(size_t output_stride)48 inline CopyOperatorTester& output_stride(size_t output_stride) { 49 assert(output_stride != 0); 50 this->output_stride_ = output_stride; 51 return *this; 52 } 53 output_stride()54 inline size_t output_stride() const { 55 if (this->output_stride_ == 0) { 56 return this->channels_; 57 } else { 58 assert(this->output_stride_ >= this->channels_); 59 return this->output_stride_; 60 } 61 } 62 batch_size(size_t batch_size)63 inline CopyOperatorTester& batch_size(size_t batch_size) { 64 assert(batch_size != 0); 65 this->batch_size_ = batch_size; 66 return *this; 67 } 68 batch_size()69 inline size_t batch_size() const { 70 return this->batch_size_; 71 } 72 iterations(size_t iterations)73 inline CopyOperatorTester& iterations(size_t iterations) { 74 this->iterations_ = iterations; 75 return *this; 76 } 77 iterations()78 inline size_t iterations() const { 79 return this->iterations_; 80 } 81 TestX8()82 void TestX8() const { 83 std::random_device random_device; 84 auto rng = std::mt19937(random_device()); 85 std::uniform_int_distribution<uint32_t> u8dist( 86 std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max()); 87 88 std::vector<uint8_t> input(XNN_EXTRA_BYTES / sizeof(uint8_t) + 89 (batch_size() - 1) * input_stride() + channels()); 90 std::vector<uint8_t> output((batch_size() - 1) * output_stride() + channels()); 91 std::vector<uint8_t> output_ref(batch_size() * channels()); 92 for (size_t iteration = 0; iteration < iterations(); iteration++) { 93 std::generate(input.begin(), input.end(), [&]() { return u8dist(rng); }); 94 std::fill(output.begin(), output.end(), UINT8_C(0xFA)); 95 96 // Compute reference results. 97 for (size_t i = 0; i < batch_size(); i++) { 98 for (size_t c = 0; c < channels(); c++) { 99 output_ref[i * channels() + c] = input[i * input_stride() + c]; 100 } 101 } 102 103 // Create, setup, run, and destroy Copy operator. 104 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 105 xnn_operator_t copy_op = nullptr; 106 107 ASSERT_EQ(xnn_status_success, 108 xnn_create_copy_nc_x8( 109 channels(), input_stride(), output_stride(), 110 0, ©_op)); 111 ASSERT_NE(nullptr, copy_op); 112 113 // Smart pointer to automatically delete copy_op. 114 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_copy_op(copy_op, xnn_delete_operator); 115 116 ASSERT_EQ(xnn_status_success, 117 xnn_setup_copy_nc_x8( 118 copy_op, 119 batch_size(), 120 input.data(), output.data(), 121 nullptr /* thread pool */)); 122 123 ASSERT_EQ(xnn_status_success, 124 xnn_run_operator(copy_op, nullptr /* thread pool */)); 125 126 // Verify results. 127 for (size_t i = 0; i < batch_size(); i++) { 128 for (size_t c = 0; c < channels(); c++) { 129 ASSERT_EQ(output_ref[i * channels() + c], output[i * output_stride() + c]) 130 << "at batch " << i << " / " << batch_size() << ", channel = " << c << " / " << channels(); 131 } 132 } 133 } 134 } 135 TestX16()136 void TestX16() const { 137 std::random_device random_device; 138 auto rng = std::mt19937(random_device()); 139 std::uniform_int_distribution<uint16_t> u16dist; 140 141 std::vector<uint16_t> input(XNN_EXTRA_BYTES / sizeof(uint16_t) + 142 (batch_size() - 1) * input_stride() + channels()); 143 std::vector<uint16_t> output((batch_size() - 1) * output_stride() + channels()); 144 std::vector<uint16_t> output_ref(batch_size() * channels()); 145 for (size_t iteration = 0; iteration < iterations(); iteration++) { 146 std::generate(input.begin(), input.end(), [&]() { return u16dist(rng); }); 147 std::fill(output.begin(), output.end(), UINT16_C(0xDEAD)); 148 149 // Compute reference results. 150 for (size_t i = 0; i < batch_size(); i++) { 151 for (size_t c = 0; c < channels(); c++) { 152 output_ref[i * channels() + c] = input[i * input_stride() + c]; 153 } 154 } 155 156 // Create, setup, run, and destroy Copy operator. 157 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 158 xnn_operator_t copy_op = nullptr; 159 160 ASSERT_EQ(xnn_status_success, 161 xnn_create_copy_nc_x16( 162 channels(), input_stride(), output_stride(), 163 0, ©_op)); 164 ASSERT_NE(nullptr, copy_op); 165 166 // Smart pointer to automatically delete copy_op. 167 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_copy_op(copy_op, xnn_delete_operator); 168 169 ASSERT_EQ(xnn_status_success, 170 xnn_setup_copy_nc_x16( 171 copy_op, 172 batch_size(), 173 input.data(), output.data(), 174 nullptr /* thread pool */)); 175 176 ASSERT_EQ(xnn_status_success, 177 xnn_run_operator(copy_op, nullptr /* thread pool */)); 178 179 // Verify results. 180 for (size_t i = 0; i < batch_size(); i++) { 181 for (size_t c = 0; c < channels(); c++) { 182 ASSERT_EQ(output_ref[i * channels() + c], output[i * output_stride() + c]) 183 << "at batch " << i << " / " << batch_size() << ", channel = " << c << " / " << channels(); 184 } 185 } 186 } 187 } 188 TestX32()189 void TestX32() const { 190 std::random_device random_device; 191 auto rng = std::mt19937(random_device()); 192 std::uniform_int_distribution<uint32_t> u32dist; 193 194 std::vector<uint32_t> input(XNN_EXTRA_BYTES / sizeof(uint32_t) + 195 (batch_size() - 1) * input_stride() + channels()); 196 std::vector<uint32_t> output((batch_size() - 1) * output_stride() + channels()); 197 std::vector<uint32_t> output_ref(batch_size() * channels()); 198 for (size_t iteration = 0; iteration < iterations(); iteration++) { 199 std::generate(input.begin(), input.end(), [&]() { return u32dist(rng); }); 200 std::fill(output.begin(), output.end(), UINT32_C(0xDEADBEEF)); 201 202 // Compute reference results. 203 for (size_t i = 0; i < batch_size(); i++) { 204 for (size_t c = 0; c < channels(); c++) { 205 output_ref[i * channels() + c] = input[i * input_stride() + c]; 206 } 207 } 208 209 // Create, setup, run, and destroy Copy operator. 210 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 211 xnn_operator_t copy_op = nullptr; 212 213 ASSERT_EQ(xnn_status_success, 214 xnn_create_copy_nc_x32( 215 channels(), input_stride(), output_stride(), 216 0, ©_op)); 217 ASSERT_NE(nullptr, copy_op); 218 219 // Smart pointer to automatically delete copy_op. 220 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_copy_op(copy_op, xnn_delete_operator); 221 222 ASSERT_EQ(xnn_status_success, 223 xnn_setup_copy_nc_x32( 224 copy_op, 225 batch_size(), 226 input.data(), output.data(), 227 nullptr /* thread pool */)); 228 229 ASSERT_EQ(xnn_status_success, 230 xnn_run_operator(copy_op, nullptr /* thread pool */)); 231 232 // Verify results. 233 for (size_t i = 0; i < batch_size(); i++) { 234 for (size_t c = 0; c < channels(); c++) { 235 ASSERT_EQ(output_ref[i * channels() + c], output[i * output_stride() + c]) 236 << "at batch " << i << " / " << batch_size() << ", channel = " << c << " / " << channels(); 237 } 238 } 239 } 240 } 241 242 private: 243 size_t batch_size_{1}; 244 size_t channels_{1}; 245 size_t input_stride_{0}; 246 size_t output_stride_{0}; 247 size_t iterations_{15}; 248 }; 249