1 // Copyright 2022 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 #include <algorithm>
7 #include <array>
8 #include <cstddef>
9 #include <cstdint>
10 #include <limits>
11 #include <memory>
12
13 #include <xnnpack.h>
14 #include <xnnpack/subgraph.h>
15
16 #include <gtest/gtest.h>
17
18 namespace {
DefineGraphWithoutInternalTensors(xnn_subgraph_t * subgraph,std::array<size_t,4> dims)19 void DefineGraphWithoutInternalTensors(xnn_subgraph_t* subgraph, std::array<size_t, 4> dims)
20 {
21 xnn_create_subgraph(/*external_value_ids=*/0, /*flags=*/0, subgraph);
22 uint32_t input_id = XNN_INVALID_VALUE_ID;
23 xnn_define_tensor_value(
24 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
25 XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id);
26 ASSERT_NE(input_id, XNN_INVALID_VALUE_ID);
27
28 uint32_t output_id = XNN_INVALID_VALUE_ID;
29 xnn_define_tensor_value(
30 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
31 XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id);
32 ASSERT_NE(output_id, XNN_INVALID_VALUE_ID);
33
34 ASSERT_EQ(xnn_status_success, xnn_define_abs(*subgraph, input_id, output_id, /*flags=*/0));
35 }
36
37 // Helper function to create a subgraph with 1 input, 1 output, and 1 intermediate tensor.
38 // input -> (abs) -> intermediate -> (hard swish) -> output
39 // The size of the tensors are all the same, specified by `dims`.
DefineGraph(xnn_subgraph_t * subgraph,std::array<size_t,4> dims)40 void DefineGraph(xnn_subgraph_t* subgraph, std::array<size_t, 4> dims)
41 {
42 xnn_create_subgraph(/*external_value_ids=*/0, /*flags=*/0, subgraph);
43 uint32_t input_id = XNN_INVALID_VALUE_ID;
44 xnn_define_tensor_value(
45 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
46 XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id);
47 ASSERT_NE(input_id, XNN_INVALID_VALUE_ID);
48
49 uint32_t intermediate_id = XNN_INVALID_VALUE_ID;
50 xnn_define_tensor_value(
51 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID, /*flags=*/0,
52 &intermediate_id);
53 ASSERT_NE(intermediate_id, XNN_INVALID_VALUE_ID);
54
55 uint32_t output_id = XNN_INVALID_VALUE_ID;
56 xnn_define_tensor_value(
57 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
58 XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id);
59 ASSERT_NE(output_id, XNN_INVALID_VALUE_ID);
60
61 ASSERT_EQ(xnn_status_success, xnn_define_abs(*subgraph, input_id, intermediate_id, /*flags=*/0));
62 ASSERT_EQ(xnn_status_success, xnn_define_hardswish(*subgraph, intermediate_id, output_id, /*flags=*/0));
63 }
64
DefineGraphWithStaticData(xnn_subgraph_t * subgraph,std::array<size_t,4> dims,const std::vector<float> * static_value)65 void DefineGraphWithStaticData(xnn_subgraph_t* subgraph, std::array<size_t, 4> dims, const std::vector<float>* static_value)
66 {
67 xnn_create_subgraph(/*external_value_ids=*/0, /*flags=*/0, subgraph);
68 uint32_t input_id = XNN_INVALID_VALUE_ID;
69 xnn_define_tensor_value(
70 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
71 XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id);
72 ASSERT_NE(input_id, XNN_INVALID_VALUE_ID);
73
74 uint32_t static_value_id = XNN_INVALID_VALUE_ID;
75 xnn_define_tensor_value(
76 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), static_value->data(), XNN_INVALID_VALUE_ID, /*flags=*/0,
77 &static_value_id);
78 ASSERT_NE(static_value_id, XNN_INVALID_VALUE_ID);
79
80 uint32_t output_id = XNN_INVALID_VALUE_ID;
81 xnn_define_tensor_value(
82 *subgraph, xnn_datatype_fp32, dims.size(), dims.data(), nullptr, XNN_INVALID_VALUE_ID,
83 XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id);
84 ASSERT_NE(output_id, XNN_INVALID_VALUE_ID);
85
86 ASSERT_EQ(xnn_status_success,
87 xnn_define_add2(*subgraph, -std::numeric_limits<float>::infinity(),
88 std::numeric_limits<float>::infinity(), input_id,
89 static_value_id, output_id, /*flags=*/0));
90 }
91
BlobInWorkspace(xnn_blob * blob,xnn_workspace_t workspace)92 testing::AssertionResult BlobInWorkspace(xnn_blob* blob, xnn_workspace_t workspace) {
93 if ((blob->data >= workspace->data) &&
94 ((uintptr_t) blob->data + blob->size) <= ((uintptr_t) workspace->data + workspace->size)) {
95 return testing::AssertionSuccess();
96 } else {
97 return testing::AssertionFailure()
98 << "blob at " << blob->data << " of size " << blob->size
99 << "is outside of workspace at " << workspace->data << " of size " << workspace->size;
100 }
101 }
102
Contains(std::vector<xnn_runtime_t> workspace_users,xnn_runtime_t runtime)103 testing::AssertionResult Contains(std::vector<xnn_runtime_t> workspace_users, xnn_runtime_t runtime) {
104 if (std::find(workspace_users.begin(), workspace_users.end(), runtime) != workspace_users.end()) {
105 return testing::AssertionSuccess();
106 } else {
107 return testing::AssertionFailure() << "runtime " << runtime << " not found in list of workspace users";
108 }
109 }
110
workspace_user_to_list(xnn_workspace_t workspace)111 std::vector<xnn_runtime_t> workspace_user_to_list(xnn_workspace_t workspace)
112 {
113 std::vector<xnn_runtime_t> users;
114 for (xnn_runtime_t rt = workspace->first_user; rt != NULL; rt = rt->next_workspace_user) {
115 users.push_back(rt);
116 }
117 return users;
118 }
119 } // namespace
120
TEST(WORKSPACE,static_data_not_moved_does_not_segv)121 TEST(WORKSPACE, static_data_not_moved_does_not_segv)
122 {
123 std::array<size_t, 4> dims = {2, 20, 20, 3};
124 size_t num_elements = dims[0] * dims[1] * dims[2] * dims[3];
125
126 xnn_initialize(/*allocator=*/nullptr);
127 xnn_workspace_t workspace = nullptr;
128 xnn_create_workspace(&workspace);
129 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
130
131 // Create a graph that with static data.
132 xnn_subgraph_t subgraph1 = nullptr;
133 std::vector<float> static_data = std::vector<float>(num_elements, 1.0f);
134 DefineGraphWithStaticData(&subgraph1, dims, &static_data);
135 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
136 xnn_runtime_t runtime1 = nullptr;
137 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
138 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
139
140 // The workspace remains at size 0, without any memory allocated, since we don't have any internal tensors.
141 size_t old_workspace_size = workspace->size;
142 ASSERT_EQ(old_workspace_size, 0);
143 void* old_runtime_workspace = runtime1->workspace->data;
144 ASSERT_EQ(old_runtime_workspace, nullptr);
145
146 // Then create a graph that has internal tensors, we will need to resize the workspace.
147 xnn_subgraph_t subgraph2 = nullptr;
148 DefineGraph(&subgraph2, dims);
149 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
150 xnn_runtime_t runtime2 = nullptr;
151 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
152 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
153
154 // Check that the workspace grew.
155 ASSERT_GE(workspace->size, num_elements * sizeof(float));
156 ASSERT_NE(runtime2->workspace->data, nullptr);
157
158 // Try to access all the blobs and ensure that we don't segfault.
159 for (size_t i = 0; i < runtime1->num_blobs; i++) {
160 xnn_blob* blob = &runtime1->blobs[i];
161 if (blob->allocation_type == xnn_allocation_type_external) {
162 continue;
163 }
164 ASSERT_GT(blob->size, 0);
165 char access = *((char *)blob->data);
166 (void) access;
167 }
168
169 for (size_t i = 0; i < runtime2->num_blobs; i++) {
170 xnn_blob* blob = &runtime2->blobs[i];
171 if (blob->allocation_type == xnn_allocation_type_external) {
172 continue;
173 }
174 ASSERT_GT(blob->size, 0);
175 char access = *((char *)blob->data);
176 (void) access;
177 }
178 }
179
TEST(WORKSPACE,workspace_no_growth)180 TEST(WORKSPACE, workspace_no_growth)
181 {
182 xnn_initialize(/*allocator=*/nullptr);
183 xnn_workspace_t workspace = nullptr;
184 xnn_create_workspace(&workspace);
185 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
186
187 std::array<size_t, 4> dims = {2, 20, 20, 3};
188
189 xnn_subgraph_t subgraph1 = nullptr;
190 DefineGraph(&subgraph1, dims);
191 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
192
193 xnn_runtime_t runtime1 = nullptr;
194 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
195 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
196
197 size_t old_workspace_size = workspace->size;
198 ASSERT_GE(old_workspace_size, 0);
199 void* old_runtime_workspace = runtime1->workspace->data;
200 ASSERT_NE(old_runtime_workspace, nullptr);
201
202 // Create the same graph again with a different runtime that shares the workspace.
203 xnn_subgraph_t subgraph2 = nullptr;
204 DefineGraph(&subgraph2, dims);
205 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
206
207 xnn_runtime_t runtime2 = nullptr;
208 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
209 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
210
211 // Check that the workspace did not grow.
212 ASSERT_EQ(workspace->size, old_workspace_size);
213 // Check that runtime 2 uses the same workspace.
214 ASSERT_EQ(runtime2->workspace->data, old_runtime_workspace);
215
216 ASSERT_EQ(runtime1->num_blobs, runtime2->num_blobs);
217 for (size_t i = 0; i < runtime1->num_blobs; i++) {
218 xnn_blob* blob1 = &runtime1->blobs[i];
219 if (blob1->allocation_type != xnn_allocation_type_workspace) {
220 continue;
221 }
222 ASSERT_TRUE(BlobInWorkspace(blob1, runtime1->workspace));
223 xnn_blob* blob2 = &runtime2->blobs[i];
224 ASSERT_TRUE(BlobInWorkspace(blob2, runtime2->workspace));
225 }
226
227 std::vector<xnn_runtime_t> workspace_users = workspace_user_to_list(workspace);
228 ASSERT_EQ(workspace_users.size(), 2);
229 ASSERT_TRUE(Contains(workspace_users, runtime1));
230 ASSERT_TRUE(Contains(workspace_users, runtime2));
231 ASSERT_EQ(workspace->ref_count, 3);
232 }
233
TEST(WORKSPACE,workspace_grow)234 TEST(WORKSPACE, workspace_grow)
235 {
236 xnn_initialize(/*allocator=*/nullptr);
237 xnn_workspace_t workspace = nullptr;
238 ASSERT_EQ(xnn_status_success, xnn_create_workspace(&workspace));
239 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
240
241 std::array<size_t, 4> dims1 = {2, 20, 20, 3};
242
243 xnn_subgraph_t subgraph1 = nullptr;
244 DefineGraph(&subgraph1, dims1);
245 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
246
247 xnn_runtime_t runtime1 = nullptr;
248 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
249 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
250
251 size_t old_workspace_size = workspace->size;
252 ASSERT_GE(old_workspace_size, 0);
253 void* old_runtime_workspace = runtime1->workspace->data;
254 ASSERT_NE(old_runtime_workspace, nullptr);
255
256 std::array<size_t, 4> dims2 = dims1;
257 // Create the same graph but with larger tensors, this will require a larger workspace.
258 std::transform(dims2.begin(), dims2.end(), dims2.begin(), [](size_t i) { return i * 2; });
259 xnn_subgraph_t subgraph2 = nullptr;
260 DefineGraph(&subgraph2, dims2);
261 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
262
263 xnn_runtime_t runtime2 = nullptr;
264 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
265 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
266
267 // Check that the workspace grew.
268 ASSERT_GE(workspace->size, old_workspace_size);
269 // Check that runtime 2 uses the same workspace.
270 ASSERT_NE(runtime2->workspace->data, old_runtime_workspace);
271 // Check that runtime1's workspace has been updated as well.
272 ASSERT_EQ(runtime1->workspace->data, runtime2->workspace->data);
273 ASSERT_EQ(runtime1->workspace->size, runtime2->workspace->size);
274
275 // Check that both runtime's blob pointers are within range.
276 for (size_t i = 0; i < runtime1->num_blobs; i++) {
277 xnn_blob* blob = &runtime1->blobs[i];
278 if (blob->allocation_type != xnn_allocation_type_workspace) {
279 continue;
280 }
281 ASSERT_TRUE(BlobInWorkspace(blob, runtime1->workspace));
282 }
283 for (size_t i = 0; i < runtime2->num_blobs; i++) {
284 xnn_blob* blob = &runtime2->blobs[i];
285 if (blob->allocation_type != xnn_allocation_type_workspace) {
286 continue;
287 }
288 ASSERT_TRUE(BlobInWorkspace(blob, runtime2->workspace));
289 }
290
291 std::vector<xnn_runtime_t> workspace_users = workspace_user_to_list(workspace);
292 ASSERT_EQ(workspace_users.size(), 2);
293 ASSERT_TRUE(Contains(workspace_users, runtime1));
294 ASSERT_TRUE(Contains(workspace_users, runtime2));
295 ASSERT_EQ(workspace->ref_count, 3);
296 }
297
TEST(WORKSPACE,workspace_runtime_delete_head_runtime_first)298 TEST(WORKSPACE, workspace_runtime_delete_head_runtime_first)
299 {
300 xnn_initialize(/*allocator=*/nullptr);
301 xnn_workspace_t workspace = nullptr;
302 ASSERT_EQ(xnn_status_success, xnn_create_workspace(&workspace));
303 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
304
305 const std::array<size_t, 4> dims = {2, 20, 20, 3};
306
307 xnn_subgraph_t subgraph1 = nullptr;
308 DefineGraph(&subgraph1, dims);
309 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
310
311 xnn_runtime_t runtime1 = nullptr;
312 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
313 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
314
315 xnn_subgraph_t subgraph2 = nullptr;
316 DefineGraph(&subgraph2, dims);
317 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
318
319 xnn_runtime_t runtime2 = nullptr;
320 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
321 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
322
323 ASSERT_EQ(workspace->first_user, runtime2);
324 ASSERT_EQ(runtime2->next_workspace_user, runtime1);
325 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
326
327 ASSERT_EQ(workspace->ref_count, 3);
328 xnn_delete_runtime(auto_runtime2.release());
329 ASSERT_EQ(workspace->first_user, runtime1);
330 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
331 ASSERT_EQ(workspace->ref_count, 2);
332
333 xnn_delete_runtime(auto_runtime1.release());
334 ASSERT_EQ(workspace->first_user, nullptr);
335 ASSERT_EQ(workspace->ref_count, 1);
336 }
337
TEST(WORKSPACE,workspace_runtime_delete_tail_runtime_first)338 TEST(WORKSPACE, workspace_runtime_delete_tail_runtime_first)
339 {
340 xnn_initialize(/*allocator=*/nullptr);
341 xnn_workspace_t workspace = nullptr;
342 ASSERT_EQ(xnn_status_success, xnn_create_workspace(&workspace));
343 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
344
345 std::array<size_t, 4> dims = {2, 20, 20, 3};
346
347 xnn_subgraph_t subgraph1 = nullptr;
348 DefineGraph(&subgraph1, dims);
349 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
350
351 xnn_runtime_t runtime1 = nullptr;
352 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
353 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
354
355 xnn_subgraph_t subgraph2 = nullptr;
356 DefineGraph(&subgraph2, dims);
357 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
358
359 xnn_runtime_t runtime2 = nullptr;
360 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
361 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
362
363 ASSERT_EQ(workspace->first_user, runtime2);
364 ASSERT_EQ(runtime2->next_workspace_user, runtime1);
365 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
366
367 ASSERT_EQ(workspace->ref_count, 3);
368 xnn_delete_runtime(auto_runtime1.release());
369
370 ASSERT_EQ(workspace->first_user, runtime2);
371 ASSERT_EQ(runtime2->next_workspace_user, nullptr);
372 ASSERT_EQ(workspace->ref_count, 2);
373
374 xnn_delete_runtime(auto_runtime2.release());
375 ASSERT_EQ(workspace->first_user, nullptr);
376 ASSERT_EQ(workspace->ref_count, 1);
377 }
378
TEST(WORKSPACE,workspace_runtime_delete_middle_runtime_first)379 TEST(WORKSPACE, workspace_runtime_delete_middle_runtime_first)
380 {
381 xnn_initialize(/*allocator=*/nullptr);
382 xnn_workspace_t workspace = nullptr;
383 ASSERT_EQ(xnn_status_success, xnn_create_workspace(&workspace));
384 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
385
386 std::array<size_t, 4> dims = {2, 20, 20, 3};
387
388 xnn_subgraph_t subgraph1 = nullptr;
389 DefineGraph(&subgraph1, dims);
390 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph1(subgraph1, xnn_delete_subgraph);
391
392 xnn_runtime_t runtime1 = nullptr;
393 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph1, nullptr, workspace, nullptr, 0, &runtime1));
394 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime1(runtime1, xnn_delete_runtime);
395
396 xnn_subgraph_t subgraph2 = nullptr;
397 DefineGraph(&subgraph2, dims);
398 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph2(subgraph2, xnn_delete_subgraph);
399
400 xnn_runtime_t runtime2 = nullptr;
401 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph2, nullptr, workspace, nullptr, 0, &runtime2));
402 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime2(runtime2, xnn_delete_runtime);
403
404 xnn_subgraph_t subgraph3 = nullptr;
405 DefineGraph(&subgraph3, dims);
406 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph3(subgraph3, xnn_delete_subgraph);
407
408 xnn_runtime_t runtime3 = nullptr;
409 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph3, nullptr, workspace, nullptr, 0, &runtime3));
410 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime3(runtime3, xnn_delete_runtime);
411
412 ASSERT_EQ(workspace->first_user, runtime3);
413 ASSERT_EQ(runtime3->next_workspace_user, runtime2);
414 ASSERT_EQ(runtime2->next_workspace_user, runtime1);
415 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
416
417 ASSERT_EQ(workspace->ref_count, 4);
418 xnn_delete_runtime(auto_runtime2.release());
419
420 ASSERT_EQ(workspace->first_user, runtime3);
421 ASSERT_EQ(runtime3->next_workspace_user, runtime1);
422 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
423 ASSERT_EQ(workspace->ref_count, 3);
424
425 xnn_delete_runtime(auto_runtime3.release());
426 ASSERT_EQ(workspace->first_user, runtime1);
427 ASSERT_EQ(runtime1->next_workspace_user, nullptr);
428 ASSERT_EQ(workspace->ref_count, 2);
429
430 xnn_delete_runtime(auto_runtime1.release());
431 ASSERT_EQ(workspace->first_user, nullptr);
432 ASSERT_EQ(workspace->ref_count, 1);
433 }
434
TEST(WORKSPACE,zero_sized_workspace_for_graph_without_internal_tensors)435 TEST(WORKSPACE, zero_sized_workspace_for_graph_without_internal_tensors)
436 {
437 xnn_initialize(/*allocator=*/nullptr);
438 xnn_workspace_t workspace = nullptr;
439 xnn_create_workspace(&workspace);
440 std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> auto_workspace(workspace, xnn_release_workspace);
441
442 std::array<size_t, 4> dims = {2, 20, 20, 3};
443
444 xnn_subgraph_t subgraph = nullptr;
445 DefineGraphWithoutInternalTensors(&subgraph, dims);
446 std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
447
448 xnn_runtime_t runtime = nullptr;
449 ASSERT_EQ(xnn_status_success, xnn_create_runtime_v4(subgraph, nullptr, workspace, nullptr, 0, &runtime));
450 std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
451
452 ASSERT_EQ(0, workspace->size);
453 ASSERT_EQ(nullptr, workspace->data);
454 ASSERT_EQ(std::vector<xnn_runtime_t>({runtime}), workspace_user_to_list(workspace));
455 ASSERT_EQ(workspace->ref_count, 2);
456 }
457