Skip to content

Commit 5755945

Browse files
authored
[CORE] Fix tensor is_continuous method (#28973)
### Details: - *If data is a contiguous memory area, return true* - *Strides can be different and data can still be contiguous in memory. Extra checks are needed in this case* ### Tickets: - *E#156237* --------- Signed-off-by: Bogdan Pereanu <bogdan.pereanu@intel.com>
1 parent aed285c commit 5755945

File tree

3 files changed

+245
-1
lines changed

3 files changed

+245
-1
lines changed

src/core/src/runtime/itensor.cpp

+16-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <memory>
88

9+
#include "compare.hpp"
910
#include "openvino/core/except.hpp"
1011
#include "openvino/core/shape_util.hpp"
1112
#include "openvino/core/type/element_iterator.hpp"
@@ -46,7 +47,21 @@ bool ITensor::is_continuous() const {
4647
// OpenVINO doesn't support strides for lp types
4748
return true;
4849
}
49-
return default_byte_strides(get_shape(), get_element_type()) == get_strides();
50+
51+
const auto& strides = get_strides();
52+
auto stride = strides.rbegin();
53+
const auto default_strides = default_byte_strides(get_shape(), get_element_type());
54+
auto default_stride = default_strides.rbegin();
55+
56+
for (; stride != strides.rend(); ++stride, ++default_stride) {
57+
if (*stride != *default_stride) {
58+
break;
59+
}
60+
}
61+
62+
const auto default_last = default_strides.rend();
63+
return (default_stride == default_last) || (*default_stride < *stride && (get_shape()[0] == 1) &&
64+
std::all_of(default_stride, default_last, cmp::Equal(*default_stride)));
5065
}
5166

5267
void ITensor::copy_to(const std::shared_ptr<ov::ITensor>& dst) const {

src/core/tests/ov_tensor_test.cpp

+101
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,107 @@ TEST_F(OVTensorTest, readRangeRoiBlobStringTensor) {
709709
}
710710
}
711711

