IVGCVSW-4417 'Serialise ArmNN Model on android-nn-driver'

* Implemented serialization of the network on android-nn-driver

!armnn:4850

Signed-off-by: Sadik Armagan <sadik.armagan@arm.com>
Change-Id: I3caf07bd4d1d2a3068c58f0b344303c4cf977ca6
diff --git a/1.2/ArmnnDriverImpl.cpp b/1.2/ArmnnDriverImpl.cpp
index d5539bc..2ef51db 100644
--- a/1.2/ArmnnDriverImpl.cpp
+++ b/1.2/ArmnnDriverImpl.cpp
@@ -125,6 +125,11 @@
         return V1_0::ErrorStatus::NONE;
     }
 
+    // Serialize the network graph to a .armnn file if an output directory
+    // has been specified in the drivers' arguments.
+    auto serializedNetworkFileName =
+        SerializeNetwork(*modelConverter.GetINetwork(), options.GetRequestInputsAndOutputsDumpDir());
+
     // Optimize the network
     armnn::IOptimizedNetworkPtr optNet(nullptr, nullptr);
     armnn::OptimizerOptions OptOptions;
@@ -195,11 +200,12 @@
         return V1_0::ErrorStatus::NONE;
     }
 
-    // Now that we have a networkId for the graph rename the dump file to use it
-    // so that we can associate the graph file and the input/output tensor dump files
-    RenameGraphDotFile(dotGraphFileName,
-                       options.GetRequestInputsAndOutputsDumpDir(),
-                       netId);
+    // Now that we have a networkId for the graph rename the exported files to use it
+    // so that we can associate the graph file and the input/output tensor exported files
+    RenameExportedFiles(serializedNetworkFileName,
+                        dotGraphFileName,
+                        options.GetRequestInputsAndOutputsDumpDir(),
+                        netId);
 
     std::unique_ptr<ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>> preparedModel(
             new ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>(
diff --git a/1.3/ArmnnDriverImpl.cpp b/1.3/ArmnnDriverImpl.cpp
index 595df0a..c9f0340 100644
--- a/1.3/ArmnnDriverImpl.cpp
+++ b/1.3/ArmnnDriverImpl.cpp
@@ -136,6 +136,11 @@
         return V1_3::ErrorStatus::NONE;
     }
 
+    // Serialize the network graph to a .armnn file if an output directory
+    // has been specified in the drivers' arguments.
+    auto serializedNetworkFileName =
+        SerializeNetwork(*modelConverter.GetINetwork(), options.GetRequestInputsAndOutputsDumpDir());
+
     // Optimize the network
     armnn::IOptimizedNetworkPtr optNet(nullptr, nullptr);
     armnn::OptimizerOptions OptOptions;
@@ -206,11 +211,12 @@
         return V1_3::ErrorStatus::NONE;
     }
 
-    // Now that we have a networkId for the graph rename the dump file to use it
-    // so that we can associate the graph file and the input/output tensor dump files
-    RenameGraphDotFile(dotGraphFileName,
-                       options.GetRequestInputsAndOutputsDumpDir(),
-                       netId);
+    // Now that we have a networkId for the graph rename the exported files to use it
+    // so that we can associate the graph file and the input/output tensor exported files
+    RenameExportedFiles(serializedNetworkFileName,
+                        dotGraphFileName,
+                        options.GetRequestInputsAndOutputsDumpDir(),
+                        netId);
 
     std::unique_ptr<ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>> preparedModel(
             new ArmnnPreparedModel_1_3<hal_1_3::HalPolicy>(
diff --git a/ArmnnDriverImpl.cpp b/ArmnnDriverImpl.cpp
index ef37cae..917370c 100644
--- a/ArmnnDriverImpl.cpp
+++ b/ArmnnDriverImpl.cpp
@@ -100,6 +100,11 @@
         return V1_0::ErrorStatus::NONE;
     }
 
+    // Serialize the network graph to a .armnn file if an output directory
+    // has been specified in the drivers' arguments.
+    auto serializedNetworkFileName =
+        SerializeNetwork(*modelConverter.GetINetwork(), options.GetRequestInputsAndOutputsDumpDir());
+
     // Optimize the network
     armnn::IOptimizedNetworkPtr optNet(nullptr, nullptr);
     armnn::OptimizerOptions OptOptions;
@@ -169,11 +174,12 @@
         return V1_0::ErrorStatus::NONE;
     }
 
