Skip to content

Commit a4092c5

Browse files
authored
introduce dim order tests to op test
Differential Revision: D55227304 Pull Request resolved: #2637
1 parent 5af5ed0 commit a4092c5

File tree

10 files changed

+443
-64
lines changed

10 files changed

+443
-64
lines changed

kernels/portable/cpu/op_abs.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Tensor& abs_out(RuntimeContext& ctx, const Tensor& in, Tensor& out) {
2828
"Failed to resize output tensor.");
2929

3030
ET_KERNEL_CHECK(ctx, tensors_have_same_dtype(in, out), InvalidArgument, out);
31+
ET_KERNEL_CHECK(
32+
ctx, tensors_have_same_dim_order(in, out), InvalidArgument, out);
3133

3234
ET_SWITCH_REAL_TYPES(in.scalar_type(), ctx, "abs.out", CTYPE, [&] {
3335
apply_unary_map_fn(

kernels/test/TestUtil.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@
3030
#define ET_EXPECT_KERNEL_FAILURE_WITH_MSG(_context, _statement, _matcher) \
3131
EXPECT_ANY_THROW(_statement)
3232

33+
#define ET_TEST_OP_SUPPORTS_MEMORY_FORMATS( \
34+
tf, op, input_contiguous, expected_contiguous, channels_last_support) \
35+
Tensor input_channels_last = tf.channels_last_like(input_contiguous); \
36+
Tensor expected_channel_last = tf.channels_last_like(expected_contiguous); \
37+
\
38+
Tensor output_contiguous = tf.zeros_like(expected_contiguous); \
39+
Tensor output_channels_last = tf.channels_last_like(output_contiguous); \
40+
\
41+
Tensor ret = op(input_channels_last, output_channels_last); \
42+
if (channels_last_support) { \
43+
EXPECT_TENSOR_EQ(output_channels_last, expected_channel_last); \
44+
} else { \
45+
EXPECT_TENSOR_NE(output_channels_last, expected_channel_last); \
46+
} \
47+
EXPECT_TENSOR_EQ(output_channels_last, ret);
48+
3349
#else
3450

3551
#define ET_EXPECT_KERNEL_FAILURE(_context, _statement) \
@@ -52,6 +68,26 @@
5268
} \
5369
} while (false)
5470

71+
#define ET_TEST_OP_SUPPORTS_MEMORY_FORMATS( \
72+
tf, op, input_contiguous, expected_contiguous, channels_last_support) \
73+
Tensor input_channels_last = tf.channels_last_like(input_contiguous); \
74+
Tensor expected_channel_last = tf.channels_last_like(expected_contiguous); \
75+
\
76+
Tensor output_contiguous = tf.zeros_like(expected_contiguous); \
77+
Tensor output_channels_last = tf.channels_last_like(output_contiguous); \
78+
\
79+
Tensor ret = op(input_channels_last, output_channels_last); \
80+
if (channels_last_support) { \
81+
EXPECT_TENSOR_EQ(output_channels_last, expected_channel_last); \
82+
} else { \
83+
EXPECT_TENSOR_NE(output_channels_last, expected_channel_last); \
84+
} \
85+
EXPECT_TENSOR_EQ(output_channels_last, ret); \
86+
ET_EXPECT_KERNEL_FAILURE( \
87+
context_, op(input_channels_last, output_contiguous)); \
88+
ET_EXPECT_KERNEL_FAILURE( \
89+
context_, op(input_contiguous, output_channels_last));
90+
5591
#endif // USE_ATEN_LIB
5692