712+
TEST_F(OVTensorTest, checkIsContinuousTensorScalar) {
713+
ov::Tensor tensor(ov::element::f32, ov::Shape{});
714+
auto data = tensor.data();
715+
auto strides = tensor.get_strides();
716+
717+
ov::Tensor view_tensor(ov::element::f32, ov::Shape{}, data, strides);
718+
EXPECT_EQ(view_tensor.is_continuous(), true);
719+
}
720+
721+
TEST_F(OVTensorTest, checkIsContinuousTensor1Dimension) {
722+
ov::Tensor tensor(ov::element::f32, ov::Shape{128});
723+
auto data = tensor.data();
724+
auto strides = tensor.get_strides();
725+
726+
ov::Tensor view_tensor;
727+
728+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{128}, data, strides);
729+
EXPECT_EQ(view_tensor.is_continuous(), true);
730+
731+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16}, data, strides);
732+
EXPECT_EQ(view_tensor.is_continuous(), true);
733+
}
734+
735+
TEST_F(OVTensorTest, checkIsContinuousTensor2Dimensions) {
736+
ov::Tensor tensor(ov::element::f32, ov::Shape{32, 128});
737+
auto data = tensor.data();
738+
auto strides = tensor.get_strides();
739+
740+
ov::Tensor view_tensor;
741+
742+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16, 128}, data, strides);
743+
EXPECT_EQ(view_tensor.is_continuous(), true);
744+
745+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 128}, data, strides);
746+
EXPECT_EQ(view_tensor.is_continuous(), true);
747+
748+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 16}, data, strides);
749+
EXPECT_EQ(view_tensor.is_continuous(), true);
750+
751+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 16}, data, strides);
752+
EXPECT_EQ(view_tensor.is_continuous(), false);
753+
}
754+
755+
TEST_F(OVTensorTest, checkIsContinuousTensor3Dimensions) {
756+
ov::Tensor tensor(ov::element::f32, ov::Shape{5, 32, 128});
757+
auto data = tensor.data();
758+
auto strides = tensor.get_strides();
759+
760+
ov::Tensor view_tensor;
761+
762+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 32, 128}, data, strides);
763+
EXPECT_EQ(view_tensor.is_continuous(), true);
764+
765+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 16, 128}, data, strides);
766+
EXPECT_EQ(view_tensor.is_continuous(), false);
767+
768+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 128}, data, strides);
769+
EXPECT_EQ(view_tensor.is_continuous(), true);
770+
771+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 64}, data, strides);
772+
EXPECT_EQ(view_tensor.is_continuous(), true);
773+
774+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 16, 128}, data, strides);
775+
EXPECT_EQ(view_tensor.is_continuous(), true);
776+
}
777+
778+
TEST_F(OVTensorTest, checkIsContinuousTensor4Dimensions) {
779+
ov::Tensor tensor(ov::element::f32, ov::Shape{3, 5, 32, 128});
780+
auto data = tensor.data();
781+
auto strides = tensor.get_strides();
782+
783+
ov::Tensor view_tensor;
784+
785+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 2, 32, 128}, data, strides);
786+
EXPECT_EQ(view_tensor.is_continuous(), true);
787+
788+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 5, 32, 128}, data, strides);
789+
EXPECT_EQ(view_tensor.is_continuous(), true);
790+
791+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 2, 32, 128}, data, strides);
792+
EXPECT_EQ(view_tensor.is_continuous(), false);
793+
794+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 2, 5, 128}, data, strides);
795+
EXPECT_EQ(view_tensor.is_continuous(), false);
796+
797+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{3, 5, 32, 64}, data, strides);
798+
EXPECT_EQ(view_tensor.is_continuous(), false);
799+
800+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 16, 128}, data, strides);
801+
EXPECT_EQ(view_tensor.is_continuous(), true);
802+
803+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 1, 16, 128}, data, strides);
804+
EXPECT_EQ(view_tensor.is_continuous(), false);
805+
806+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 1, 128}, data, strides);
807+
EXPECT_EQ(view_tensor.is_continuous(), true);
808+
809+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 1, 32}, data, strides);
810+
EXPECT_EQ(view_tensor.is_continuous(), true);
811+
}
812+
712813
struct TestParams {
713814
ov::Shape src_shape;
714815
ov::Strides src_strides;

src/plugins/intel_npu/tests/functional/behavior/remote_tensor_tests/remote_run.hpp

+128
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,134 @@ class RemoteRunTests : public ov::test::behavior::OVPluginTestBase,
9393
}
9494
};
9595