-    // Now that we have a networkId for the graph rename the dump file to use it
-    // so that we can associate the graph file and the input/output tensor dump files
-    RenameGraphDotFile(dotGraphFileName,
-                       options.GetRequestInputsAndOutputsDumpDir(),
-                       netId);
+    // Now that we have a networkId for the graph rename the exported files to use it
+    // so that we can associate the graph file and the input/output tensor exported files
+    RenameExportedFiles(serializedNetworkFileName,
+                        dotGraphFileName,
+                        options.GetRequestInputsAndOutputsDumpDir(),
+                        netId);
 
     sp<ArmnnPreparedModel<HalPolicy>> preparedModel(
             new ArmnnPreparedModel<HalPolicy>(
diff --git a/ModelToINetworkConverter.cpp b/ModelToINetworkConverter.cpp
index e8cf8a8..c205a57 100644
--- a/ModelToINetworkConverter.cpp
+++ b/ModelToINetworkConverter.cpp
@@ -87,8 +87,9 @@
             const HalOperand& operand = getMainModel(m_Model).operands[inputIndex];
             ALOGV("ModelToINetworkConverter::Convert(): GetTensorInfoForOperand(operand)");
             const armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand);
-            ALOGV("ModelToINetworkConverter::Convert(): m_Data.m_Network->AddInputLayer(i)");
-            armnn::IConnectableLayer* layer = m_Data.m_Network->AddInputLayer(i);
+            const std::string layerName = "Input_" + std::to_string(i);
+            ALOGV("ModelToINetworkConverter::Convert(): m_Data.m_Network->AddInputLayer(i, layerName.c_str())");
+            armnn::IConnectableLayer* layer = m_Data.m_Network->AddInputLayer(i, layerName.c_str());
 
             ALOGV("ModelToINetworkConverter::Convert(): layer->GetOutputSlot(0)");
             armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
@@ -184,7 +185,8 @@
                 uint32_t outputIndex = getMainModel(m_Model).outputIndexes[i];
                 const HalOperand& operand = getMainModel(m_Model).operands[outputIndex];
                 const armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand);
-                armnn::IConnectableLayer* layer = m_Data.m_Network->AddOutputLayer(i);
+                const std::string layerName = "Output_" + std::to_string(i);
+                armnn::IConnectableLayer* layer = m_Data.m_Network->AddOutputLayer(i, layerName.c_str());
 
                 assert(m_Data.m_OutputSlotForOperand[outputIndex]);
                 m_Data.m_OutputSlotForOperand[outputIndex]->Connect(layer->GetInputSlot(0));
diff --git a/Utils.cpp b/Utils.cpp
index 895278a..53877ba 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -8,6 +8,7 @@
 #include "Utils.hpp"
 #include "Half.hpp"
 
+#include <armnnSerializer/ISerializer.hpp>
 #include <armnnUtils/Permute.hpp>
 
 #include <armnn/Utils.hpp>
@@ -22,8 +23,6 @@
 #include <cstdio>
 #include <time.h>
 
-
-
 using namespace android;
 using namespace android::hardware;
 using namespace android::hidl::memory::V1_0;
@@ -572,6 +571,41 @@
     return fileName;
 }
 
