[GPU] reorder_data supports fusing two or more activations (#23306)

### Details:
 - *Currently reorder_data supports fusing only one activation*
 - *reorder_data should be able to fuse two or more activations*

### Tickets:
 - *132988*
This commit is contained in:
David Nam 2024-03-13 07:52:03 +08:00 committed by GitHub
parent d0c20f7714
commit 14bc4222a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 131 additions and 32 deletions

View File

@ -137,42 +137,81 @@ KERNEL (reorder_data)(
#endif
#if defined INPUT0_LAYOUT_NV12 && !SURFACE_INPUT
uint8 ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 0, v, u, w, z, y, x);
uint output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(R), NL_M, NL_N);
ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 1, v, u, w, z, y, x);
output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(G), NL_M, NL_N);
ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 2, v, u, w, z, y, x);
output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(B), NL_M, NL_N);
uint8 ov0 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 0, v, u, w, z, y, x);
uint8 ov1 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 1, v, u, w, z, y, x);
uint8 ov2 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 2, v, u, w, z, y, x);
uint output_idx_R = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov0.s0, ov0.s1, ov0.s2, ov0.s3, ov0.s4, ov0.s5, ov0.s6, ov0.s7);
uint output_idx_G = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov1.s0, ov1.s1, ov1.s2, ov1.s3, ov1.s4, ov1.s5, ov1.s6, ov1.s7);
uint output_idx_B = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov2.s0, ov2.s1, ov2.s2, ov2.s3, ov2.s4, ov2.s5, ov2.s6, ov2.s7);
#if HAS_FUSED_OPS
res = TO_OUTPUT_REORDER_TYPE(R);
FUSED_OPS;
output[output_idx_R] = FUSED_OPS_RESULT;
res = TO_OUTPUT_REORDER_TYPE(G);
FUSED_OPS;
output[output_idx_G] = FUSED_OPS_RESULT;
res = TO_OUTPUT_REORDER_TYPE(B);
FUSED_OPS;
output[output_idx_B] = FUSED_OPS_RESULT;
#else
output[output_idx_R] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(R), NL_M, NL_N);
output[output_idx_G] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(G), NL_M, NL_N);
output[output_idx_B] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(B), NL_M, NL_N);
#endif
#elif INPUT0_LAYOUT_IMAGE_2D_RGBA
uint8 ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 0, v, u, w, z, y, x);
uint output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s0), NL_M, NL_N);
ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 1, v, u, w, z, y, x);
output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s1), NL_M, NL_N);
ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 2, v, u, w, z, y, x);
output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s2), NL_M, NL_N);
#if INPUT0_FEATURE_NUM == 4
ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 3, v, u, w, z, y, x);
output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s3), NL_M, NL_N);
#endif
uint8 ov0 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 0, v, u, w, z, y, x);
uint8 ov1 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 1, v, u, w, z, y, x);
uint8 ov2 = RESHAPE_DIMS(INPUT0, OUTPUT, b, 2, v, u, w, z, y, x);
uint output_idx_0 = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov0.s0, ov0.s1, ov0.s2, ov0.s3, ov0.s4, ov0.s5, ov0.s6, ov0.s7);
uint output_idx_1 = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov1.s0, ov1.s1, ov1.s2, ov1.s3, ov1.s4, ov1.s5, ov1.s6, ov1.s7);
uint output_idx_2 = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov2.s0, ov2.s1, ov2.s2, ov2.s3, ov2.s4, ov2.s5, ov2.s6, ov2.s7);
#if HAS_FUSED_OPS
res = TO_OUTPUT_REORDER_TYPE(colorRGBA.s0);
FUSED_OPS;
output[output_idx_0] = FUSED_OPS_RESULT;
res = TO_OUTPUT_REORDER_TYPE(colorRGBA.s1);
FUSED_OPS;
output[output_idx_1] = FUSED_OPS_RESULT;
res = TO_OUTPUT_REORDER_TYPE(colorRGBA.s2);
FUSED_OPS;
output[output_idx_2] = FUSED_OPS_RESULT;
#else
output[output_idx_0] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s0), NL_M, NL_N);
output[output_idx_1] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s1), NL_M, NL_N);
output[output_idx_2] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s2), NL_M, NL_N);
#endif
#if INPUT0_FEATURE_NUM == 4
uint8 ov = RESHAPE_DIMS(INPUT0, OUTPUT, b, 3, v, u, w, z, y, x);
uint output_idx = FUNC_CALL(get_output_index)(OPTIONAL_SHAPE_INFO_TENSOR ov.s0, ov.s1, ov.s2, ov.s3, ov.s4, ov.s5, ov.s6, ov.s7);
#if HAS_FUSED_OPS
res = TO_OUTPUT_REORDER_TYPE(colorRGBA.s3);
FUSED_OPS;
output[output_idx] = FUSED_OPS_RESULT;
#else
output[output_idx] = ACTIVATION_FUNC_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(colorRGBA.s3), NL_M, NL_N);
#endif
#endif
#elif OUTPUT_LAYOUT_IMAGE_2D_RGBA
IMAGE_WRITE(output, (int2)(x, y), colorRGBA);
#else
#if INPUT0_IS_FP && !OUTPUT_IS_FP
#if CONVERT_TRUNCATE
output[output_idx] = ACTIVATION_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(convert_long(res)), ACTIVATION_PARAMS_TYPED);
#else
output[output_idx] = ACTIVATION_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE_SAT(res), ACTIVATION_PARAMS_TYPED);
#endif
#else
output[output_idx] = ACTIVATION_TYPED(OUTPUT_REORDER, TO_OUTPUT_REORDER_TYPE(res), ACTIVATION_PARAMS_TYPED);
#endif
#if INPUT0_IS_FP && !OUTPUT_IS_FP
#if CONVERT_TRUNCATE
#define __TO_OUTPUT_REORDER_TYPE(res) TO_OUTPUT_REORDER_TYPE(convert_long(res))
#else
#define __TO_OUTPUT_REORDER_TYPE(res) TO_OUTPUT_REORDER_TYPE_SAT(res)
#endif
#else
#define __TO_OUTPUT_REORDER_TYPE(res) TO_OUTPUT_REORDER_TYPE(res)
#endif
#if HAS_FUSED_OPS
res = __TO_OUTPUT_REORDER_TYPE(res);
FUSED_OPS;
output[output_idx] = FUSED_OPS_RESULT;
#else
output[output_idx] = ACTIVATION_TYPED(OUTPUT_REORDER, __TO_OUTPUT_REORDER_TYPE(res), ACTIVATION_PARAMS_TYPED);
#endif
#undef __TO_OUTPUT_REORDER_TYPE
#endif
}