5793
/*

kernels/test/op_abs_test.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,28 @@ TEST_F(OpAbsTest, SanityCheck) {
3838
EXPECT_TENSOR_EQ(out, ret);
3939
EXPECT_TENSOR_EQ(out, expected);
4040
}
41+
42+
TEST_F(OpAbsTest, MemoryFormatCheck) {
43+
TensorFactory<ScalarType::Float> tf;
44+
45+
std::vector<int32_t> sizes = {2, 3, 1, 5};
46+
47+
Tensor input_contiguous =
48+
tf.make(sizes, {0.8737, 0.5359, 0.3743, -0.3040, -0.7800, -0.2306,
49+
-0.7684, -0.5364, 0.3478, -0.3289, 0.0829, 0.2939,
50+
-0.8211, 0.8572, -0.0802, 0.9252, -0.2093, 0.9013,
51+
-0.4197, 0.3987, -0.5291, -0.5567, 0.2691, 0.7819,
52+
-0.8009, -0.4286, -0.9299, 0.2143, 0.2565, -0.5701});
53+
Tensor expected_contiguous = tf.make(
54+
sizes, {0.8737, 0.5359, 0.3743, 0.3040, 0.7800, 0.2306, 0.7684, 0.5364,
55+
0.3478, 0.3289, 0.0829, 0.2939, 0.8211, 0.8572, 0.0802, 0.9252,
56+
0.2093, 0.9013, 0.4197, 0.3987, 0.5291, 0.5567, 0.2691, 0.7819,
57+
0.8009, 0.4286, 0.9299, 0.2143, 0.2565, 0.5701});
58+
59+
ET_TEST_OP_SUPPORTS_MEMORY_FORMATS(
60+
tf,
61+
op_abs_out,
62+
input_contiguous,
63+
expected_contiguous,
64+
/*channels_last_support=*/true);
65+
}

runtime/core/exec_aten/testing_util/tensor_factory.h

Lines changed: 145 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#pragma once
44

55
#include <algorithm>
6+
#include <cstdint>
67

