1041 lines
48 KiB
Diff
1041 lines
48 KiB
Diff
From 3c20ee014b27db29c3457b8b2d48821f30b11752 Mon Sep 17 00:00:00 2001
|
|
From: liufeiyang <liufeiyang6@huawei.com>
|
|
Date: Fri, 21 Jun 2024 11:42:59 +0800
|
|
Subject: [PATCH] Add batch inference feature and optimizer model.
|
|
|
|
---
|
|
aiframe/CMakeLists.txt | 8 +-
|
|
aiframe/ONNXRunner.cpp | 353 ++++++++++++++++++++++-------------
|
|
aiframe/include/ONNXRunner.h | 244 +++++++++++++++++++-----
|
|
models/optimizer.onnx | Bin 0 -> 41037 bytes
|
|
4 files changed, 426 insertions(+), 179 deletions(-)
|
|
create mode 100644 models/optimizer.onnx
|
|
|
|
diff --git a/aiframe/CMakeLists.txt b/aiframe/CMakeLists.txt
|
|
index 8ece1ce6..9f8022f5 100644
|
|
--- a/aiframe/CMakeLists.txt
|
|
+++ b/aiframe/CMakeLists.txt
|
|
@@ -4,8 +4,6 @@ project(ONNXRunner)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
|
|
-#set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")
|
|
-
|
|
set(INC_DIR /usr/include)
|
|
set(INC_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
set(LIB_DIR /usr/lib64)
|
|
@@ -18,6 +16,6 @@ link_directories(${LIB_DIR})
|
|
|
|
add_library(ONNXRunner SHARED ONNXRunner.cpp)
|
|
|
|
-#target_link_libraries(ONNXRunner
|
|
-#PRIVATE
|
|
-#libonnxruntime.so)
|
|
+target_link_libraries(ONNXRunner
|
|
+PRIVATE
|
|
+libcrypto.so) # libonnxruntime.so
|
|
diff --git a/aiframe/ONNXRunner.cpp b/aiframe/ONNXRunner.cpp
|
|
index 3ec159c7..3aefae33 100644
|
|
--- a/aiframe/ONNXRunner.cpp
|
|
+++ b/aiframe/ONNXRunner.cpp
|
|
@@ -1,151 +1,244 @@
|
|
-#include <iostream>
|
|
-#include <vector>
|
|
+#include "include/ONNXRunner.h"
|
|
#include <algorithm>
|
|
+#include <iostream>
|
|
#include <numeric>
|
|
-#include "include/ONNXRunner.h"
|
|
+#include <vector>
|
|
|
|
-namespace boltONNXRunner{
|
|
+namespace compilerONNXRunner {
|
|
|
|
-Ort::Value ONNXRunner::getInputValueFloat(Ort::Session *session,
|
|
+Ort::Value ONNXRunner::getInputValueFloat(Ort::Session *session,
|
|
std::vector<float> &input,
|
|
- int inputIdx) {
|
|
- auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
- auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
- auto inputDims = tensorInfo.GetShape();
|
|
- std::replace_if(
|
|
- inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
-
|
|
- size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
- 1, std::multiplies<int>());
|
|
- auto memory_info =
|
|
- Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
- auto inputTmp = Ort::Value::CreateTensor<float>(
|
|
- memory_info, input.data(), inputTensorSize, inputDims.data(),
|
|
- inputDims.size());
|
|
- auto inputTensor = &inputTmp;
|
|
- return inputTmp;
|
|
+ int inputIdx, int batchSize) {
|
|
+ auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
+ auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
+ auto inputDims = tensorInfo.GetShape();
|
|
+ std::replace_if(
|
|
+ inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
+
|
|
+ size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
+ 1, std::multiplies<int>());
|
|
+ // try to add batch size
|
|
+ inputDims[0] = batchSize;
|
|
+ inputTensorSize = inputTensorSize * batchSize;
|
|
+ auto memoryInfo =
|
|
+ Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
+ auto inputTmp =
|
|
+ Ort::Value::CreateTensor<float>(memoryInfo, input.data(), inputTensorSize,
|
|
+ inputDims.data(), inputDims.size());
|
|
+ auto inputTensor = &inputTmp;
|
|
+ return inputTmp;
|
|
}
|
|
|
|
-Ort::Value ONNXRunner::getInputValueString(Ort::AllocatorWithDefaultOptions allocator,
|
|
- Ort::Session *session,
|
|
- std::vector<std::string> &input,
|
|
- int inputIdx) {
|
|
- auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
- auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
- auto inputDims = tensorInfo.GetShape();
|
|
-
|
|
- std::replace_if(
|
|
- inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
-
|
|
- size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
- 1, std::multiplies<int>());
|
|
- const char* input_strings[inputTensorSize];
|
|
- for(int i = 0; i < inputTensorSize; i++) {
|
|
- input_strings[i] = input[i].c_str();
|
|
- }
|
|
-
|
|
- auto memory_info =
|
|
- Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
- auto inputTmp = Ort::Value::CreateTensor(allocator, inputDims.data(),
|
|
- inputDims.size(),
|
|
- ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING);
|
|
- inputTmp.FillStringTensor(input_strings, inputTensorSize);
|
|
- auto inputTensor = &inputTmp;
|
|
- return inputTmp;
|
|
+Ort::Value ONNXRunner::getInputValueString(
|
|
+ Ort::AllocatorWithDefaultOptions allocator, Ort::Session *session,
|
|
+ std::vector<std::string> &input, int inputIdx, int batchSize) {
|
|
+ auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
+ auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
+ auto inputDims = tensorInfo.GetShape();
|
|
+
|
|
+ std::replace_if(
|
|
+ inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
+
|
|
+ size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
+ 1, std::multiplies<int>());
|
|
+ inputDims[0] = batchSize;
|
|
+ inputTensorSize = inputTensorSize * batchSize;
|
|
+ const char *inputStrings[inputTensorSize];
|
|
+ for (int i = 0; i < inputTensorSize; i++) {
|
|
+ inputStrings[i] = input[i].c_str();
|
|
+ }
|
|
+
|
|
+ auto memoryInfo =
|
|
+ Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
+ auto inputTmp =
|
|
+ Ort::Value::CreateTensor(allocator, inputDims.data(), inputDims.size(),
|
|
+ ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING);
|
|
+ inputTmp.FillStringTensor(inputStrings, inputTensorSize);
|
|
+ auto inputTensor = &inputTmp;
|
|
+ return inputTmp;
|
|
}
|
|
|
|
-Ort::Value ONNXRunner::getInputValueInt64(Ort::Session *session,
|
|
+Ort::Value ONNXRunner::getInputValueInt64(Ort::Session *session,
|
|
std::vector<int64_t> &input,
|
|
- int inputIdx) {
|
|
- auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
- auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
- auto inputDims = tensorInfo.GetShape();
|
|
- std::replace_if(
|
|
- inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
-
|
|
- size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
- 1, std::multiplies<int>());
|
|
- auto memory_info =
|
|
- Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
- auto inputTmp = Ort::Value::CreateTensor<int64_t>(
|
|
- memory_info, input.data(), inputTensorSize, inputDims.data(),
|
|
- inputDims.size());
|
|
- auto inputTensor = &inputTmp;
|
|
- return inputTmp;
|
|
+ int inputIdx, int batchSize) {
|
|
+ auto typeInfo = session->GetInputTypeInfo(inputIdx);
|
|
+ auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo();
|
|
+ auto inputDims = tensorInfo.GetShape();
|
|
+ std::replace_if(
|
|
+ inputDims.begin(), inputDims.end(), [](int64_t &i) { return i < 0; }, 1);
|
|
+
|
|
+ size_t inputTensorSize = std::accumulate(inputDims.begin(), inputDims.end(),
|
|
+ 1, std::multiplies<int>());
|
|
+ inputDims[0] = batchSize;
|
|
+ inputTensorSize = inputTensorSize * batchSize;
|
|
+ auto memoryInfo =
|
|
+ Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
|
|
+ auto inputTmp = Ort::Value::CreateTensor<int64_t>(
|
|
+ memoryInfo, input.data(), inputTensorSize, inputDims.data(),
|
|
+ inputDims.size());
|
|
+ auto inputTensor = &inputTmp;
|
|
+ return inputTmp;
|
|
}
|
|
|
|
-float ONNXRunner::runONNXModel(std::vector<std::string> input_string, std::vector<int64_t> input_int64, std::vector<float> input_float){
|
|
- Ort::AllocatorWithDefaultOptions allocator;
|
|
-
|
|
- //Try to get input;
|
|
- int input_count = session->GetInputCount();
|
|
-
|
|
- //Get input name
|
|
- std::vector<std::string> inputNameList;
|
|
- for (int i = 0; i < input_count; i++) {
|
|
- auto inputName = session->GetInputNameAllocated(i, allocator);
|
|
- auto inputNameStr = inputName.get();
|
|
- inputNameList.push_back(inputNameStr);
|
|
- }
|
|
-
|
|
- //Form input tensor(s)
|
|
- std::vector<Ort::Value> input_final;
|
|
- std::vector<const char *> inputNameStr_final;
|
|
-
|
|
- int currentIdx = 0;
|
|
- if(!input_string.empty()) {
|
|
- input_final.push_back(getInputValueString(allocator, session, input_string, currentIdx));
|
|
- currentIdx ++;
|
|
- }
|
|
-
|
|
- if(!input_int64.empty()) {
|
|
- input_final.push_back(getInputValueInt64(session, input_int64, currentIdx));
|
|
- currentIdx ++;
|
|
- }
|
|
-
|
|
- if(!input_float.empty()) {
|
|
- input_final.push_back(getInputValueFloat(session, input_float, currentIdx));
|
|
- currentIdx ++;
|
|
+std::vector<float>
|
|
+ONNXRunner::runONNXModel(std::vector<std::string> inputString,
|
|
+ std::vector<int64_t> inputInt64,
|
|
+ std::vector<float> inputFloat, int batchSize) {
|
|
+ Ort::AllocatorWithDefaultOptions allocator;
|
|
+
|
|
+ // Get input count
|
|
+ int inputCount = session->GetInputCount();
|
|
+
|
|
+ // Get input name
|
|
+ std::vector<std::string> inputNameList;
|
|
+ for (int i = 0; i < inputCount; i++) {
|
|
+ auto inputName = session->GetInputNameAllocated(i, allocator);
|
|
+ auto inputNameStr = inputName.get();
|
|
+ inputNameList.push_back(inputNameStr);
|
|
+ }
|
|
+
|
|
+ // Form input tensor(s)
|
|
+ std::vector<Ort::Value> inputFinal;
|
|
+ std::vector<const char *> inputNameStrFinal;
|
|
+ int currentIdx = 0;
|
|
+ if (!inputString.empty()) {
|
|
+ inputFinal.push_back(getInputValueString(allocator, session, inputString,
|
|
+ currentIdx, batchSize));
|
|
+ currentIdx++;
|
|
+ }
|
|
+
|
|
+ if (!inputInt64.empty()) {
|
|
+ inputFinal.push_back(
|
|
+ getInputValueInt64(session, inputInt64, currentIdx, batchSize));
|
|
+ currentIdx++;
|
|
+ }
|
|
+
|
|
+ if (!inputFloat.empty()) {
|
|
+ inputFinal.push_back(
|
|
+ getInputValueFloat(session, inputFloat, currentIdx, batchSize));
|
|
+ currentIdx++;
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < inputCount; i++) {
|
|
+ inputNameStrFinal.push_back(inputNameList[i].c_str());
|
|
+ }
|
|
+
|
|
+ // Run model
|
|
+ int outputCount = session->GetOutputCount();
|
|
+ std::vector<std::string> outputNameList;
|
|
+ for (int i = 0; i < outputCount; i++) {
|
|
+ auto outputName = session->GetOutputNameAllocated(i, allocator);
|
|
+ std::string outputNameStr = outputName.get();
|
|
+ if (!outputNameStr.empty()) {
|
|
+ outputNameList.push_back(outputNameStr);
|
|
+ } else {
|
|
+ std::string outputNameDefault = "Output_" + std::to_string(i);
|
|
+ outputNameList.push_back(outputNameDefault);
|
|
}
|
|
+ }
|
|
+
|
|
+ std::vector<const char *> outputNameStrFinal;
|
|
+ for (int i = 0; i < outputCount; i++) {
|
|
+ outputNameStrFinal.push_back(outputNameList[i].c_str());
|
|
+ }
|
|
+
|
|
+ auto outputTensors = session->Run(
|
|
+ Ort::RunOptions{nullptr}, inputNameStrFinal.data(), inputFinal.data(),
|
|
+ inputCount, outputNameStrFinal.data(), outputCount);
|
|
+
|
|
+ // Get result and return
|
|
+ std::vector<float> probs;
|
|
+ float *outputProbability = outputTensors[0].GetTensorMutableData<float>();
|
|
+ for (int i = 0; i < batchSize; i++) {
|
|
+ Ort::Value mapOut =
|
|
+ outputTensors[1].GetValue(static_cast<int>(i), allocator);
|
|
+ Ort::Value keysOrt = mapOut.GetValue(0, allocator);
|
|
+ int64_t *keysRet = keysOrt.GetTensorMutableData<int64_t>();
|
|
+ Ort::Value valuesOrt = mapOut.GetValue(1, allocator);
|
|
+ float *valuesRet = valuesOrt.GetTensorMutableData<float>();
|
|
+ probs.push_back((*(valuesRet + 1)));
|
|
+ }
|
|
+
|
|
+ return probs;
|
|
+}
|
|
|
|
- for (int i = 0; i < input_count; i++) {
|
|
- inputNameStr_final.push_back(inputNameList[i].c_str());
|
|
+int64_t ONNXRunner::runONNXModelOptimizer(std::vector<std::string> inputString,
|
|
+ std::vector<int64_t> inputInt64,
|
|
+ std::vector<float> inputFloat,
|
|
+ int batchSize) {
|
|
+ Ort::AllocatorWithDefaultOptions allocator;
|
|
+
|
|
+ // Get input count
|
|
+ int inputCount = session->GetInputCount();
|
|
+ std::vector<int64_t> inputInt64Tensor(FEATURE_SIZE_INT64_OPT);
|
|
+ std::vector<std::string> inputStringTensor;
|
|
+ std::vector<Ort::Value> inputFinal;
|
|
+
|
|
+ inputInt64.clear();
|
|
+ inputInt64.resize(FEATURE_SIZE_INT64_OPT);
|
|
+ for (int i = 0; i < FEATURE_SIZE_INT64_OPT; i++) {
|
|
+ auto inputName = session->GetInputNameAllocated(i, allocator);
|
|
+ auto inputNameStr = inputName.get();
|
|
+
|
|
+ inputInt64Tensor.clear();
|
|
+ inputInt64Tensor.push_back(inputInt64[i]);
|
|
+ inputFinal.push_back(
|
|
+ getInputValueInt64(session, inputInt64Tensor, i, batchSize));
|
|
+ }
|
|
+
|
|
+ for (int i = FEATURE_SIZE_INT64_OPT;
|
|
+ i < FEATURE_SIZE_INT64_OPT + FEATURE_SIZE_STRING_OPT; i++) {
|
|
+ inputStringTensor.clear();
|
|
+ inputStringTensor.push_back(inputString[i - FEATURE_SIZE_INT64_OPT]);
|
|
+ inputFinal.push_back(getInputValueString(allocator, session,
|
|
+ inputStringTensor, i, batchSize));
|
|
+ }
|
|
+
|
|
+ // Get input name from model
|
|
+ std::vector<std::string> inputNameList;
|
|
+ for (int i = 0; i < inputCount; i++) {
|
|
+ auto inputName = session->GetInputNameAllocated(i, allocator);
|
|
+ auto inputNameStr = inputName.get();
|
|
+ inputNameList.push_back(inputNameStr);
|
|
+ }
|
|
+
|
|
+ // Form input tensor(s)
|
|
+ std::vector<const char *> inputNameStrFinal;
|
|
+ for (int i = 0; i < inputCount; i++) {
|
|
+ inputNameStrFinal.push_back(inputNameList[i].c_str());
|
|
+ }
|
|
+
|
|
+ // Run model
|
|
+ int outputCount = session->GetOutputCount();
|
|
+ std::vector<std::string> outputNameList;
|
|
+ for (int i = 0; i < outputCount; i++) {
|
|
+ auto outputName = session->GetOutputNameAllocated(i, allocator);
|
|
+ std::string outputNameStr = outputName.get();
|
|
+ if (!outputNameStr.empty()) {
|
|
+ outputNameList.push_back(outputNameStr);
|
|
+ } else {
|
|
+ std::string outputNameDefault = "Output_" + std::to_string(i);
|
|
+ outputNameList.push_back(outputNameDefault);
|
|
}
|
|
+ }
|
|
|
|
- //Run the model
|
|
- int output_count = session->GetOutputCount();
|
|
- std::vector<std::string> outputNameList;
|
|
- for (int i = 0; i < output_count; i++) {
|
|
- auto outputName = session->GetOutputNameAllocated(i, allocator);
|
|
- std::string outputNameStr = outputName.get();
|
|
- if(!outputNameStr.empty()) {
|
|
- outputNameList.push_back(outputNameStr);
|
|
- } else {
|
|
- std::string outputNameDefault = "Output_" + std::to_string(i);
|
|
- outputNameList.push_back(outputNameDefault);
|
|
- }
|
|
- }
|
|
+ std::vector<const char *> outputNameStrFinal;
|
|
+ for (int i = 0; i < outputCount; i++) {
|
|
+ outputNameStrFinal.push_back(outputNameList[i].c_str());
|
|
+ }
|
|
|
|
- std::vector<const char *> outputNameStr_final;
|
|
- for(int i = 0; i < output_count; i++) {
|
|
- outputNameStr_final.push_back(outputNameList[i].c_str());
|
|
- }
|
|
-
|
|
- auto outputTensors =
|
|
- session->Run(Ort::RunOptions{nullptr}, inputNameStr_final.data(),
|
|
- input_final.data(), input_count, outputNameStr_final.data(), output_count);
|
|
+ auto outputTensors = session->Run(
|
|
+ Ort::RunOptions{nullptr}, inputNameStrFinal.data(), inputFinal.data(),
|
|
+ inputCount, outputNameStrFinal.data(), outputCount);
|
|
|
|
- //Try to get the result & return
|
|
- float* output_probability = outputTensors[0].GetTensorMutableData<float>();
|
|
- Ort::Value map_out = outputTensors[1].GetValue(static_cast<int>(0), allocator);
|
|
+ // Get result and return
|
|
+ int64_t label = 0;
|
|
+ for (int i = 0; i < batchSize; i++) {
|
|
+ int64_t *outputLabel = outputTensors[0].GetTensorMutableData<int64_t>();
|
|
+ label = *outputLabel;
|
|
+ }
|
|
|
|
- Ort::Value keys_ort = map_out.GetValue(0, allocator);
|
|
- int64_t* keys_ret = keys_ort.GetTensorMutableData<int64_t>();
|
|
- Ort::Value values_ort = map_out.GetValue(1, allocator);
|
|
- float* values_ret = values_ort.GetTensorMutableData<float>();
|
|
-
|
|
- return *(values_ret + 1);
|
|
+ return label;
|
|
}
|
|
|
|
-} // namespace boltONNXRunner
|
|
-
|
|
+} // namespace compilerONNXRunner
|
|
diff --git a/aiframe/include/ONNXRunner.h b/aiframe/include/ONNXRunner.h
|
|
index 54bf341e..bc8535f1 100644
|
|
--- a/aiframe/include/ONNXRunner.h
|
|
+++ b/aiframe/include/ONNXRunner.h
|
|
@@ -1,64 +1,220 @@
|
|
-#ifndef BOLT_PROFILE_ONNXRUNNER_H
|
|
-#define BOLT_PROFILE_ONNXRUNNER_H
|
|
+#ifndef COMPILER_PROFILE_ONNXRUNNER_H
|
|
+#define COMPILER_PROFILE_ONNXRUNNER_H
|
|
|
|
-#include <iostream>
|
|
-#include <vector>
|
|
-#include <algorithm>
|
|
-#include <numeric>
|
|
#include "onnxruntime_c_api.h"
|
|
#include "onnxruntime_cxx_api.h"
|
|
+#include <algorithm>
|
|
+#include <fstream>
|
|
+#include <iomanip>
|
|
+#include <iostream>
|
|
+#include <numeric>
|
|
+#include <openssl/sha.h>
|
|
+#include <sstream>
|
|
+#include <string>
|
|
+#include <vector>
|
|
|
|
extern "C" {
|
|
-namespace boltONNXRunner {
|
|
+namespace compilerONNXRunner {
|
|
+
|
|
+const char* MODEL_PATH_OPT = "/usr/lib64/AI4C/optimizer.onnx";
|
|
+const int FEATURE_SIZE_INT64_OPT = 6;
|
|
+const int FEATURE_SIZE_STRING_OPT = 11;
|
|
+
|
|
class ONNXRunner {
|
|
public:
|
|
- explicit ONNXRunner() {}
|
|
- explicit ONNXRunner(const char* model_path) {
|
|
- // prepare model and env
|
|
- session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_BASIC);
|
|
- session = new Ort::Session(env, model_path, session_options);
|
|
- }
|
|
- ~ONNXRunner() {
|
|
- delete session;
|
|
- }
|
|
- float runONNXModel(std::vector<std::string> input_string, std::vector<int64_t> input_int64, std::vector<float> input_float);
|
|
+ explicit ONNXRunner() {}
|
|
+ explicit ONNXRunner(const char *modelPath) {
|
|
+ // Prepare model and env
|
|
+ sessionOptions.SetGraphOptimizationLevel(
|
|
+ GraphOptimizationLevel::ORT_ENABLE_BASIC);
|
|
+ session = new Ort::Session(env, modelPath, sessionOptions);
|
|
+ }
|
|
+ ~ONNXRunner() { delete session; }
|
|
+ std::vector<float> runONNXModel(std::vector<std::string> inputString,
|
|
+ std::vector<int64_t> inputInt64,
|
|
+ std::vector<float> inputFloat, int batchSize);
|
|
+ int64_t runONNXModelOptimizer(std::vector<std::string> inputString,
|
|
+ std::vector<int64_t> inputInt64,
|
|
+ std::vector<float> inputFloat, int batchSize);
|
|
|
|
private:
|
|
- static Ort::Value getInputValueFloat(Ort::Session *session,
|
|
- std::vector<float> &input,
|
|
- int inputIdx);
|
|
-
|
|
- static Ort::Value getInputValueString(Ort::AllocatorWithDefaultOptions allocator,
|
|
- Ort::Session *session,
|
|
- std::vector<std::string> &input,
|
|
- int inputIdx);
|
|
-
|
|
- static Ort::Value getInputValueInt64(Ort::Session *session,
|
|
- std::vector<int64_t> &input,
|
|
- int inputIdx);
|
|
-
|
|
- Ort::SessionOptions session_options;
|
|
- Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "test"};
|
|
- Ort::Session *session;
|
|
+ static Ort::Value getInputValueFloat(Ort::Session *session,
|
|
+ std::vector<float> &input, int inputIdx,
|
|
+ int batchSize);
|
|
+
|
|
+ static Ort::Value
|
|
+ getInputValueString(Ort::AllocatorWithDefaultOptions allocator,
|
|
+ Ort::Session *session, std::vector<std::string> &input,
|
|
+ int inputIdx, int batchSize);
|
|
+
|
|
+ static Ort::Value getInputValueInt64(Ort::Session *session,
|
|
+ std::vector<int64_t> &input,
|
|
+ int inputIdx, int batchSize);
|
|
+
|
|
+ Ort::SessionOptions sessionOptions;
|
|
+ Ort::Env env{ORT_LOGGING_LEVEL_WARNING, "test"};
|
|
+ Ort::Session *session;
|
|
};
|
|
|
|
-extern ONNXRunner* createONNXRunner(const char* model_path) {
|
|
- return new ONNXRunner(model_path);
|
|
+extern ONNXRunner *createONNXRunner(const char *modelPath) {
|
|
+ std::ifstream file(modelPath);
|
|
+ if (file.good()) {
|
|
+ return new ONNXRunner(modelPath);
|
|
+ } else {
|
|
+ return nullptr;
|
|
+ }
|
|
+}
|
|
+
|
|
+extern void deleteONNXRunner(ONNXRunner *instance) {
|
|
+ if (instance != nullptr) {
|
|
+ delete instance;
|
|
+ }
|
|
+}
|
|
+
|
|
+extern std::vector<float> runONNXModel(ONNXRunner *instance,
|
|
+ std::vector<std::string> inputString,
|
|
+ std::vector<int64_t> inputInt64,
|
|
+ std::vector<float> inputFloat,
|
|
+ int batchSize) {
|
|
+ std::vector<float> nullResult;
|
|
+ if (instance != nullptr) {
|
|
+ return instance->runONNXModel(inputString, inputInt64, inputFloat,
|
|
+ batchSize);
|
|
+ } else {
|
|
+ return nullResult;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool startsWithPatt(const std::string &str, const std::string &pattern) {
|
|
+ return str.rfind(pattern, 0) == 0;
|
|
}
|
|
|
|
-extern void deleteONNXRunner(ONNXRunner* instance) {
|
|
- if (instance != nullptr) {
|
|
- delete instance;
|
|
+static bool optionComparator(const std::string &str1, const std::string &str2) {
|
|
+ for (size_t i = 0; i < str1.size() && i < str2.size(); ++i) {
|
|
+ char c1 = str1[i];
|
|
+ char c2 = str2[i];
|
|
+ if (std::isupper(c1) && !std::isupper(c2)) {
|
|
+ return 1;
|
|
+ } else if (!std::isupper(c1) && std::isupper(c2)) {
|
|
+ return 0;
|
|
+ } else if (c1 != c2) {
|
|
+ return c1 > c2;
|
|
}
|
|
+ }
|
|
+
|
|
+ return str1.size() < str2.size();
|
|
+}
|
|
+
|
|
+static void truncatePrefix(const std::string &str, std::string &strPrefix) {
|
|
+ const char prefixIndicator = '_';
|
|
+ size_t idx = str.find(prefixIndicator);
|
|
+ if (idx == std::string::npos)
|
|
+ strPrefix = str;
|
|
+ else
|
|
+ strPrefix = str.substr(0, idx + 1);
|
|
+}
|
|
+
|
|
+static std::string encodeStringFeature(const std::string &str) {
|
|
+ unsigned char hash[SHA256_DIGEST_LENGTH];
|
|
+ SHA256_CTX sha256;
|
|
+ SHA256_Init(&sha256);
|
|
+ SHA256_Update(&sha256, str.c_str(), str.size());
|
|
+ SHA256_Final(hash, &sha256);
|
|
+
|
|
+ std::stringstream ss;
|
|
+ for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
|
|
+ ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
|
|
+ }
|
|
+ return ss.str();
|
|
}
|
|
|
|
-extern float runONNXModel(ONNXRunner* instance, std::vector<std::string> input_string, std::vector<int64_t> input_int64, std::vector<float> input_float) {
|
|
- if (instance != nullptr) {
|
|
- return instance->runONNXModel(input_string, input_int64, input_float);
|
|
- } else {
|
|
- return -1;
|
|
+static void preprocessData(std::vector<std::string> &inputString,
|
|
+ std::vector<int64_t> &inputInt64, int argcSW,
|
|
+ const char **argvSW, const char *mcpuOption,
|
|
+ int argcHW, int64_t *argvHW) {
|
|
+ const char *outputOption = "-o";
|
|
+ const char *macroPrefix = "-D";
|
|
+ const char *needle = "--param";
|
|
+ const char *flagPrefix = "-";
|
|
+ const char *defaultOption = "-fdefault-option";
|
|
+ const int defaultIntVal = 0;
|
|
+
|
|
+ // Preprocessing string features.
|
|
+ std::string outputFile = "";
|
|
+ std::vector<std::string> inputStringRaw = {std::string(mcpuOption)};
|
|
+ std::vector<std::string> macroOptions;
|
|
+ for (int i = 0; i < argcSW; ++i) {
|
|
+ std::string opt = std::string(argvSW[i]);
|
|
+ if (startsWithPatt(opt, macroPrefix)) {
|
|
+ macroOptions.push_back(opt);
|
|
+ }
|
|
+ if (i + 1 < argcSW && opt.compare(outputOption) == 0) {
|
|
+ truncatePrefix(std::string(argvSW[i + 1]), outputFile);
|
|
+ }
|
|
+ }
|
|
+ inputStringRaw.push_back(outputFile);
|
|
+
|
|
+ std::sort(macroOptions.begin(), macroOptions.end(), optionComparator);
|
|
+ for (size_t i = 0; i < macroOptions.size() &&
|
|
+ (int)inputStringRaw.size() < FEATURE_SIZE_STRING_OPT;
|
|
+ ++i) {
|
|
+ inputStringRaw.push_back(macroOptions[i]);
|
|
+ }
|
|
+
|
|
+ for (int i = 0;
|
|
+ i < argcSW && (int)inputStringRaw.size() < FEATURE_SIZE_STRING_OPT;
|
|
+ ++i) {
|
|
+ std::string opt = std::string(argvSW[i]);
|
|
+ if (!startsWithPatt(opt, macroPrefix) && !startsWithPatt(opt, needle) &&
|
|
+ startsWithPatt(opt, flagPrefix)) {
|
|
+ inputStringRaw.push_back(opt);
|
|
}
|
|
+ }
|
|
+
|
|
+ for (int i = (int)inputStringRaw.size(); i < FEATURE_SIZE_STRING_OPT; ++i) {
|
|
+ inputStringRaw.push_back(defaultOption);
|
|
+ }
|
|
+ for (size_t i = 0; i < inputStringRaw.size(); ++i) {
|
|
+ inputString.push_back(encodeStringFeature(inputStringRaw[i]));
|
|
+ }
|
|
+
|
|
+ // Preprocessing int64 features.
|
|
+ for (int i = 0; i < argcHW && i < FEATURE_SIZE_INT64_OPT; ++i) {
|
|
+ inputInt64.push_back(argvHW[i]);
|
|
+ }
|
|
+
|
|
+ for (int i = (int)inputInt64.size(); i < FEATURE_SIZE_INT64_OPT; ++i) {
|
|
+ inputInt64.push_back(defaultIntVal);
|
|
+ }
|
|
+}
|
|
+
|
|
+extern int64_t runONNXModelOptimizer(int argcSW, const char **argvSW,
|
|
+ const char *mcpuOption, int argcHW,
|
|
+ int64_t *argvHW) {
|
|
+ // Create model runner.
|
|
+ ONNXRunner *instance = createONNXRunner(MODEL_PATH_OPT);
|
|
+ if (instance == nullptr) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ // Preprocess data.
|
|
+ std::vector<std::string> inputString;
|
|
+ std::vector<int64_t> inputInt64;
|
|
+ std::vector<float> inputFloat;
|
|
+ preprocessData(inputString, inputInt64, argcSW, argvSW, mcpuOption, argcHW,
|
|
+ argvHW);
|
|
+
|
|
+ // Run model.
|
|
+ int64_t output;
|
|
+ if (instance != nullptr) {
|
|
+ output =
|
|
+ instance->runONNXModelOptimizer(inputString, inputInt64, inputFloat, 1);
|
|
+ }
|
|
+
|
|
+ // Delete model runner.
|
|
+ deleteONNXRunner(instance);
|
|
+ return output;
|
|
}
|
|
-} // namespace boltONNXRunner
|
|
+} // namespace compilerONNXRunner
|
|
} // extern "C"
|
|
#endif
|
|
diff --git a/models/optimizer.onnx b/models/optimizer.onnx
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8218b0c2f589a4fd6d9010e67f4e23dab6358f0e
|
|
GIT binary patch
|
|
literal 41037
|
|
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
|
|
|
|
literal 0
|
|
HcmV?d00001
|
|
|
|
--
|
|
2.33.0
|
|
|