+std::string SerializeNetwork(const armnn::INetwork& network, const std::string& dumpDir)
+{
+    std::string fileName;
+    // The dump directory must exist in advance.
+    if (dumpDir.empty())
+    {
+        return fileName;
+    }
+
+    std::string timestamp = GetFileTimestamp();
+    if (timestamp.empty())
+    {
+        return fileName;
+    }
+
+    auto serializer(armnnSerializer::ISerializer::Create());
+
+    // Serialize the Network
+    serializer->Serialize(network);
+
+    // Set the name of the output .armnn file.
+    fs::path dumpPath = dumpDir;
+    fs::path tempFilePath = dumpPath / (timestamp + "_network.armnn");
+    fileName = tempFilePath.string();
+
+    // Save serialized network to a file
+    std::ofstream serializedFile(fileName, std::ios::out | std::ios::binary);
+    bool serialized = serializer->SaveSerializedToStream(serializedFile);
+    if (!serialized)
+    {
+        ALOGW("An error occurred when serializing to file %s", fileName.c_str());
+    }
+    return fileName;
+}
+
 bool IsDynamicTensor(const armnn::TensorInfo& tensorInfo)
 {
     if (tensorInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified)
@@ -613,25 +647,37 @@
     return ss.str();
 }
 
-void RenameGraphDotFile(const std::string& oldName, const std::string& dumpDir, const armnn::NetworkId networkId)
+void RenameExportedFiles(const std::string& existingSerializedFileName,
+                         const std::string& existingDotFileName,
+                         const std::string& dumpDir,
+                         const armnn::NetworkId networkId)
 {
     if (dumpDir.empty())
     {
         return;
     }
-    if (oldName.empty())
+    RenameFile(existingSerializedFileName, std::string("_network.armnn"), dumpDir, networkId);
+    RenameFile(existingDotFileName, std::string("_networkgraph.dot"), dumpDir, networkId);
+}
+
+void RenameFile(const std::string& existingName,
+                const std::string& extension,
+                const std::string& dumpDir,
+                const armnn::NetworkId networkId)
+{
+    if (existingName.empty() || dumpDir.empty())
     {
         return;
     }
-    fs::path dumpPath = dumpDir;
-    const fs::path newFileName = dumpPath / (std::to_string(networkId) + "_networkgraph.dot");
 
-    int iRet = rename(oldName.c_str(), newFileName.c_str());
+    fs::path dumpPath = dumpDir;
+    const fs::path newFileName = dumpPath / (std::to_string(networkId) + extension);
+    int iRet = rename(existingName.c_str(), newFileName.c_str());
     if (iRet != 0)
     {
         std::stringstream ss;
-        ss << "rename of [" << oldName << "] to [" << newFileName << "] failed with errno " << std::to_string(errno)
-           << " : " << std::strerror(errno);
+        ss << "rename of [" << existingName << "] to [" << newFileName << "] failed with errno "
+           << std::to_string(errno) << " : " << std::strerror(errno);
         ALOGW(ss.str().c_str());
     }
 }
diff --git a/Utils.hpp b/Utils.hpp
index e3b7d82..c4d89f7 100644
--- a/Utils.hpp
+++ b/Utils.hpp
@@ -135,7 +135,17 @@
 std::string ExportNetworkGraphToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork,
                                         const std::string& dumpDir);
 
-void RenameGraphDotFile(const std::string& oldName, const std::string& dumpDir, const armnn::NetworkId networkId);
+std::string SerializeNetwork(const armnn::INetwork& network, const std::string& dumpDir);
+
+void RenameExportedFiles(const std::string& existingSerializedFileName,
+                         const std::string& existingDotFileName,
+                         const std::string& dumpDir,
+                         const armnn::NetworkId networkId);
+
+void RenameFile(const std::string& existingName,
+                const std::string& extension,
+                const std::string& dumpDir,
+                const armnn::NetworkId networkId);
 
 /// Checks if a tensor info represents a dynamic tensor
 bool IsDynamicTensor(const armnn::TensorInfo& outputInfo);