78
#include <executorch/runtime/core/exec_aten/exec_aten.h>
9+
#include <executorch/runtime/core/exec_aten/util/dim_order_util.h>
810
#include <executorch/runtime/core/exec_aten/util/scalar_type_util.h>
911
#include <executorch/runtime/core/tensor_shape_dynamism.h>
1012
#include <executorch/runtime/platform/assert.h>
@@ -54,7 +56,7 @@ inline size_t sizes_to_numel(const std::vector<int32_t>& sizes) {
5456

5557
inline bool check_strides(
5658
const std::vector<int32_t> sizes,
57-
const std::vector<int32_t> strides) {
59+
const std::vector<exec_aten::StridesType> strides) {
5860
if (sizes.size() != strides.size()) {
5961
// The length of stride vector shall equal to size vector.
6062
return false;
@@ -147,14 +149,14 @@ inline bool check_dim_order(
147149
return true;
148150
}
149151

150-
inline std::vector<int32_t> strides_from_dim_order(
152+
inline std::vector<exec_aten::StridesType> strides_from_dim_order(
151153
const std::vector<int32_t>& sizes,
152154
const std::vector<uint8_t>& dim_order) {
153155
bool legal = check_dim_order(sizes, dim_order);
154156
ET_CHECK_MSG(legal, "The input dim_order variable is illegal.");
155157

156158
size_t ndim = sizes.size();
157-
std::vector<int32_t> strides(ndim);
159+
std::vector<exec_aten::StridesType> strides(ndim);
158160
strides[dim_order[ndim - 1]] = 1;
159161
for (int i = ndim - 2; i >= 0; --i) {
160162
uint8_t cur_dim = dim_order[i];
@@ -258,7 +260,7 @@ class TensorFactory {
258260
at::Tensor make(
259261
const std::vector<int32_t>& sizes,
260262
const std::vector<ctype>& data,
261-
const std::vector<int32_t> strides = {},
263+
const std::vector<exec_aten::StridesType> strides = {},
262264
ET_UNUSED TensorShapeDynamism dynamism =
263265
TensorShapeDynamism::DYNAMIC_UNBOUND) {
264266
auto expected_numel = internal::sizes_to_numel(sizes);
@@ -344,6 +346,72 @@ class TensorFactory {
344346
sizes, data, internal::channels_last_dim_order(sizes.size()), dynamism);
345347
}
346348

349+
/**
350+
* Given data in contiguous memory format, returns a new Tensor with the
351+
* specified shape and the same data but in channels last memory format.
352+
*
353+
* @param[in] sizes The sizes of the dimensions of the Tensor.
354+
* @param[in] data The data in contiguous memory format that the Tensor should
355+
* be initialized with. The size of this vector must be equal to the product
356+
* of the elements of `sizes`.
357+
*
358+
* @return A new Tensor with the specified shape and data in channls last
359+
* memory format.
360+
*/
361+
at::Tensor channels_last_like(
362+
const at::Tensor& input,
363+
TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC) {
364+
ET_CHECK_MSG(
365+
input.sizes().size() == 4, "Only 4D tensors can be channels last");
366+
367+
const std::vector<int32_t> sizes(
368+
input.sizes().begin(), input.sizes().end());
369+
370+
std::vector<uint8_t> contiguous_dim_order(sizes.size());
371+
for (uint8_t i = 0; i < sizes.size(); i++) {
372+
contiguous_dim_order[i] = i;
373+
}
374+
std::vector<exec_aten::StridesType> contiguous_strides =
375+
internal::strides_from_dim_order(sizes, contiguous_dim_order);
376+
377+
for (int32_t i = 0; i < input.dim(); i++) {
378+
ET_CHECK_MSG(
379+
input.strides()[i] == contiguous_strides[i],
380+
"Input tensor is not contiguous");
381+
}
382+
383+
int32_t N = sizes[0];
384+
int32_t C = sizes[1];
385+
int32_t H = sizes[2];
386+
int32_t W = sizes[3];
387+
388+
std::vector<ctype> contiguous_data(
389+
input.data_ptr<ctype>(), input.data_ptr<ctype>() + input.numel());
390+
std::vector<ctype> channels_last_data(
391+
N * C * H * W); // Create a new blob with the same total size to contain
392+
// channels_last data
393+
for (int32_t n = 0; n < N; ++n) {
394+
for (int32_t c = 0; c < C; ++c) {
395+
for (int32_t h = 0; h < H; ++h) {
396+
for (int32_t w = 0; w < W; ++w) {
397+
// Calculate the index in the original blob
398+
int32_t old_index = ((n * C + c) * H + h) * W + w;
399+
// Calculate the index in the new blob
400+
int32_t new_index = ((n * H + h) * W + w) * C + c;
401+
// Copy the data
402+
channels_last_data[new_index] = contiguous_data[old_index];
403+
}
404+
}
405+
}
406+
}
407+
408+
return make_with_dimorder(
409+
sizes,
410+
channels_last_data,
411+
internal::channels_last_dim_order(sizes.size()),
412+
dynamism);
413+
}
414+
347415
/**
348416
* Returns a new Tensor with the specified shape, containing contiguous
349417
* data will all elements set to `value`.
@@ -459,14 +527,13 @@ class TensorFactory {
459527
*/
460528
at::Tensor empty_strided(
461529
const std::vector<int32_t>& sizes,
462-
const std::vector<int32_t>& strides,
530+
const std::vector<exec_aten::StridesType>& strides,
463531
ET_UNUSED TensorShapeDynamism dynamism =
464532
TensorShapeDynamism::DYNAMIC_UNBOUND) {
465533
auto sizes64 = vec_32_to_64(sizes);
466-
auto strides64 = vec_32_to_64(strides);
467534
return at::empty_strided(
468535
sizes64,
469-
strides64,
536+
strides,
470537
DTYPE,
471538
/*layout_opt=*/at::Layout::Strided,
472539
/*device_opt=*/at::Device(at::DeviceType::CPU),
@@ -666,7 +733,7 @@ class TensorFactory {
666733
torch::executor::Tensor make(
667734
const std::vector<int32_t>& sizes,
668735
const std::vector<ctype>& data,
669-
const std::vector<int32_t> strides = {},
736+
const std::vector<exec_aten::StridesType> strides = {},
670737
TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC) {
671738
std::vector<int32_t> default_strides;
672739
// Generate strides from the tensor dimensions, assuming contiguous data if
@@ -746,7 +813,7 @@ class TensorFactory {
746813

747814
/**
748815
* Returns a new Tensor with the specified shape and data in channels last
749-
* memory layout.
816+
* memory format.
750817
*
751818
* @param[in] sizes The sizes of the dimensions of the Tensor.
752819
* @param[in] data The data that the Tensor should be initialized with. The
@@ -764,6 +831,60 @@ class TensorFactory {
764831
sizes, data, internal::channels_last_dim_order(sizes.size()), dynamism);
765832
}
766833

834+
/**
835+
* Given data in contiguous memory format, returns a new Tensor with the
836+
* specified shape and the same data but in channels last memory format.
837+
*
838+
* @param[in] sizes The sizes of the dimensions of the Tensor.
839+
* @param[in] data The data in contiguous memory format that the Tensor should
840+
* be initialized with. The size of this vector must be equal to the product
841+
* of the elements of `sizes`.
842+
*
843+
* @return A new Tensor with the specified shape and data in channls last
844+
* memory format.
845+
*/
846+
torch::executor::Tensor channels_last_like(
847+
const torch::executor::Tensor& input,
848+
TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC) {
849+
const std::vector<int32_t> sizes(
850+
input.sizes().begin(), input.sizes().end());
851+
852+
ET_CHECK_MSG(sizes.size() == 4, "Only 4D tensors can be channels last");
853+
ET_CHECK_MSG(
854+
is_contiguous_dim_order(input.dim_order().data(), input.dim()) == true,
855+
"Input tensor is not contiguous");
856+
int32_t N = sizes[0];
857+
int32_t C = sizes[1];
858+
int32_t H = sizes[2];
859+
int32_t W = sizes[3];
860+
861+
std::vector<ctype> contiguous_data(
862+
input.data_ptr<ctype>(), input.data_ptr<ctype>() + input.numel());
863+
std::vector<ctype> channels_last_data(
864+
N * C * H * W); // Create a new blob with the same total size to contain
865+
// channels_last data
866+
for (int32_t n = 0; n < N; ++n) {
867+
for (int32_t c = 0; c < C; ++c) {
868+
for (int32_t h = 0; h < H; ++h) {
869+
for (int32_t w = 0; w < W; ++w) {
870+
// Calculate the index in the original blob
871+
int32_t old_index = ((n * C + c) * H + h) * W + w;
872+
// Calculate the index in the new blob
873+
int32_t new_index = ((n * H + h) * W + w) * C + c;
874+
// Copy the data
875+
channels_last_data[new_index] = contiguous_data[old_index];
876+
}
877+
}
878+
}
879+
}
880+
881+
return make_with_dimorder(
882+
sizes,
883+
channels_last_data,
884+
internal::channels_last_dim_order(sizes.size()),
885+
dynamism);
886+
}
887+
767888
/**
768889
* Returns a new Tensor with the specified shape, containing contiguous data
769890
* will all elements set to `value`.
@@ -799,7 +920,20 @@ class TensorFactory {
799920

800921
/**
801922
* Returns a new Tensor with the specified shape, containing contiguous data
802-
* with all `0` elements.
923+
* in channels last memory format with all `0` elements.
924+
*
925+
* @param[in] sizes The sizes of the dimensions of the Tensor.
926+
* @return A new Tensor with the specified shape.
927+
*/
928+
torch::executor::Tensor zeros_channels_last(
929+
const std::vector<int32_t>& sizes,
930+
TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC) {
931+
return full_channels_last(sizes, 0, dynamism);
932+
}
933+
934+
/**
935+
* Returns a new Tensor with the specified shape, containing contiguous data
936+
* in contiguous memory format with all `0` elements.
803937
*
804938
* @param[in] sizes The sizes of the dimensions of the Tensor.
805939
* @return A new Tensor with the specified shape.
@@ -878,7 +1012,7 @@ class TensorFactory {
8781012
std::vector<int32_t> sizes_;
8791013
std::vector<ctype> data_;
8801014
std::vector<uint8_t> dim_order_;
881-
std::vector<int32_t> strides_;
1015+
std::vector<exec_aten::StridesType> strides_;
8821016
torch::executor::TensorImpl impl_;
8831017
};
8841018

runtime/core/exec_aten/testing_util/test/tensor_factory_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ TEST_F(TensorFactoryTest, MakeStridedDataIsCopied) {
449449

450450
// Create two tensors using the same input data and strided vector.
451451
std::vector<int32_t> data = {1, 2, 3, 4};
452-
std::vector<int32_t> strides = {1, 2};
452+
std::vector<exec_aten::StridesType> strides = {1, 2};
453453
Tensor t1 = tf.make(/*sizes=*/{2, 2}, data, strides);
454454
Tensor t2 = tf.make(/*sizes=*/{2, 2}, data, strides);
455455

0 commit comments

Comments
 (0)