From 6f6e4093b97766db0e1d197ef633e219ed5c48a4 Mon Sep 17 00:00:00 2001 From: Oleg Pipikin Date: Tue, 21 Mar 2023 08:00:05 +0100 Subject: [PATCH] Fix ir10 inputs outputs serialization order (#16357) (#16401) * Change IRv10 serialisation to not reorder parameters and results * Add tests * Fix1 --- src/core/src/pass/serialize.cpp | 9 +- .../pass/serialization/deterministicity.cpp | 205 +++++++++++++++++- 2 files changed, 201 insertions(+), 13 deletions(-) diff --git a/src/core/src/pass/serialize.cpp b/src/core/src/pass/serialize.cpp index 52000869df0..2896370e8b9 100644 --- a/src/core/src/pass/serialize.cpp +++ b/src/core/src/pass/serialize.cpp @@ -255,11 +255,6 @@ class XmlSerializer : public ngraph::AttributeVisitor { } } - if (ir_version < 11) { - // ops for serialized body function are provided in reversed order - std::reverse(output.begin(), output.end()); - } - return output; } @@ -804,7 +799,9 @@ void ngfunction_2_ir(pugi::xml_node& netXml, const bool exec_graph = is_exec_graph(f); auto sorted_ops = f.get_ordered_ops(); - if (version >= 11) { + // get_ordered_ops() returns operations after a topological sort. The topological sort reverses order of Parameters + // and Results. So we need to put them into sorted_ops separately to ensure correct order of inputs and outputs. + { std::vector> result; result.reserve(sorted_ops.size()); for (const auto& param : f.get_parameters()) { diff --git a/src/core/tests/pass/serialization/deterministicity.cpp b/src/core/tests/pass/serialization/deterministicity.cpp index c2a56d51722..d64da22ca2e 100644 --- a/src/core/tests/pass/serialization/deterministicity.cpp +++ b/src/core/tests/pass/serialization/deterministicity.cpp @@ -5,22 +5,47 @@ #include #include +#include #include "common_test_utils/file_utils.hpp" +#include "openvino/opsets/opset1.hpp" #include "openvino/pass/serialize.hpp" #include "openvino/util/file_util.hpp" #include "read_ir.hpp" #include "util/test_common.hpp" -class SerializationDeterministicityTest : public ov::test::TestsCommon { +class DeterministicityCommon { protected: - std::string test_name = GetTestName(); - std::string m_out_xml_path_1 = test_name + "1" + ".xml"; - std::string m_out_bin_path_1 = test_name + "1" + ".bin"; - std::string m_out_xml_path_2 = test_name + "2" + ".xml"; - std::string m_out_bin_path_2 = test_name + "2" + ".bin"; + std::string generateTestFilePrefix() { + // Generate unique file names based on test name, thread id and timestamp + // This allows execution of tests in parallel (stress mode) + auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); + std::string testName = testInfo->test_case_name(); + testName += testInfo->name(); + testName = std::to_string(std::hash()(testName)); + std::stringstream ss; + auto ts = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()); + ss << testName << "_" << std::this_thread::get_id() << "_" << ts.count(); + testName = ss.str(); + return testName; + } - void TearDown() override { + std::string m_out_xml_path_1{}; + std::string m_out_bin_path_1{}; + std::string m_out_xml_path_2{}; + std::string m_out_bin_path_2{}; + std::string filePrefix{}; + + void SetupFileNames() { + filePrefix = generateTestFilePrefix(); + m_out_xml_path_1 = filePrefix + "1" + ".xml"; + m_out_bin_path_1 = filePrefix + "1" + ".bin"; + m_out_xml_path_2 = filePrefix + "2" + ".xml"; + m_out_bin_path_2 = filePrefix + "2" + ".bin"; + } + + void RemoveFiles() { std::remove(m_out_xml_path_1.c_str()); std::remove(m_out_xml_path_2.c_str()); std::remove(m_out_bin_path_1.c_str()); @@ -47,6 +72,17 @@ protected: } }; +class SerializationDeterministicityTest : public ov::test::TestsCommon, public DeterministicityCommon { +protected: + void SetUp() override { + SetupFileNames(); + } + + void TearDown() override { + RemoveFiles(); + } +}; + #ifdef ENABLE_OV_ONNX_FRONTEND TEST_F(SerializationDeterministicityTest, BasicModel) { @@ -122,3 +158,158 @@ TEST_F(SerializationDeterministicityTest, ModelWithConstants) { ASSERT_TRUE(files_equal(xml_1, xml_2)); ASSERT_TRUE(files_equal(bin_1, bin_2)); } + +class SerializationDeterministicityInputOutputTest : public testing::TestWithParam, + public DeterministicityCommon { +protected: + std::string input0Name{"input0"}; + std::string input1Name{"input1"}; + std::string output0Name{"output0"}; + std::string output1Name{"output1"}; + + std::string xmlFileName{}; + + void SetupFileNames() { + DeterministicityCommon::SetupFileNames(); + xmlFileName = filePrefix + "_TestModel.xml"; + } + + void RemoveFiles() { + DeterministicityCommon::RemoveFiles(); + std::remove(xmlFileName.c_str()); + } + + void SetUp() override { + SetupFileNames(); + } + + void TearDown() override { + RemoveFiles(); + } +}; + +TEST_P(SerializationDeterministicityInputOutputTest, FromOvModel) { + auto irVersion = GetParam(); + + std::shared_ptr modelRef; + { + auto parameter0 = std::make_shared(ov::element::f32, ov::Shape{1, 3, 22, 22}); + parameter0->set_friendly_name("input0"); + auto result0 = std::make_shared(parameter0); + result0->set_friendly_name("output0"); + auto parameter1 = std::make_shared(ov::element::f32, ov::Shape{1, 3, 22, 22}); + parameter1->set_friendly_name("input1"); + auto result1 = std::make_shared(parameter1); + result1->set_friendly_name("output1"); + modelRef = + std::make_shared(ov::NodeVector{result0, result1}, ov::ParameterVector{parameter0, parameter1}); + } + + auto& expected1 = modelRef; + ov::pass::Serialize(m_out_xml_path_1, m_out_bin_path_1, irVersion).run_on_model(modelRef); + auto expected2 = ov::test::readModel(m_out_xml_path_1, m_out_bin_path_1); + ov::pass::Serialize(m_out_xml_path_2, m_out_bin_path_2, irVersion).run_on_model(expected2); + + EXPECT_EQ(input0Name, expected1->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected1->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected1->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected1->output(1).get_node()->get_friendly_name()); + EXPECT_EQ(input0Name, expected2->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected2->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected2->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected2->output(1).get_node()->get_friendly_name()); + + std::ifstream xml_1(m_out_xml_path_1, std::ios::in | std::ios::binary); + std::ifstream xml_2(m_out_xml_path_2, std::ios::in | std::ios::binary); + EXPECT_TRUE(files_equal(xml_1, xml_2)); +} + +TEST_P(SerializationDeterministicityInputOutputTest, FromIrModel) { + auto irVersion = GetParam(); + + std::string irModel_1stPart = R"V0G0N( + + + + + + + 1 + 3 + 22 + 22 + + + + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + + + 1 + 3 + 22 + 22 + + + + + + + + + + +)V0G0N"; + std::string strVersion = irVersion == ov::pass::Serialize::Version::IR_V11 ? "11" : "10"; + std::string irModel = irModel_1stPart + strVersion + irModel_2ndPart; + + { + std::ofstream xmlFile; + xmlFile.open(xmlFileName); + xmlFile << irModel; + xmlFile.close(); + } + + auto expected1 = ov::test::readModel(xmlFileName, ""); + ov::pass::Serialize(m_out_xml_path_1, "", irVersion).run_on_model(expected1); + auto expected2 = ov::test::readModel(m_out_xml_path_1, ""); + ov::pass::Serialize(m_out_xml_path_2, "", irVersion).run_on_model(expected2); + + EXPECT_EQ(input0Name, expected1->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected1->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected1->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected1->output(1).get_node()->get_friendly_name()); + EXPECT_EQ(input0Name, expected2->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected2->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected2->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected2->output(1).get_node()->get_friendly_name()); + + std::ifstream xml_1(m_out_xml_path_1, std::ios::in | std::ios::binary); + std::ifstream xml_2(m_out_xml_path_2, std::ios::in | std::ios::binary); + EXPECT_TRUE(files_equal(xml_2, xml_1)); +} + +INSTANTIATE_TEST_CASE_P(DeterministicityInputOutput, + SerializationDeterministicityInputOutputTest, + ::testing::Values(ov::pass::Serialize::Version::IR_V10, ov::pass::Serialize::Version::IR_V11)); \ No newline at end of file