xref: /aosp_15_r20/external/XNNPACK/test/copy-operator-tester.h (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
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, &copy_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, &copy_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, &copy_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