Skip to content

Commit

Permalink
[GPU] Roll dynamic shape implementation (openvinotoolkit#23617)
Browse files Browse the repository at this point in the history
### Details:
 - Roll dynamic shape implementation
 - Add single layer test for dynamic input shape

### Tickets:
 - 135486
 - 101356
 - 124503
 - 135711
  • Loading branch information
wilson-seok authored Apr 1, 2024
1 parent 6af05ba commit e22191c
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 28 deletions.
19 changes: 19 additions & 0 deletions src/plugins/intel_gpu/include/intel_gpu/primitives/roll.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,31 @@ struct roll : primitive_base<roll> {
: primitive_base(id, {input}, {output_padding}),
shift(shift) {}

/// @brief Constructs roll primitive for dynamic shape.
/// @param id This primitive id.
/// @param input Input primitive id.
/// @param raw_shift raw shift vector
/// @param raw_axes raw axes vector
roll(const primitive_id& id,
const input_info& input,
const std::vector<int32_t>& raw_shift,
const std::vector<int32_t>& raw_axes,
const padding& output_padding = {})
: primitive_base(id, {input}, {output_padding}),
raw_shift(raw_shift), raw_axes(raw_axes) {}

/// @brief Tensor which specifies the number of places by which the elements are shifted.
tensor shift;

/// @brief Raw shift/axes vector to calculate normalized shift when input shape becomes static
std::vector<int32_t> raw_shift;
std::vector<int32_t> raw_axes;

size_t hash() const override {
size_t seed = primitive::hash();
seed = hash_combine(seed, shift.hash());
seed = hash_range(seed, raw_shift.begin(), raw_shift.end());
seed = hash_range(seed, raw_axes.begin(), raw_axes.end());
return seed;
}

Expand Down
39 changes: 37 additions & 2 deletions src/plugins/intel_gpu/src/graph/impls/ocl/roll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,43 @@ struct roll_impl : typed_primitive_impl_ocl<roll> {
const auto& primitive = impl_param.typed_desc<roll>();
auto params = get_default_params<kernel_selector::roll_params>(impl_param);

params.shift = convert_dim_vector(primitive->shift);

if ((primitive->raw_shift.empty()) && (primitive->raw_axes.empty())) {
// Primitive created with static shape input
params.shift = convert_dim_vector(primitive->shift);
} else {
// Primitive created with dynamic shape input
const auto input_layout = impl_param.get_input_layout(0);
const auto& input_shape = input_layout.get_shape();
const auto rank = static_cast<int>(input_layout.get_rank());
const auto format = cldnn::format::get_default_format(rank);
const auto default_rank = format.dimension();
auto axes_raw = primitive->raw_axes;
auto shift_raw = primitive->raw_shift;

// Normalize axes and sum shift
std::vector<int32_t> shift(default_rank);
for (size_t a = 0; a < axes_raw.size(); ++a) {
auto& axis = axes_raw[a];
if (axis < 0) {
axis += rank;
}
if (axis < 0 || axis >= rank) {
OPENVINO_THROW(" Incorrect axis value: ", axis);
}
shift[axis] += shift_raw[a];
}

// Normalize shift
for (int s = 0; s < rank; ++s) {
auto& sh = shift[s];
const auto dim = static_cast<int32_t>(input_shape[s]);
sh %= dim;
if (sh < 0) {
sh += dim;
}
}
params.shift = convert_dim_vector({format, shift});
}
return params;
}
};
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/intel_gpu/src/graph/include/roll_inst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
#include "primitive_inst.h"

namespace cldnn {
template <>
struct typed_program_node<roll> : public typed_program_node_base<roll> {
using parent = typed_program_node_base<roll>;

public:
using parent::parent;

program_node& input(size_t idx = 0) const { return get_dependency(idx); }
std::vector<size_t> get_shape_infer_dependencies() const override { return {}; }
};

using roll_node = typed_program_node<roll>;

Expand Down
57 changes: 31 additions & 26 deletions src/plugins/intel_gpu/src/plugin/ops/roll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ void CreateRollOp(ProgramBuilder& p, const std::shared_ptr<ov::op::v7::Roll>& op
const auto layer_name = layer_type_name_ID(op);
const auto& op_friendly_name = op->get_friendly_name();
const auto& input_pshape = op->get_input_partial_shape(0);
OPENVINO_ASSERT(input_pshape.is_static(), "Dynamic shapes are not supported for Roll operation yet");
const auto& input_shape = input_pshape.to_shape();
const auto rank = static_cast<int>(input_shape.size());
const auto format = cldnn::format::get_default_format(rank);
const auto default_rank = format.dimension();

auto shift_constant = std::dynamic_pointer_cast<ov::op::v0::Constant>(op->get_input_node_shared_ptr(1));
OPENVINO_ASSERT(shift_constant != nullptr, "[GPU] Unsupported parameter nodes type in ", op_friendly_name, " (", op->get_type_name(), ")");
Expand All @@ -35,31 +30,41 @@ void CreateRollOp(ProgramBuilder& p, const std::shared_ptr<ov::op::v7::Roll>& op
OPENVINO_ASSERT(axes_constant != nullptr, "[GPU] Unsupported parameter nodes type in ", op_friendly_name, " (", op->get_type_name(), ")");
auto axes_raw = axes_constant->cast_vector<int32_t>();

// Normalize axes and sum shift
std::vector<int32_t> shift(default_rank);
for (size_t a = 0; a < axes_raw.size(); ++a) {
auto& axis = axes_raw[a];
if (axis < 0) {
axis += rank;
}
if (axis < 0 || axis >= rank) {
OPENVINO_THROW(op_friendly_name, " Incorrect axis value: ", axis);
if (input_pshape.is_dynamic()) {
const cldnn::roll roll_prim(layer_name, inputs.front(), shift_raw, axes_raw);
p.add_primitive(*op, roll_prim);
} else {
const auto& input_shape = input_pshape.to_shape();
const auto rank = static_cast<int>(input_shape.size());
const auto format = cldnn::format::get_default_format(rank);
const auto default_rank = format.dimension();

// Normalize axes and sum shift
std::vector<int32_t> shift(default_rank);
for (size_t a = 0; a < axes_raw.size(); ++a) {
auto& axis = axes_raw[a];
if (axis < 0) {
axis += rank;
}
if (axis < 0 || axis >= rank) {
OPENVINO_THROW(op_friendly_name, " Incorrect axis value: ", axis);
}
shift[axis] += shift_raw[a];
}
shift[axis] += shift_raw[a];
}

// Normalize shift
for (int s = 0; s < rank; ++s) {
auto& sh = shift[s];
const auto dim = static_cast<int32_t>(input_shape[s]);
sh %= dim;
if (sh < 0) {
sh += dim;
// Normalize shift
for (int s = 0; s < rank; ++s) {
auto& sh = shift[s];
const auto dim = static_cast<int32_t>(input_shape[s]);
sh %= dim;
if (sh < 0) {
sh += dim;
}
}
}

const cldnn::roll roll_prim(layer_name, inputs.front(), {format, shift});
p.add_primitive(*op, roll_prim);
const cldnn::roll roll_prim(layer_name, inputs.front(), {format, shift});
p.add_primitive(*op, roll_prim);
}
}

} // namespace
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include <random>

#include "common_test_utils/ov_tensor_utils.hpp"
#include "common_test_utils/test_enums.hpp"
#include "shared_test_classes/base/ov_subgraph.hpp"

#include "openvino/op/parameter.hpp"
#include "openvino/op/constant.hpp"
#include "openvino/op/result.hpp"
#include "openvino/op/roll.hpp"

namespace ov {
namespace test {
namespace {
using ov::test::InputShape;

using RollGPUTestParams = typename std::tuple<
InputShape, // Input shape
ov::element::Type, // Input precision
std::vector<int64_t>, // Shift
std::vector<int64_t>, // Axes
std::string>; // Device name

class RollLayerGPUTest : public testing::WithParamInterface<RollGPUTestParams>,
virtual public SubgraphBaseTest {
public:
static std::string getTestCaseName(testing::TestParamInfo<RollGPUTestParams> obj) {
InputShape inputShape;
ov::element::Type inputPrecision;
std::vector<int64_t> shift;
std::vector<int64_t> axes;
std::string targetDevice;
std::tie(inputShape, inputPrecision, shift, axes, targetDevice) = obj.param;

std::ostringstream result;
result << "IS=" << ov::test::utils::partialShape2str({inputShape.first}) << "_";
result << "TS=";
for (const auto& item : inputShape.second) {
result << ov::test::utils::vec2str(item) << "_";
}
result << "Precision=" << inputPrecision.get_type_name() << "_";
result << "Shift=" << ov::test::utils::vec2str(shift) << "_";
result << "Axes=" << ov::test::utils::vec2str(axes) << "_";
result << "TargetDevice=" << targetDevice;

return result.str();
}

protected:
void SetUp() override {
InputShape inputShape;
ov::element::Type inputPrecision;
std::vector<int64_t> shift;
std::vector<int64_t> axes;

std::tie(inputShape, inputPrecision, shift, axes, targetDevice) = GetParam();

init_input_shapes({inputShape});

ov::ParameterVector paramsIn;
for (auto&& shape : inputDynamicShapes) {
paramsIn.push_back(std::make_shared<ov::op::v0::Parameter>(inputPrecision, shape));
}
auto shiftNode =
std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{shift.size()}, shift)->output(0);
auto axesNode =
std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{axes.size()}, axes)->output(0);

const auto roll = std::make_shared<ov::op::v7::Roll>(paramsIn[0], shiftNode, axesNode);
const ov::ResultVector results{std::make_shared<ov::op::v0::Result>(roll)};
function = std::make_shared<ov::Model>(results, paramsIn, "roll");
}
};

TEST_P(RollLayerGPUTest, CompareWithRefs) {
run();
}

const std::vector<ov::element::Type> inputPrecisions = {
ov::element::i8,
ov::element::u8,
ov::element::i32,
ov::element::f16,
ov::element::f32,
};

const std::vector<InputShape> data2DZeroShiftShapes = {{{}, {{17, 19}}}, {{-1, -1}, {{5, 17}, {10, 20}}}};
const std::vector<InputShape> data1DShapes = {{{}, {{12}}}, {{-1}, {{10}, {20}}}};
const std::vector<InputShape> data2DShapes = {{{}, {{100, 200}}}, {{{100, 500}, 450}, {{250, 450}, {120, 450}}}};
const std::vector<InputShape> data3DShapes = {{{}, {{2, 300, 320}}},
{{2, {100, 500}, -1}, {{2, 320, 420}, {2, 500, 200}}}};
const std::vector<InputShape> data4DNegativeAxesShapes = {{{}, {{3, 11, 6, 4}}},
{{-1, -1, {5, 6}, -1}, {{5, 10, 6, 15}, {10, 20, 5, 7}}}};
const std::vector<InputShape> data5DRepeatingAxesNegativeShiftShapes = {{{}, {{2, 7, 32, 32, 5}}},
{{2, -1, -1, -1, {2, 7}}, {{2, 5, 20, 17, 3}, {2, 10, 18, 40, 7}}}};

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_2DZeroShift, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data2DZeroShiftShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{0, 0}), // Shift
::testing::Values(std::vector<int64_t>{0, 1}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_1D, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data1DShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{5}), // Shift
::testing::Values(std::vector<int64_t>{0}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_2D, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data2DShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{50, 150}), // Shift
::testing::Values(std::vector<int64_t>{0, 1}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_3D, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data3DShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{160, 150}), // Shift
::testing::Values(std::vector<int64_t>{1, 2}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_4DNegativeAxes, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data4DNegativeAxesShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{7, 3}), // Shift
::testing::Values(std::vector<int64_t>{-3, -2}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

INSTANTIATE_TEST_SUITE_P(smoke_RollGPU_5DRepeatingAxesNegativeShift, RollLayerGPUTest,
::testing::Combine(
::testing::ValuesIn(data5DRepeatingAxesNegativeShiftShapes),
::testing::ValuesIn(inputPrecisions),
::testing::Values(std::vector<int64_t>{4, -1, 7, 2, -5}), // Shift
::testing::Values(std::vector<int64_t>{-1, 0, 3, 3, 2}), // Axes
::testing::Values(ov::test::utils::DEVICE_GPU)),
RollLayerGPUTest::getTestCaseName);

} // namespace
} // namespace test
} // namespace ov

0 comments on commit e22191c

Please sign in to comment.