diff --git a/src/plugins/intel_gpu/src/graph/loop.cpp b/src/plugins/intel_gpu/src/graph/loop.cpp index 4cc6f1fca06..767ccce448e 100644 --- a/src/plugins/intel_gpu/src/graph/loop.cpp +++ b/src/plugins/intel_gpu/src/graph/loop.cpp @@ -479,11 +479,18 @@ void loop_inst::preprocess_input_memory(const int64_t num_iterations) { if (input_map->axis < 0) { auto input_inst = body_network->get_primitive(internal_id.pid); - if (memory->get_layout() != input_inst->get_output_layout()) { - input_inst->set_output_layout(memory->get_layout()); + if (!input_inst->get_output_layout().identical(_impl_params->get_input_layout(memory_num))) { + input_inst->set_output_layout(_impl_params->get_input_layout(memory_num)); + } + + if (!input_inst->get_output_layout().is_dynamic() && !memory->get_layout().identical(input_inst->get_output_layout())) { + OPENVINO_ASSERT(input_inst->get_output_layout().bytes_count() <= memory->get_layout().bytes_count(), + "input layout size(", input_inst->get_output_layout().to_short_string(), + ") should not exceed memory size(", memory->get_layout().to_short_string(), ")"); + memory = body_network->get_engine().reinterpret_buffer(*memory, input_inst->get_output_layout()); GPU_DEBUG_LOG << input_inst->id() << " is changed memory because layout is changed from " - << input_inst->get_output_layout().to_short_string() - << " to " << memory->get_layout().to_short_string() << std::endl; + << memory->get_layout().to_short_string() + << " to " << input_inst->get_output_layout().to_short_string() << std::endl; } auto internal_input_memory = memory; @@ -526,6 +533,15 @@ void loop_inst::preprocess_backedge_memory() { OPENVINO_ASSERT(!input_map_ptrs.empty(), id(), " has no input_mapping for backedged input"); auto& external_id = input_map_ptrs.front()->external_id; initial_mem = get_external_memory(external_id.pid, external_id.idx); + // in case where memory buffer has been over-allocated by shape predictor, memory layout might be unexpected shape. + // so memory layout needs to be re-interprete according to original layout. + auto initial_layout = get_external_output_layout(external_id.pid, external_id.idx); + if (initial_mem != nullptr && !initial_mem->get_layout().identical(initial_layout)) { + OPENVINO_ASSERT(initial_layout.bytes_count() <= initial_mem->get_layout().bytes_count(), + "initial layout size(", initial_layout.to_short_string(), + ") should not exceed initial memory size(", initial_mem->get_layout().to_short_string(), ")"); + initial_mem = body_network->get_engine().reinterpret_buffer(*initial_mem, initial_layout); + } GPU_DEBUG_LOG << idx << ") back_edge mapping - back_edge.from " << back_edge.from << std::endl; GPU_DEBUG_LOG << idx << ") back_edge mapping - back_edge.to " << back_edge.to << std::endl; @@ -985,7 +1001,17 @@ int64_t loop_inst::get_num_iterations() { void loop_inst::set_memory_in_body_network(cldnn::network::ptr body_network, const std::shared_ptr& inst, memory::ptr mem) { if (inst->is_input()) { - body_network->set_input_data(inst->id(), mem); + // in case where memory buffer has been over-allocated by shape predictor, memory layout might be unexpected shape. + // so memory layout needs to be re-interprete according to original layout. + memory::ptr updated_mem = mem; + layout impl_layout = inst->get_impl_params()->get_output_layout(); + OPENVINO_ASSERT(impl_layout.bytes_count() <= updated_mem->get_layout().bytes_count(), + "impl_params layout size(", impl_layout.to_short_string(), + ") should not exceed memory size(", updated_mem->get_layout().to_short_string(), ")"); + if (impl_layout.bytes_count() < updated_mem->get_layout().bytes_count()) { + updated_mem = body_network->get_engine().reinterpret_buffer(*updated_mem, impl_layout); + } + body_network->set_input_data(inst->id(), updated_mem); } else if (inst->is_output()) { body_network->set_output_memory(inst->id(), mem); } else { @@ -1053,7 +1079,7 @@ std::vector loop_inst::handle_buffers_for_next_iteration(const loop_ if (iter == 0) { auto to_id = mapping.to_primitive->id(); // Check backedge_to shape needs to be updated by initial_mem - if (!mapping.initial_mem->get_layout().identical(to_mem->get_layout())) { + if (mapping.initial_mem != nullptr && !mapping.initial_mem->get_layout().identical(to_mem->get_layout())) { to_mem = body_network->get_engine().allocate_memory(mapping.initial_mem->get_layout(), false); body_network->set_input_data(to_id, to_mem); ev = to_mem->copy_from(body_network->get_stream(), *(mapping.initial_mem)); diff --git a/src/plugins/intel_gpu/tests/unit/test_cases/loop_gpu_test.cpp b/src/plugins/intel_gpu/tests/unit/test_cases/loop_gpu_test.cpp index c1f914c12ad..c5211e39b69 100644 --- a/src/plugins/intel_gpu/tests/unit/test_cases/loop_gpu_test.cpp +++ b/src/plugins/intel_gpu/tests/unit/test_cases/loop_gpu_test.cpp @@ -16,8 +16,11 @@ #include #include #include +#include "intel_gpu/primitives/permute.hpp" #include +#include "program_wrapper.h" + #include #include #include @@ -435,8 +438,6 @@ TEST(loop_gpu, basic_concat_nested_cached) { test_loop_gpu_basic_concat_nested(true); } - - static void test_loop_gpu_wo_trip_count(ov::PartialShape body_input_layout, ov::PartialShape whole_layout, std::vector input_data, @@ -760,6 +761,127 @@ static void test_loop_gpu_wo_trip_count_w_multiple_shapes(ov::PartialShape body_ } } +static void test_loop_gpu_multiple_shapes(ov::PartialShape body_input_layout, + std::vector whole_layouts, + std::vector> input_data_list, + std::vector expected_output_data, + int32_t axis, + size_t exit_value, + bool is_caching_test = false) { + auto& engine = get_test_engine(); + + auto b_input_layout = cldnn::layout{ body_input_layout, data_types::f32, format::bfyx }; + auto const_layout = cldnn::layout{ {}, data_types::i64, format::bfyx }; + + auto e_initial_condition_mem = engine.allocate_memory(const_layout); + auto e_num_iteration_mem = engine.allocate_memory(const_layout); + auto b_exit_value_mem = engine.allocate_memory(const_layout); + auto b_index_inc_mem = engine.allocate_memory(const_layout); + + // initialize input buffers + set_values(e_initial_condition_mem, {1}); + set_values(b_exit_value_mem, {exit_value}); + set_values(b_index_inc_mem, {1}); + set_values(e_num_iteration_mem, {10}); + + primitive_id body_current_iteration_id = "b_index"; + primitive_id body_execution_condition_id = "b_cond_exit_value"; + + cldnn::topology body( + input_layout(body_current_iteration_id, const_layout), + input_layout("b_add_data", b_input_layout), + input_layout("b_mul_data", b_input_layout), + data("b_exit_value", b_exit_value_mem), + data("b_index_inc", b_index_inc_mem), + eltwise("b_index_update", input_info(body_current_iteration_id), input_info("b_index_inc"), eltwise_mode::sum), + reorder("b_index_cast", input_info("b_index_update"), + cldnn::format::any, data_types::f32, {}, cldnn::reorder_mean_mode::subtract, cldnn::padding(), true), + eltwise(body_execution_condition_id, input_info("b_index"), input_info("b_exit_value"), eltwise_mode::lt), + eltwise("b_add", input_info("b_add_data"), input_info("b_index_cast"), eltwise_mode::sum), + eltwise("b_mul", input_info("b_mul_data"), input_info("b_index_cast"), eltwise_mode::prod)); + + primitive_id trip_count_id = ""; + primitive_id actual_iteration_count_id = "actual_iteration_count"; + primitive_id initial_condition_id = "initial_condition"; + int64_t num_iterations = -1; + + std::vector input_primitive_maps { + loop::io_primitive_map("input1", "b_add_data", axis), + loop::io_primitive_map("input2", "b_mul_data", axis), + loop::io_primitive_map(actual_iteration_count_id, body_current_iteration_id) }; + std::vector output_primitive_maps { + loop::io_primitive_map(cldnn::input_info("loop", 0), cldnn::input_info("b_add", 0), axis), + loop::io_primitive_map(cldnn::input_info("loop", 1), cldnn::input_info("b_mul", 0), axis) }; + std::vector back_edges { + loop::backedge_mapping("b_index_update", body_current_iteration_id) }; + + auto body_program = build_program(engine, body, body_execution_condition_id, output_primitive_maps, back_edges, true); + + auto const_shape = engine.allocate_memory({ov::PartialShape{4}, data_types::i32, format::bfyx}); + std::vector body_input_layouts; + for (size_t i = 0; i < body_input_layout.size(); i++) { + if (body_input_layout[i].is_dynamic()) + body_input_layouts.push_back(-1); + else + body_input_layouts.push_back(body_input_layout[i].get_length()); + } + set_values(const_shape, body_input_layouts); + + cldnn::topology topology( + input_layout("input_origin", b_input_layout), + input_layout(initial_condition_id, e_initial_condition_mem->get_layout()), + mutable_data(actual_iteration_count_id, e_num_iteration_mem), + permute("input2", input_info("input_origin"), {0, 1, 2, 3}), + data("const", const_shape), + permute("permute1", input_info("input_origin"), {0, 1, 2, 3}), + concatenation("input1", {input_info("permute1"), input_info("input_origin")}, 0), + loop("loop", + {input_info(actual_iteration_count_id), input_info(initial_condition_id), input_info("input1"), input_info("input2")}, + body_program, trip_count_id, initial_condition_id, actual_iteration_count_id, + input_primitive_maps, output_primitive_maps, back_edges, + num_iterations, body_current_iteration_id, body_execution_condition_id, 2), + eltwise("out_sum", input_info("loop", 0), input_info("loop", 1), eltwise_mode::sum)); + + ExecutionConfig config = get_test_default_config(engine); + config.set_property(ov::intel_gpu::allow_new_shape_infer(true)); + + network network(engine, topology, config); + for (size_t i = 0 ; i < whole_layouts.size(); i++) { + auto whole_layout = whole_layouts[i]; + auto input_data = input_data_list[i]; + + set_values(e_initial_condition_mem, {1}); + set_values(b_exit_value_mem, {exit_value}); + set_values(b_index_inc_mem, {1}); + set_values(e_num_iteration_mem, {10}); + + auto e_input_layout = cldnn::layout{ whole_layout, data_types::f32, format::bfyx }; + auto e_input_mem = engine.allocate_memory(e_input_layout); // b,f,x,y + auto expected_output_layout = whole_layout; + set_values(e_input_mem, input_data); + network.set_input_data("input_origin", e_input_mem); + + network.set_input_data(initial_condition_id, e_initial_condition_mem); + + auto outputs = network.execute(); + ASSERT_EQ(outputs.size(), 1); + auto output_layout = outputs.begin()->second.get_layout(); + auto input_layout = network.get_primitive("input1")->get_output_layout(); + + ASSERT_EQ(output_layout.batch(), input_layout.batch()); + ASSERT_EQ(output_layout.feature(), input_layout.feature()); + ASSERT_EQ(output_layout.spatial(0), input_layout.spatial(0)); + ASSERT_EQ(output_layout.spatial(1), input_layout.spatial(1)); + } +} + +std::vector input_data_2_4{ + 1.0f, 2.0f, + 4.0f, -15.f, + -15.f, 7.0f, + 0.0f, -15.f, +}; + std::vector input_data_4_4{ 1.0f, 2.0f, -15.f, 3.0f, 4.0f, -15.f, 5.0f, 6.0f, @@ -779,7 +901,7 @@ std::vector input_data_2_4_4{ 0.0f, -15.f, 0.5f, -0.5f, }; -TEST(loop_gpu, support_loop_w_dynamic_input_w_various_shapes) { +TEST(loop_gpu, support_loop_w_dynamic_input_w_various_shapes1) { test_loop_gpu_wo_trip_count_w_multiple_shapes( { 1, -1, 4, 4 }, {{ 1, 1, 4, 4 }, { 1, 2, 4, 4 }}, // axis value should be iter_num = (exit_value + 1) @@ -788,6 +910,15 @@ TEST(loop_gpu, support_loop_w_dynamic_input_w_various_shapes) { 2, 3); } +TEST(loop_gpu, support_loop_w_dynamic_input_w_various_shapes2) { + test_loop_gpu_multiple_shapes( + { 1, -1, -1, 4 }, + {{ 1, 1, 2, 4 }, { 1, 1, 4, 4 }, { 1, 2, 4, 4 }}, + {input_data_2_4, input_data_4_4, input_data_2_4_4}, + std::vector(), + -1, 10); +} + static void test_loop_gpu_wo_trip_count_update_primitive_id(ov::PartialShape body_input_layout, std::vector whole_layouts, std::vector> input_data_list,