diff --git a/samples/cpp/benchmark_app/inputs_filling.cpp b/samples/cpp/benchmark_app/inputs_filling.cpp index b302d294afe..309f18a0647 100644 --- a/samples/cpp/benchmark_app/inputs_filling.cpp +++ b/samples/cpp/benchmark_app/inputs_filling.cpp @@ -584,6 +584,24 @@ ov::Tensor get_random_tensor(const std::pair(inputInfo.second); } else if (type == ov::element::boolean) { return create_tensor_random(inputInfo.second, 0, 1); + } else if (type == ov::element::string) { + const auto& in_info = inputInfo.second; + const auto tensor_size = ov::shape_size(in_info.dataShape); + auto tensor = ov::Tensor(in_info.type, in_info.dataShape); + auto data = tensor.data(); + + std::mt19937 str_len_gen(0); + uniformDistribution len_distribution(20, 50); + std::mt19937 char_val_gen(0); + uniformDistribution char_distribution(0, 127); + for (size_t i = 0; i < tensor_size; i++) { + data[i].resize(len_distribution(str_len_gen)); + for (size_t j = 0lu; j < data[i].size(); j++) { + data[i][j] = static_cast(char_distribution(char_val_gen)); + } + } + + return tensor; } else { OPENVINO_THROW("Input type is not supported for " + inputInfo.first); } diff --git a/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.cpp b/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.cpp index ab8b426f0cd..2b63c749d79 100644 --- a/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.cpp +++ b/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.cpp @@ -1,15 +1,15 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2018-2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // #include "insert_convert_after_extension.hpp" -#include +#include "openvino/op/convert.hpp" #include "cpu_types.h" #include "itt.hpp" -#include +#include "transformations/utils/utils.hpp" -ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension() { +ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension(bool convert_output_precision) { MATCHER_SCOPE(InsertConvertAfterExtension); auto i64_extension = [](const ov::Output& output) -> bool { @@ -29,6 +29,10 @@ ov::pass::InsertConvertAfterExtension::InsertConvertAfterExtension() { auto convert = std::make_shared(output, ov::element::i32); for (const auto& targetInput : targetInputs) { + // Keep the original output element type if required. + if (!convert_output_precision && is_type(targetInput.get_node())) { + continue; + } targetInput.replace_source_output(convert); } diff --git a/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp b/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp index 14c5cb72aff..06048de3f79 100644 --- a/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp +++ b/src/plugins/intel_cpu/src/transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp @@ -1,10 +1,10 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2018-2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // #pragma once -#include +#include "openvino/pass/graph_rewrite.hpp" namespace ov { namespace pass { @@ -15,7 +15,7 @@ namespace pass { class InsertConvertAfterExtension: public ov::pass::MatcherPass { public: OPENVINO_RTTI("InsertConvertAfterExtension", "0"); - InsertConvertAfterExtension(); + InsertConvertAfterExtension(bool convert_output_precision = true); }; } // namespace pass diff --git a/src/plugins/intel_cpu/src/transformations/cpu_opset/convert_to_cpu_specific_opset.hpp b/src/plugins/intel_cpu/src/transformations/cpu_opset/convert_to_cpu_specific_opset.hpp index e245d7573ab..934a86bbc8b 100644 --- a/src/plugins/intel_cpu/src/transformations/cpu_opset/convert_to_cpu_specific_opset.hpp +++ b/src/plugins/intel_cpu/src/transformations/cpu_opset/convert_to_cpu_specific_opset.hpp @@ -54,6 +54,7 @@ inline void ConvertToCPUSpecificOpset(std::shared_ptr &nGraphFunc, in false, false); CPU_REGISTER_PASS_COMMON(manager, ov::pass::Validate); + CPU_REGISTER_PASS_COMMON(manager, ov::pass::EliminateConvert); // Need to clean up after the ConvertPrecision. manager.run_passes(nGraphFunc); } diff --git a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp index 5cad866e115..6a2ee84db0d 100644 --- a/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp +++ b/src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp @@ -405,9 +405,12 @@ void Transformations::PreLpt(const std::vector& defaultPrecis // Common ConvertPrecision pass handles only a limited set of opevino operations to match the list of precisions supported by the plugin. // However, if the extension operation produces an output precision that is not natively supported, this may lead to inconsistency during // element type propagation. This transformation is called before the ConvertPrecision pass to align the actual precisions with the list of supported ones. - CPU_REGISTER_PASS_COMMON(manager, ov::pass::InsertConvertAfterExtension); + constexpr bool convert_input_output_precision = false; + CPU_REGISTER_PASS_COMMON(manager, ov::pass::InsertConvertAfterExtension, convert_input_output_precision); + // Do not insert pass::Validate between pass::InsertConvertAfterExtension and pass::ConvertPrecision. + // This may result in the loss of the original Element type of the Output . // element type convert is disabled. - CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConvertPrecision, precisions, type_to_fuse, false, false); + CPU_REGISTER_PASS_COMMON(manager, ov::pass::ConvertPrecision, precisions, type_to_fuse, false, convert_input_output_precision); CPU_REGISTER_PASS_COMMON(manager, ov::pass::EliminateConvert); CPU_REGISTER_PASS_COMMON(manager, SwapConvertTranspose); diff --git a/src/plugins/intel_cpu/tests/functional/custom/subgraph_tests/src/custom_op_insert_convert_i64.cpp b/src/plugins/intel_cpu/tests/functional/custom/subgraph_tests/src/custom_op_insert_convert_i64.cpp index d5a98d73832..6b535279af0 100644 --- a/src/plugins/intel_cpu/tests/functional/custom/subgraph_tests/src/custom_op_insert_convert_i64.cpp +++ b/src/plugins/intel_cpu/tests/functional/custom/subgraph_tests/src/custom_op_insert_convert_i64.cpp @@ -2,46 +2,71 @@ // SPDX-License-Identifier: Apache-2.0 // -#include -#include -#include +#include "common_test_utils/ov_tensor_utils.hpp" +#include "openvino/op/op.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" #include "utils/cpu_test_utils.hpp" +// --------------------- --------------------- +// | PARAMETER | | PARAMETER | +// --------------------- --------------------- +// | | | | +// ------------- ---------- ------------- ---------- +// | EXT_I64 | | CPU_OP | | EXT_I64 | | CPU_OP | +// ------------- ---------- -------\ ------------- ---------- +// |i64 |i32 | \ |i64 |i32 | +// -------- --------- ------------- / -------------- --------- ------------- +// | CPU_OP | | RES_I32 | | EXT_I64 | -------/ | CVT I64->I32 | | RES_I32 | | EXT_I64 | +// -------- --------- ------------- -------------- --------- ------------- +// |i64 |i64 |i32 |i32 |i64 |i32 +// --------- --------- --------- -------- --------- --------- +// | RES_I64 | | RES_I64 | | RES_I32 | | CPU_OP | | RES_I64 | | RES_I32 | +// --------- --------- --------- -------- --------- --------- +// |i32 +// -------------- +// | CVT I32->I64 | +// -------------- +// |i64 +// --------- +// | RES_I64 | +// --------- + using namespace ov::test; using namespace CPUTestUtils; namespace ov { namespace test { -using CustomOpI64CPUTestParams = std::tuple; +using CustomOpI64CPUTestParams = std::tuple< + ElementType, // Input element type + InputShape // Input shape +>; class CustomOpI64 : public ov::op::Op { public: OPENVINO_OP("CustomOpI64"); CustomOpI64() = default; - CustomOpI64(const ov::OutputVector& args) : Op(args) { + + CustomOpI64(const Output& arg) : Op({arg}) { constructor_validate_and_infer_types(); } void validate_and_infer_types() override { const auto& inputs_count = input_values().size(); - OPENVINO_ASSERT(inputs_count == 1, - "Input count must be 1, Got: ", - inputs_count); - OPENVINO_ASSERT(get_input_element_type(0) == ov::element::Type_t::i32, - "The input must be i32."); + OPENVINO_ASSERT(inputs_count == 1, "Input count must be 1, Got: ", inputs_count); + OPENVINO_ASSERT(get_input_element_type(0) == element::i32, "The input must be i32."); set_output_size(2); - auto inShape = get_input_partial_shape(0); + auto in_shape = get_input_partial_shape(0); - set_output_type(0, ov::element::Type_t::i64, inShape); - set_output_type(1, ov::element::Type_t::i32, inShape); + set_output_type(0, element::i64, in_shape); + set_output_type(1, element::i32, in_shape); } std::shared_ptr clone_with_new_inputs(const ov::OutputVector& new_args) const override { OPENVINO_ASSERT(new_args.size() == 1, "Incorrect number of new arguments"); - return std::make_shared(new_args); + return std::make_shared(new_args[0]); } bool visit_attributes(ov::AttributeVisitor& visitor) override { @@ -81,42 +106,44 @@ class CustomOpConvertI64CPUTest : public testing::WithParamInterface& obj) { - ElementType inType; - InputShape inputShape; - std::tie(inType, inputShape) = obj.param; - std::ostringstream result; - result << "IS=" << inputShape << "_"; - result << "Prc=" << inType; + + result << "InElType=" << std::get<0>(obj.param) << "_"; + result << "IS=" << std::get<1>(obj.param); + return result.str(); } protected: void SetUp() override { - targetDevice = ov::test::utils::DEVICE_CPU; + targetDevice = test::utils::DEVICE_CPU; - ElementType inType; - InputShape inputShape; - std::tie(inType, inputShape) = this->GetParam(); + const auto& params = this->GetParam(); + const auto& in_el_type = std::get<0>(params); + const auto& in_shape = std::get<1>(params); - init_input_shapes({inputShape}); - ov::ParameterVector inputParams; - ov::OutputVector paramsOuts; - for (auto&& shape : inputDynamicShapes) { - auto param = std::make_shared(inType, shape); - inputParams.push_back(param); - paramsOuts.push_back(param); - } - auto customOp = std::make_shared(paramsOuts); + init_input_shapes({in_shape}); + auto param = std::make_shared(in_el_type, inputDynamicShapes[0]); - ov::ResultVector results{std::make_shared(customOp)}; - function = std::make_shared(results, inputParams, "customOpTest"); + auto custom_op_0 = std::make_shared(param); + auto i32_op_0 = std::make_shared(custom_op_0->output(0)); + auto i32_op_1 = std::make_shared(param); + auto custom_op_1 = std::make_shared(i32_op_1); + + OutputVector results{ + i32_op_0->output(0), + custom_op_0->output(1), + custom_op_1->output(0), + custom_op_1->output(1) + }; + + function = std::make_shared(results, ParameterVector{param}, "customOpI64Test"); } void generate_inputs(const std::vector& targetInputStaticShapes) override { inputs.clear(); const auto& funcInputs = function->inputs(); - for (size_t i = 0; i < funcInputs.size(); ++i) { + for (size_t i = 0lu; i < funcInputs.size(); ++i) { const auto& funcInput = funcInputs[i]; auto tensor = ov::test::utils::create_and_fill_tensor(funcInput.get_element_type(), targetInputStaticShapes[i]); inputs.insert({funcInput.get_node_shared_ptr(), tensor}); @@ -138,8 +165,41 @@ protected: TEST_P(CustomOpConvertI64CPUTest, CompareWithRefs) { run(); - // TODO: Graph could not be dumped with int64 for now. Swith on this in scope of int64 enabling. - // CPUTestUtils::CheckNumberOfNodesWithType(compiledModel, "Convert", 1); + + size_t cvt_i64_i32_num = 0lu; + size_t cvt_i32_i64_num = 0lu; + size_t res_i64_num = 0lu; + size_t res_i32_num = 0lu; + + for (const auto& node : compiledModel.get_runtime_model()->get_ops()) { + auto rt_info = node->get_rt_info(); + auto it = rt_info.find(exec_model_info::LAYER_TYPE); + ASSERT_NE(rt_info.end(), it); + + if (it->second.as() == "Convert") { + if (node->get_output_element_type(0) == element::i64) { + cvt_i32_i64_num++; + } else if (node->get_output_element_type(0) == element::i32) { + cvt_i64_i32_num++; + } else { + FAIL() << "Unexpected convertion type: " << node->get_output_element_type(0); + } + } + if (it->second.as() == "Output") { + if (node->get_output_element_type(0) == element::i64) { + res_i64_num++; + } else if (node->get_output_element_type(0) == element::i32) { + res_i32_num++; + } else { + FAIL() << "Unexpected Result type: " << node->get_output_element_type(0); + } + } + } + + ASSERT_EQ(cvt_i32_i64_num, 1lu) << "Unexpected number of the Convert i32->i64 nodes."; + ASSERT_EQ(cvt_i64_i32_num, 1lu) << "Unexpected number of the Convert i64->i32 nodes."; + ASSERT_EQ(res_i64_num, 2lu) << "Unexpected number of the Result nodes with type i64."; + ASSERT_EQ(res_i32_num, 2lu) << "Unexpected number of the Result nodes with type i32."; } const InputShape inputShapes = { @@ -148,8 +208,9 @@ const InputShape inputShapes = { INSTANTIATE_TEST_SUITE_P(smoke_CustomOp, CustomOpConvertI64CPUTest, - ::testing::Combine(::testing::Values(ElementType::i32), ::testing::Values(inputShapes)), + ::testing::Combine( + ::testing::Values(ElementType::i32), + ::testing::Values(inputShapes)), CustomOpConvertI64CPUTest::getTestCaseName); - } // namespace test } // namespace ov