View File

@ -41,6 +41,17 @@ JitConstants ReorderKernelRef::GetJitConstants(const reorder_params& params) con
if (params.surface_input)
jit.AddConstant(MakeJitConstant("SURFACE_INPUT", true));
if (!params.fused_ops.empty()) {
std::vector<std::string> idx_order;
if (DataTensor::ChannelsCount(params.outputs[0].GetLayout()) == 4) {
idx_order = {"b", "f", "y", "x"};
} else if (DataTensor::ChannelsCount(params.outputs[0].GetLayout()) == 5) {
idx_order = {"b", "f", "z", "y", "x"};
}
FusedOpsConfiguration conf = {"", idx_order, "res", GetUnitType(params), 1};
jit.Merge(MakeFusedOpsJitConstants(params, {conf}));
}
return jit;
}

View File

@ -583,6 +583,55 @@ TEST(reorder_gpu_f32, basic_subtract_value) {
}
}
TEST(reorder_gpu_f32, fusing_double_activations) {
// reorder_data reorder_data
// | |
// sqrt |
// | fuse |
// power data ----> | data
// \ / | /
// divide divide
// | |
// result result
//
// This test case is limited to the case of reorder_data using ReorderKernelRef.
// Because other kernels for reorder_data don't support fusing double activations e.g. reorder_data_fast_b1
//
auto& engine = get_test_engine();
auto input1 = engine.allocate_memory({{1}, data_types::f32, format::bfyx});
auto input2 = engine.allocate_memory({{1, 1, 1, 2, 2}, data_types::f32, format::bfzyx});
topology topology {
input_layout("input1", input1->get_layout()),
reorder("reorder", input_info("input1"), format::bfyx, data_types::f32),
activation("sqrt", input_info("reorder"), activation_func::sqrt),
activation("power", input_info("sqrt"), activation_func::pow),
input_layout("input2", input2->get_layout()),
eltwise("divide", {input_info("power"), input_info("input2")}, eltwise_mode::div),
reorder("result", input_info("divide"), format::bfyx, data_types::f32)
};
set_values(input1, {25000});
set_values(input2, {0.1f, 0.2f, 0.5f, 1.0f});
ExecutionConfig config = get_test_default_config(engine);
ov::intel_gpu::ImplementationDesc reorder_impl = {format::bfyx, "reorder_data"};
config.set_property(ov::intel_gpu::force_implementations(ov::intel_gpu::ImplForcingMap{{"reorder", reorder_impl}}));
network network(engine, topology, config);
network.set_input_data("input1", input1);
network.set_input_data("input2", input2);
auto output = network.execute();
mem_lock<float> output_mem(output.at("result").get_memory(), network.get_stream());
std::vector<int32_t> output_ref = {10, 5, 2, 1};
for (size_t i = 0; i < output_mem.size(); ++i) {
ASSERT_EQ(output_mem[i], output_ref[i]);
}
}
TEST(reorder_gpu_f16, basic_subtract_f32_output_f32) {
// Input : 2x2x2x2 (FP16)
// Output : 2x2x2x2 (FP32)