96+
TEST_P(RemoteRunTests, CheckIsContinuousHostTensorScalar) {
97+
// Skip test according to plugin specific disabledTestPatterns() (if any)
98+
SKIP_IF_CURRENT_TEST_IS_DISABLED()
99+
100+
auto zero_context = core->get_default_context(target_device);
101+
102+
auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{});
103+
auto data = host_tensor.data();
104+
auto strides = host_tensor.get_strides();
105+
106+
ov::Tensor view_tensor;
107+
108+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{}, data, strides);
109+
EXPECT_EQ(view_tensor.is_continuous(), true);
110+
}
111+
112+
TEST_P(RemoteRunTests, CheckIsContinuousHostTensor1Dimension) {
113+
// Skip test according to plugin specific disabledTestPatterns() (if any)
114+
SKIP_IF_CURRENT_TEST_IS_DISABLED()
115+
116+
auto zero_context = core->get_default_context(target_device);
117+
118+
auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{128});
119+
auto data = host_tensor.data();
120+
auto strides = host_tensor.get_strides();
121+
122+
ov::Tensor view_tensor;
123+
124+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{128}, data, strides);
125+
EXPECT_EQ(view_tensor.is_continuous(), true);
126+
127+
view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16}, data, strides);
128+
EXPECT_EQ(view_tensor.is_continuous(), true);
129+
}
130+
131+
TEST_P(RemoteRunTests, CheckIsContinuousHostTensor2Dimensions) {
132+
// Skip test according to plugin specific disabledTestPatterns() (if any)
133+
SKIP_IF_CURRENT_TEST_IS_DISABLED()
134+
135+
auto zero_context = core->get_default_context(target_device);
136+
137+
auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{32, 128});
138+
auto data = host_tensor.data();
139+
auto strides = host_tensor.get_strides();
140+
141+
ov::Tensor view_tensor;
142+
143+
view_tensor = ov::Tensor(ov::element::f32, Shape{16, 128}, data, strides);
144+
EXPECT_EQ(view_tensor.is_continuous(), true);
145+
146+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 128}, data, strides);
147+
EXPECT_EQ(view_tensor.is_continuous(), true);
148+
149+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 16}, data, strides);
150+
EXPECT_EQ(view_tensor.is_continuous(), true);
151+
152+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 16}, data, strides);
153+
EXPECT_EQ(view_tensor.is_continuous(), false);
154+
}
155+
156+
TEST_P(RemoteRunTests, CheckIsContinuousHostTensor3Dimensions) {
157+
// Skip test according to plugin specific disabledTestPatterns() (if any)
158+
SKIP_IF_CURRENT_TEST_IS_DISABLED()
159+
160+
auto zero_context = core->get_default_context(target_device);
161+
162+
auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{5, 32, 128});
163+
auto data = host_tensor.data();
164+
auto strides = host_tensor.get_strides();
165+
166+
ov::Tensor view_tensor;
167+
168+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 32, 128}, data, strides);
169+
EXPECT_EQ(view_tensor.is_continuous(), true);
170+
171+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 16, 128}, data, strides);
172+
EXPECT_EQ(view_tensor.is_continuous(), false);
173+
174+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 128}, data, strides);
175+
EXPECT_EQ(view_tensor.is_continuous(), true);
176+
177+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 64}, data, strides);
178+
EXPECT_EQ(view_tensor.is_continuous(), true);
179+
180+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 16, 128}, data, strides);
181+
EXPECT_EQ(view_tensor.is_continuous(), true);
182+
}
183+
184+
TEST_P(RemoteRunTests, CheckIsContinuousHostTensor4Dimensions) {
185+
// Skip test according to plugin specific disabledTestPatterns() (if any)
186+
SKIP_IF_CURRENT_TEST_IS_DISABLED()
187+
188+
auto zero_context = core->get_default_context(target_device);
189+
190+
auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{3, 5, 32, 128});
191+
auto data = host_tensor.data();
192+
auto strides = host_tensor.get_strides();
193+
194+
ov::Tensor view_tensor;
195+
196+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 2, 32, 128}, data, strides);
197+
EXPECT_EQ(view_tensor.is_continuous(), true);
198+
199+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 5, 32, 128}, data, strides);
200+
EXPECT_EQ(view_tensor.is_continuous(), true);
201+
202+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 2, 32, 128}, data, strides);
203+
EXPECT_EQ(view_tensor.is_continuous(), false);
204+
205+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 2, 5, 128}, data, strides);
206+
EXPECT_EQ(view_tensor.is_continuous(), false);
207+
208+
view_tensor = ov::Tensor(ov::element::f32, Shape{3, 5, 32, 64}, data, strides);
209+
EXPECT_EQ(view_tensor.is_continuous(), false);
210+
211+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 16, 128}, data, strides);
212+
EXPECT_EQ(view_tensor.is_continuous(), true);
213+
214+
view_tensor = ov::Tensor(ov::element::f32, Shape{2, 1, 16, 128}, data, strides);
215+
EXPECT_EQ(view_tensor.is_continuous(), false);
216+
217+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 1, 128}, data, strides);
218+
EXPECT_EQ(view_tensor.is_continuous(), true);
219+
220+
view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 1, 32}, data, strides);
221+
EXPECT_EQ(view_tensor.is_continuous(), true);
222+
}
223+
96224
TEST_P(RemoteRunTests, CheckRemoteTensorInternalBuf) {
97225
// Skip test according to plugin specific disabledTestPatterns() (if any)
98226
SKIP_IF_CURRENT_TEST_IS_DISABLED()

0 commit comments

Comments
 (0)