Skip to content

Commit

Permalink
[CORE] Fix tensor is_continuous method (#28973)
Browse files Browse the repository at this point in the history
### 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 <[email protected]>
  • Loading branch information
pereanub authored Feb 14, 2025
1 parent aed285c commit 5755945
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/core/src/runtime/itensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <memory>

#include "compare.hpp"
#include "openvino/core/except.hpp"
#include "openvino/core/shape_util.hpp"
#include "openvino/core/type/element_iterator.hpp"
Expand Down Expand Up @@ -46,7 +47,21 @@ bool ITensor::is_continuous() const {
// OpenVINO doesn't support strides for lp types
return true;
}
return default_byte_strides(get_shape(), get_element_type()) == get_strides();

const auto& strides = get_strides();
auto stride = strides.rbegin();
const auto default_strides = default_byte_strides(get_shape(), get_element_type());
auto default_stride = default_strides.rbegin();

for (; stride != strides.rend(); ++stride, ++default_stride) {
if (*stride != *default_stride) {
break;
}
}

const auto default_last = default_strides.rend();
return (default_stride == default_last) || (*default_stride < *stride && (get_shape()[0] == 1) &&
std::all_of(default_stride, default_last, cmp::Equal(*default_stride)));
}

void ITensor::copy_to(const std::shared_ptr<ov::ITensor>& dst) const {
Expand Down
101 changes: 101 additions & 0 deletions src/core/tests/ov_tensor_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,107 @@ TEST_F(OVTensorTest, readRangeRoiBlobStringTensor) {
}
}

TEST_F(OVTensorTest, checkIsContinuousTensorScalar) {
ov::Tensor tensor(ov::element::f32, ov::Shape{});
auto data = tensor.data();
auto strides = tensor.get_strides();

ov::Tensor view_tensor(ov::element::f32, ov::Shape{}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_F(OVTensorTest, checkIsContinuousTensor1Dimension) {
ov::Tensor tensor(ov::element::f32, ov::Shape{128});
auto data = tensor.data();
auto strides = tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_F(OVTensorTest, checkIsContinuousTensor2Dimensions) {
ov::Tensor tensor(ov::element::f32, ov::Shape{32, 128});
auto data = tensor.data();
auto strides = tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);
}

TEST_F(OVTensorTest, checkIsContinuousTensor3Dimensions) {
ov::Tensor tensor(ov::element::f32, ov::Shape{5, 32, 128});
auto data = tensor.data();
auto strides = tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 64}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_F(OVTensorTest, checkIsContinuousTensor4Dimensions) {
ov::Tensor tensor(ov::element::f32, ov::Shape{3, 5, 32, 128});
auto data = tensor.data();
auto strides = tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 5, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 2, 5, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{3, 5, 32, 64}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{2, 1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{1, 1, 1, 32}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

struct TestParams {
ov::Shape src_shape;
ov::Strides src_strides;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,134 @@ class RemoteRunTests : public ov::test::behavior::OVPluginTestBase,
}
};

TEST_P(RemoteRunTests, CheckIsContinuousHostTensorScalar) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()

auto zero_context = core->get_default_context(target_device);

auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{});
auto data = host_tensor.data();
auto strides = host_tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_P(RemoteRunTests, CheckIsContinuousHostTensor1Dimension) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()

auto zero_context = core->get_default_context(target_device);

auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{128});
auto data = host_tensor.data();
auto strides = host_tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, ov::Shape{16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_P(RemoteRunTests, CheckIsContinuousHostTensor2Dimensions) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()

auto zero_context = core->get_default_context(target_device);

auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{32, 128});
auto data = host_tensor.data();
auto strides = host_tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, Shape{16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 16}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);
}

TEST_P(RemoteRunTests, CheckIsContinuousHostTensor3Dimensions) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()

auto zero_context = core->get_default_context(target_device);

auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{5, 32, 128});
auto data = host_tensor.data();
auto strides = host_tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 64}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_P(RemoteRunTests, CheckIsContinuousHostTensor4Dimensions) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()

auto zero_context = core->get_default_context(target_device);

auto host_tensor = zero_context.create_host_tensor(ov::element::f32, Shape{3, 5, 32, 128});
auto data = host_tensor.data();
auto strides = host_tensor.get_strides();

ov::Tensor view_tensor;

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 5, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 2, 32, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 2, 5, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, Shape{3, 5, 32, 64}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{2, 1, 16, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), false);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 1, 128}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);

view_tensor = ov::Tensor(ov::element::f32, Shape{1, 1, 1, 32}, data, strides);
EXPECT_EQ(view_tensor.is_continuous(), true);
}

TEST_P(RemoteRunTests, CheckRemoteTensorInternalBuf) {
// Skip test according to plugin specific disabledTestPatterns() (if any)
SKIP_IF_CURRENT_TEST_IS_DISABLED()
Expand Down

0 comments on commit 5755945

Please sign in to comment.