diff --git a/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/CXX_Api_Sample.cpp b/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/CXX_Api_Sample.cpp new file mode 100644 index 0000000000..09897e6f0b --- /dev/null +++ b/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/CXX_Api_Sample.cpp @@ -0,0 +1,124 @@ +// Copyright(c) Microsoft Corporation.All rights reserved. +// Licensed under the MIT License. +// + +#include +#include +#include + +int main(int argc, char* argv[]) { + //************************************************************************* + // initialize enviroment...one enviroment per process + // enviroment maintains thread pools and other state info + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test"); + + // initialize session options if needed + Ort::SessionOptions session_options; + session_options.SetThreadPoolSize(1); + + // If onnxruntime.dll is built with CUDA enabled, we can uncomment out this line to use CUDA for this + // session (we also need to include cuda_provider_factory.h above which defines it) + // #include "cuda_provider_factory.h" + // OrtSessionOptionsAppendExecutionProvider_CUDA(session_opsions, 1); + + // Sets graph optimization level + // Available levels are + // 0 -> To disable all optimizations + // 1 -> To enable basic optimizations (Such as redundant node removals) + // 2 -> To enable all optimizations (Includes level 1 + more complex optimizations like node fusions) + session_options.SetGraphOptimizationLevel(1); + + //************************************************************************* + // create session and load model into memory + // using squeezenet version 1.3 + // URL = https://github.com/onnx/models/tree/master/squeezenet + const wchar_t* model_path = L"squeezenet.onnx"; + Ort::Session session(env, model_path, session_options); + + //************************************************************************* + // print model input layer (node names, types, shape etc.) + Ort::Allocator allocator = Ort::Allocator::Create_Default(); + + // print number of model input nodes + size_t num_input_nodes = session.GetInputCount(); + std::vector input_node_names(num_input_nodes); + std::vector input_node_dims; // simplify... this model has only 1 input node {1, 3, 224, 224}. + // Otherwise need vector> + + printf("Number of inputs = %zu\n", num_input_nodes); + + // iterate over all input nodes + for (int i = 0; i < num_input_nodes; i++) { + // print input node names + char* input_name = session.GetInputName(i, allocator); + printf("Input %d : name=%s\n", i, input_name); + input_node_names[i] = input_name; + + // print input node types + Ort::TypeInfo type_info = session.GetInputTypeInfo(i); + auto tensor_info = type_info.GetTensorTypeAndShapeInfo(); + + ONNXTensorElementDataType type = tensor_info.GetElementType(); + printf("Input %d : type=%d\n", i, type); + + // print input shapes/dims + input_node_dims = tensor_info.GetShape(); + printf("Input %d : num_dims=%zu\n", i, input_node_dims.size()); + for (int j = 0; j < input_node_dims.size(); j++) + printf("Input %d : dim %d=%jd\n", i, j, input_node_dims[j]); + } + + // Results should be... + // Number of inputs = 1 + // Input 0 : name = data_0 + // Input 0 : type = 1 + // Input 0 : num_dims = 4 + // Input 0 : dim 0 = 1 + // Input 0 : dim 1 = 3 + // Input 0 : dim 2 = 224 + // Input 0 : dim 3 = 224 + + //************************************************************************* + // Similar operations to get output node information. + // Use OrtSessionGetOutputCount(), OrtSessionGetOutputName() + // OrtSessionGetOutputTypeInfo() as shown above. + + //************************************************************************* + // Score the model using sample data, and inspect values + + size_t input_tensor_size = 224 * 224 * 3; // simplify ... using known dim values to calculate size + // use OrtGetTensorShapeElementCount() to get official size! + + std::vector input_tensor_values(input_tensor_size); + std::vector output_node_names = {"softmaxout_1"}; + + // initialize input data with values in [0.0, 1.0] + for (unsigned int i = 0; i < input_tensor_size; i++) + input_tensor_values[i] = (float)i / (input_tensor_size + 1); + + // create input tensor object from data values + Ort::AllocatorInfo allocator_info = Ort::AllocatorInfo::Create_Cpu(OrtArenaAllocator, OrtMemTypeDefault); + Ort::Value input_tensors[1] = {Ort::Value::CreateTensor(allocator_info, input_tensor_values.data(), input_tensor_size * sizeof(float), input_node_dims.data(), 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT)}; + assert(input_tensors[0].IsTensor()); + + // score model & input tensor, get back output tensor + Ort::Value output_tensor = session.Run(nullptr, input_node_names.data(), input_tensors, output_node_names.data(), 1); + assert(output_tensor.IsTensor()); + + // Get pointer to output tensor float values + float* floatarr = output_tensor.GetTensorMutableData(); + assert(abs(floatarr[0] - 0.000045) < 1e-6); + + // score the model, and print scores for first 5 classes + for (int i = 0; i < 5; i++) + printf("Score for class [%d] = %f\n", i, floatarr[i]); + + // Results should be as below... + // Score for class[0] = 0.000045 + // Score for class[1] = 0.003846 + // Score for class[2] = 0.000125 + // Score for class[3] = 0.001180 + // Score for class[4] = 0.001317 + printf("Done!\n"); + return 0; +} diff --git a/include/onnxruntime/core/session/onnxruntime_c_api.h b/include/onnxruntime/core/session/onnxruntime_c_api.h index 599e17cbc7..cf07e58477 100644 --- a/include/onnxruntime/core/session/onnxruntime_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_c_api.h @@ -237,6 +237,7 @@ ORT_API(void, OrtSetSessionLogVerbosityLevel, _In_ OrtSessionOptions* options, u ORT_API(int, OrtSetSessionGraphOptimizationLevel, _In_ OrtSessionOptions* options, uint32_t graph_optimization_level); // How many threads in the session thread pool. +// Returns 0 on success, and -1 otherwise ORT_API(int, OrtSetSessionThreadPoolSize, _In_ OrtSessionOptions* options, int session_thread_pool_size); /** @@ -553,11 +554,11 @@ struct OrtCustomOpApi { size_t(ORT_API_CALL* GetDimensionCount)(_In_ const OrtTensorTypeAndShapeInfo* info); void(ORT_API_CALL* GetDimensions)(_In_ const OrtTensorTypeAndShapeInfo* info, _Out_ int64_t* dim_values, size_t dim_values_length); OrtStatus*(ORT_API_CALL* SetDimensions)(OrtTensorTypeAndShapeInfo* info, _In_ const int64_t* dim_values, size_t dim_count); - OrtStatus*(ORT_API_CALL* GetTensorMutableData)(_Inout_ OrtValue* value, void** data); + OrtStatus*(ORT_API_CALL* GetTensorMutableData)(_Inout_ OrtValue* value, _Out_ void** data); void(ORT_API_CALL* ReleaseTensorTypeAndShapeInfo)(OrtTensorTypeAndShapeInfo* input); - OrtValue*(ORT_API_CALL* KernelContext_GetInput)(OrtKernelContext* context, _In_ size_t index); + const OrtValue*(ORT_API_CALL* KernelContext_GetInput)(const OrtKernelContext* context, _In_ size_t index); OrtValue*(ORT_API_CALL* KernelContext_GetOutput)(OrtKernelContext* context, _In_ size_t index, _In_ const int64_t* dim_values, size_t dim_count); }; typedef struct OrtCustomOpApi OrtCustomOpApi; diff --git a/include/onnxruntime/core/session/onnxruntime_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_cxx_api.h index c0be19e149..dcb84ba024 100644 --- a/include/onnxruntime/core/session/onnxruntime_cxx_api.h +++ b/include/onnxruntime/core/session/onnxruntime_cxx_api.h @@ -3,79 +3,443 @@ #pragma once #include "onnxruntime_c_api.h" -#include -#include -#include +#include +#include +#include #include -#include "core/common/exceptions.h" - -//TODO: encode error code in the message? -#define ORT_THROW_ON_ERROR(expr) \ - do { \ - OrtStatus* onnx_status = (expr); \ - if (onnx_status != nullptr) { \ - std::string ort_error_message = OrtGetErrorMessage(onnx_status); \ - OrtErrorCode error_code = OrtGetErrorCode(onnx_status); \ - OrtReleaseStatus(onnx_status); \ - switch (error_code) { \ - case ORT_NOT_IMPLEMENTED: \ - throw onnxruntime::NotImplementedException(ort_error_message); \ - default: \ - throw onnxruntime::OnnxRuntimeException(ORT_WHERE, ort_error_message); \ - } \ - } \ - } while (0); +#include +#include +#include #define ORT_REDIRECT_SIMPLE_FUNCTION_CALL(NAME) \ decltype(Ort##NAME(value.get())) NAME() { \ return Ort##NAME(value.get()); \ } +#define ORT_DEFINE_DELETER(NAME) \ + template <> \ + struct default_delete { \ + void operator()(Ort##NAME* ptr) { \ + OrtRelease##NAME(ptr); \ + } \ + }; + namespace std { -template <> -struct default_delete { - void operator()(OrtAllocator* ptr) { - OrtReleaseAllocator(ptr); - } -}; - -template <> -struct default_delete { - void operator()(OrtEnv* ptr) { - OrtReleaseEnv(ptr); - } -}; - -template <> -struct default_delete { - void operator()(OrtRunOptions* ptr) { - OrtReleaseRunOptions(ptr); - } -}; - -template <> -struct default_delete { - void operator()(OrtTypeInfo* ptr) { - OrtReleaseTypeInfo(ptr); - } -}; - -template <> -struct default_delete { - void operator()(OrtTensorTypeAndShapeInfo* ptr) { - OrtReleaseTensorTypeAndShapeInfo(ptr); - } -}; - -template <> -struct default_delete { - void operator()(OrtSessionOptions* ptr) { - OrtReleaseSessionOptions(ptr); - } -}; +ORT_DEFINE_DELETER(Allocator); +ORT_DEFINE_DELETER(TypeInfo); +ORT_DEFINE_DELETER(RunOptions); +ORT_DEFINE_DELETER(SessionOptions); +ORT_DEFINE_DELETER(TensorTypeAndShapeInfo); } // namespace std +namespace Ort { + +using std::nullptr_t; + +struct Exception : std::exception { + Exception(std::string&& string, OrtErrorCode code) : message_{std::move(string)}, code_{code} {} + + OrtErrorCode GetOrtErrorCode() const { return code_; } + const char* what() const noexcept { return message_.c_str(); } + + private: + std::string message_; + OrtErrorCode code_; +}; + +#define ORT_THROW_ON_ERROR(expr) \ + if (OrtStatus* onnx_status = (expr)) { \ + std::string ort_error_message = OrtGetErrorMessage(onnx_status); \ + OrtErrorCode ort_error_code = OrtGetErrorCode(onnx_status); \ + OrtReleaseStatus(onnx_status); \ + throw Ort::Exception(std::move(ort_error_message), ort_error_code); \ + } + +#define ORT_DEFINE_RELEASE(NAME) \ + inline void Release(Ort##NAME* ptr) { OrtRelease##NAME(ptr); } + +ORT_DEFINE_RELEASE(Allocator); +ORT_DEFINE_RELEASE(AllocatorInfo); +ORT_DEFINE_RELEASE(CustomOpDomain); +ORT_DEFINE_RELEASE(Env); +ORT_DEFINE_RELEASE(RunOptions); +ORT_DEFINE_RELEASE(Session); +ORT_DEFINE_RELEASE(SessionOptions); +ORT_DEFINE_RELEASE(TensorTypeAndShapeInfo); +ORT_DEFINE_RELEASE(TypeInfo); +ORT_DEFINE_RELEASE(Value); + +template +struct Base { + Base() = default; + Base(T* p) : p_{p} {} + ~Base() { Release(p_); } + + operator T*() { return p_; } + operator const T*() const { return p_; } + + T* release() { + T* p = p_; + p_ = nullptr; + return p; + } + + protected: + Base(const Base&) = delete; + Base(Base&& v) : p_{v.p_} { v.p_ = nullptr; } + void operator=(Base&& v) { + Release(p_); + p_ = v.p_; + v.p_ = nullptr; + } + + T* p_{}; + + template + friend struct Unowned; +}; + +template +struct Unowned : T { + Unowned(decltype(T::p_) p) : T{p} {} + Unowned(Unowned&& v) : T{v.p_} {} + ~Unowned() { this->p_ = nullptr; } +}; + +struct TypeInfo; +struct Value; + +struct Env : Base { + Env(nullptr_t) {} + Env(OrtLoggingLevel default_warning_level, _In_ const char* logid); +}; + +struct CustomOpDomain : Base { + CustomOpDomain(nullptr_t) {} + CustomOpDomain(const char* domain); + + void Add(OrtCustomOp* op); +}; + +struct RunOptions : Base { + RunOptions(nullptr_t) {} + RunOptions(); + + RunOptions& SetRunLogVerbosityLevel(unsigned int); + unsigned int GetRunLogVerbosityLevel() const; + + RunOptions& SetRunTag(const char* run_tag); + const char* GetRunTag() const; + + RunOptions& SetTerminate(bool flag); +}; + +struct SessionOptions : Base { + SessionOptions(nullptr_t) {} + SessionOptions(); + explicit SessionOptions(OrtSessionOptions* p) : Base{p} {} + + SessionOptions clone() const; + + SessionOptions& SetThreadPoolSize(int session_thread_pool_size); + SessionOptions& SetGraphOptimizationLevel(uint32_t graph_optimization_level); + + SessionOptions& EnableCpuMemArena(); + SessionOptions& DisableCpuMemArena(); + + SessionOptions& EnableSequentialExecution(); + SessionOptions& DisableSequentialExecution(); + + SessionOptions& SetLogId(const char* logid); + + SessionOptions& Add(OrtCustomOpDomain* custom_op_domain); +}; + +struct Session : Base { + Session(nullptr_t) {} + Session(OrtEnv* env, const ORTCHAR_T* model_path, const OrtSessionOptions* options); + + template + Value Run(OrtRunOptions* run_options, const char* const* input_names, Value (&input)[InputCount], + const char* const* output_names, size_t output_names_len); + + size_t GetInputCount() const; + size_t GetOutputCount() const; + + char* GetInputName(size_t index, OrtAllocator* allocator) const; + char* GetOutputName(size_t index, OrtAllocator* allocator) const; + + TypeInfo GetInputTypeInfo(size_t index) const; + TypeInfo GetOutputTypeInfo(size_t index) const; +}; + +struct TensorTypeAndShapeInfo : Base { + TensorTypeAndShapeInfo(nullptr_t) {} + explicit TensorTypeAndShapeInfo(OrtTensorTypeAndShapeInfo* p) : Base{p} {} + + ONNXTensorElementDataType GetElementType() const; + + size_t GetDimensionsCount() const; + void GetDimensions(int64_t* values, size_t values_count) const; + std::vector GetShape() const; +}; + +struct TypeInfo : Base { + TypeInfo(nullptr_t) {} + explicit TypeInfo(OrtTypeInfo* p) : Base{p} {} + + Unowned GetTensorTypeAndShapeInfo() const; +}; + +struct Value : Base { + static Value CreateTensor(const OrtAllocatorInfo* info, void* p_data, size_t p_data_len, const int64_t* shape, size_t shape_len, + ONNXTensorElementDataType type); + + Value(nullptr_t) {} + explicit Value(OrtValue* p) : Base{p} {} + + bool IsTensor() const; + template + T* GetTensorMutableData(); + + TensorTypeAndShapeInfo GetTensorTypeAndShapeInfo() const; +}; + +struct Allocator : Base { + static Allocator Create_Default(); + + Allocator(nullptr_t) {} + explicit Allocator(OrtAllocator* p) : Base{p} {} + + void* Alloc(size_t size); + void Free(void* p); + + const OrtAllocatorInfo* GetInfo() const; +}; + +struct AllocatorInfo : Base { + static AllocatorInfo Create_Cpu(OrtAllocatorType type, OrtMemType mem_type1); + + AllocatorInfo(nullptr_t) {} + explicit AllocatorInfo(OrtAllocatorInfo* p) : Base{p} {} +}; +} // namespace Ort + +namespace Ort { +inline Allocator Allocator::Create_Default() { + OrtAllocator* p; + ORT_THROW_ON_ERROR(OrtCreateDefaultAllocator(&p)); + return Allocator(p); +} + +inline void* Allocator::Alloc(size_t size) { + return OrtAllocatorAlloc(p_, size); +} + +inline void Allocator::Free(void* p) { + OrtAllocatorFree(p_, p); +} + +inline const OrtAllocatorInfo* Allocator::GetInfo() const { + return OrtAllocatorGetInfo(p_); +} + +inline AllocatorInfo AllocatorInfo::Create_Cpu(OrtAllocatorType type, OrtMemType mem_type) { + OrtAllocatorInfo* p; + ORT_THROW_ON_ERROR(OrtCreateCpuAllocatorInfo(type, mem_type, &p)); + return AllocatorInfo(p); +} + +inline Env::Env(OrtLoggingLevel default_warning_level, _In_ const char* logid) { + ORT_THROW_ON_ERROR(OrtCreateEnv(default_warning_level, logid, &p_)); +} + +inline CustomOpDomain::CustomOpDomain(const char* domain) + : Base{OrtCreateCustomOpDomain(domain)} { +} + +inline void CustomOpDomain::Add(OrtCustomOp* op) { + ORT_THROW_ON_ERROR(OrtCustomOpDomain_Add(p_, op)); +} + +inline RunOptions::RunOptions() : Base{ + OrtCreateRunOptions()} {} + +inline RunOptions& RunOptions::SetRunLogVerbosityLevel(unsigned int level) { + ORT_THROW_ON_ERROR(OrtRunOptionsSetRunLogVerbosityLevel(p_, level)); + return *this; +} + +inline unsigned int RunOptions::GetRunLogVerbosityLevel() const { + return OrtRunOptionsGetRunLogVerbosityLevel(p_); +} + +inline RunOptions& RunOptions::SetRunTag(const char* run_tag) { + ORT_THROW_ON_ERROR(OrtRunOptionsSetRunTag(p_, run_tag)); + return *this; +} + +inline const char* RunOptions::GetRunTag() const { + return OrtRunOptionsGetRunTag(p_); +} + +inline RunOptions& RunOptions::SetTerminate(bool flag) { + OrtRunOptionsSetTerminate(p_, flag ? 1 : 0); + return *this; +} + +inline SessionOptions::SessionOptions() : Base{OrtCreateSessionOptions()} { +} + +inline SessionOptions SessionOptions::clone() const { + return SessionOptions{OrtCloneSessionOptions(p_)}; +} + +inline SessionOptions& SessionOptions::SetThreadPoolSize(int session_thread_pool_size) { + if (OrtSetSessionThreadPoolSize(p_, session_thread_pool_size) == -1) + throw Exception("Error calling SessionOptions::SetThreadPoolSize", ORT_FAIL); + return *this; +} + +inline SessionOptions& SessionOptions::SetGraphOptimizationLevel(uint32_t graph_optimization_level) { + if (OrtSetSessionGraphOptimizationLevel(p_, graph_optimization_level) == -1) + throw Exception("Error calling SessionOptions::SetGraphOptimizationLevel", ORT_FAIL); + return *this; +} + +inline SessionOptions& SessionOptions::EnableCpuMemArena() { + OrtEnableCpuMemArena(p_); + return *this; +} + +inline SessionOptions& SessionOptions::DisableCpuMemArena() { + OrtDisableCpuMemArena(p_); + return *this; +} + +inline SessionOptions& SessionOptions::EnableSequentialExecution() { + OrtEnableSequentialExecution(p_); + return *this; +} + +inline SessionOptions& SessionOptions::DisableSequentialExecution() { + OrtDisableSequentialExecution(p_); + return *this; +} + +inline SessionOptions& SessionOptions::SetLogId(const char* logid) { + OrtSetSessionLogId(p_, logid); + return *this; +} +inline SessionOptions& SessionOptions::Add(OrtCustomOpDomain* custom_op_domain) { + ORT_THROW_ON_ERROR(OrtAddCustomOpDomain(p_, custom_op_domain)); + return *this; +} + +inline Session::Session(OrtEnv* env, const ORTCHAR_T* model_path, const OrtSessionOptions* options) { + ORT_THROW_ON_ERROR(OrtCreateSession(env, model_path, options, &p_)); +} + +template +inline Value Session::Run(OrtRunOptions* run_options, const char* const* input_names, Value (&inputs)[InputCount], + const char* const* output_names, size_t output_names_len) { + std::array internal_inputs; + std::copy_n(inputs, InputCount, internal_inputs.data()); + + OrtValue* out{}; + ORT_THROW_ON_ERROR(OrtRun(p_, run_options, input_names, internal_inputs.data(), InputCount, output_names, output_names_len, &out)); + return Value{out}; +} + +inline size_t Session::GetInputCount() const { + size_t out; + ORT_THROW_ON_ERROR(OrtSessionGetInputCount(p_, &out)); + return out; +} + +inline size_t Session::GetOutputCount() const { + size_t out; + ORT_THROW_ON_ERROR(OrtSessionGetOutputCount(p_, &out)); + return out; +} + +inline char* Session::GetInputName(size_t index, OrtAllocator* allocator) const { + char* out; + ORT_THROW_ON_ERROR(OrtSessionGetInputName(p_, index, allocator, &out)); + return out; +} + +inline char* Session::GetOutputName(size_t index, OrtAllocator* allocator) const { + char* out; + ORT_THROW_ON_ERROR(OrtSessionGetOutputName(p_, index, allocator, &out)); + return out; +} + +inline TypeInfo Session::GetInputTypeInfo(size_t index) const { + OrtTypeInfo* out; + ORT_THROW_ON_ERROR(OrtSessionGetInputTypeInfo(p_, index, &out)); + return TypeInfo{out}; +} + +inline TypeInfo Session::GetOutputTypeInfo(size_t index) const { + OrtTypeInfo* out; + ORT_THROW_ON_ERROR(OrtSessionGetOutputTypeInfo(p_, index, &out)); + return TypeInfo{out}; +} + +inline ONNXTensorElementDataType TensorTypeAndShapeInfo::GetElementType() const { + return OrtGetTensorElementType(p_); +} + +inline size_t TensorTypeAndShapeInfo::GetDimensionsCount() const { + return OrtGetNumOfDimensions(p_); +} + +inline void TensorTypeAndShapeInfo::GetDimensions(int64_t* values, size_t values_count) const { + OrtGetDimensions(p_, values, values_count); +} + +inline std::vector TensorTypeAndShapeInfo::GetShape() const { + std::vector output; + output.resize(GetDimensionsCount()); + GetDimensions(output.data(), output.size()); + return output; +} + +inline Unowned TypeInfo::GetTensorTypeAndShapeInfo() const { + return Unowned{const_cast(OrtCastTypeInfoToTensorInfo(p_))}; +} + +inline Value Value::CreateTensor(const OrtAllocatorInfo* info, void* p_data, size_t p_data_len, const int64_t* shape, size_t shape_len, + ONNXTensorElementDataType type) { + OrtValue* out; + ORT_THROW_ON_ERROR(OrtCreateTensorWithDataAsOrtValue(info, p_data, p_data_len, shape, shape_len, type, &out)); + return Value(out); +} + +inline bool Value::IsTensor() const { + return OrtIsTensor(p_) != 0; +} + +template +T* Value::GetTensorMutableData() { + T* output; + ORT_THROW_ON_ERROR(OrtGetTensorMutableData(p_, (void**)&output)); + return output; +} + +inline TensorTypeAndShapeInfo Value::GetTensorTypeAndShapeInfo() const { + OrtTensorTypeAndShapeInfo* output; + ORT_THROW_ON_ERROR(OrtGetTensorShapeAndType(p_, &output)); + return TensorTypeAndShapeInfo{output}; +} + +} // namespace Ort + +// Deprecated: Will be removed once all dependencies of it are removed +#if 1 namespace onnxruntime { + class SessionOptionsWrapper { private: std::unique_ptr value; @@ -115,20 +479,14 @@ class SessionOptionsWrapper { OrtSessionOptions* p = OrtCloneSessionOptions(value.get()); return SessionOptionsWrapper(env_, p); } -#ifdef _WIN32 - OrtSession* OrtCreateSession(_In_ const wchar_t* model_path) { + + OrtSession* OrtCreateSession(_In_ const ORTCHAR_T* model_path) { OrtSession* ret = nullptr; ORT_THROW_ON_ERROR(::OrtCreateSession(env_, model_path, value.get(), &ret)); return ret; } -#else - OrtSession* OrtCreateSession(_In_ const char* model_path) { - OrtSession* ret = nullptr; - ORT_THROW_ON_ERROR(::OrtCreateSession(env_, model_path, value.get(), &ret)); - return ret; - } -#endif }; + inline OrtValue* OrtCreateTensorAsOrtValue(_Inout_ OrtAllocator* env, const std::vector& shape, ONNXTensorElementDataType type) { OrtValue* ret; ORT_THROW_ON_ERROR(::OrtCreateTensorAsOrtValue(env, shape.data(), shape.size(), type, &ret)); @@ -148,6 +506,10 @@ inline std::vector GetTensorShape(const OrtTensorTypeAndShapeInfo* info return ret; } +} // namespace onnxruntime +#endif + +namespace Ort { struct CustomOpApi { CustomOpApi(const OrtCustomOpApi& api) : api_(api) {} @@ -183,11 +545,16 @@ struct CustomOpApi { return data; } + template + const T* GetTensorData(_Inout_ const OrtValue* value) { + return GetTensorMutableData(const_cast(value)); + } + void ReleaseTensorTypeAndShapeInfo(OrtTensorTypeAndShapeInfo* input) { api_.ReleaseTensorTypeAndShapeInfo(input); } - OrtValue* KernelContext_GetInput(OrtKernelContext* context, _In_ size_t index) { + const OrtValue* KernelContext_GetInput(const OrtKernelContext* context, _In_ size_t index) { return api_.KernelContext_GetInput(context, index); } OrtValue* KernelContext_GetOutput(OrtKernelContext* context, _In_ size_t index, _In_ const int64_t* dim_values, size_t dim_count) { @@ -231,6 +598,6 @@ struct CustomOpBase : OrtCustomOp { } }; -} // namespace onnxruntime +} // namespace Ort #undef ORT_REDIRECT_SIMPLE_FUNCTION_CALL diff --git a/onnxruntime/core/session/custom_ops.cc b/onnxruntime/core/session/custom_ops.cc index a821a718de..63d737652c 100644 --- a/onnxruntime/core/session/custom_ops.cc +++ b/onnxruntime/core/session/custom_ops.cc @@ -29,8 +29,8 @@ ORT_API_STATUS_IMPL(OrtKernelInfoGetAttribute_int64, _In_ const OrtKernelInfo* i return onnxruntime::ToOrtStatus(status); } -ORT_API(OrtValue*, OrtKernelContext_GetInput, OrtKernelContext* context, _In_ size_t index) { - return reinterpret_cast(const_cast(reinterpret_cast(context)->GetInputMLValue(index))); +ORT_API(const OrtValue*, OrtKernelContext_GetInput, const OrtKernelContext* context, _In_ size_t index) { + return reinterpret_cast(reinterpret_cast(context)->GetInputMLValue(index)); }; ORT_API(OrtValue*, OrtKernelContext_GetOutput, OrtKernelContext* context, _In_ size_t index, _In_ const int64_t* dim_values, size_t dim_count) { @@ -58,7 +58,6 @@ constexpr OrtCustomOpApi g_custom_op_api = { }; namespace onnxruntime { - struct CustomOpKernel : OpKernel { CustomOpKernel(const OpKernelInfo& info, OrtCustomOp& op) : OpKernel(info), op_(op) { if (op_.version != 1) diff --git a/onnxruntime/test/onnx/TestCase.h b/onnxruntime/test/onnx/TestCase.h index 5d7fe59453..56a5eb9dfe 100644 --- a/onnxruntime/test/onnx/TestCase.h +++ b/onnxruntime/test/onnx/TestCase.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/onnxruntime/test/onnx/main.cc b/onnxruntime/test/onnx/main.cc index 9ad622a74e..f57301b35c 100644 --- a/onnxruntime/test/onnx/main.cc +++ b/onnxruntime/test/onnx/main.cc @@ -67,9 +67,9 @@ int GetNumCpuCores() { return std::thread::hardware_concurrency(); } } // namespace #ifdef _WIN32 -int real_main(int argc, wchar_t* argv[], OrtEnv** p_env) { +int real_main(int argc, wchar_t* argv[], Ort::Env& env) { #else -int real_main(int argc, char* argv[], OrtEnv** p_env) { +int real_main(int argc, char* argv[], Ort::Env& env) { #endif // if this var is not empty, only run the tests with name in this list std::vector > whitelisted_test_cases; @@ -164,16 +164,14 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) { usage(); return -1; } - OrtEnv* env; - { - OrtStatus* ost = OrtCreateEnv(logging_level, "Default", &env); - if (ost != nullptr) { - fprintf(stderr, "Error creating environment: %s \n", OrtGetErrorMessage(ost)); - OrtReleaseStatus(ost); - return -1; - } - *p_env = env; + + try { + env = Ort::Env{logging_level, "Default"}; + } catch (std::exception& ex) { + fprintf(stderr, "Error creating environment: %s \n", ex.what()); + return -1; } + std::vector > data_dirs; TestResultStat stat; @@ -184,7 +182,7 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) { double per_sample_tolerance = 1e-3; // when cuda is enabled, set it to a larger value for resolving random MNIST test failure double relative_per_sample_tolerance = enable_cuda ? 0.017 : 1e-3; - SessionOptionsWrapper sf(env); + Ort::SessionOptions sf; if (enable_cpu_mem_arena) sf.EnableCpuMemArena(); else @@ -265,7 +263,8 @@ int real_main(int argc, char* argv[], OrtEnv** p_env) { } } } - TestEnv args(tests, stat, sf); + + TestEnv args(tests, stat, env, sf); Status st = RunTests(args, p_models, concurrent_session_runs, static_cast(repeat_count), GetDefaultThreadPool(Env::Default())); if (!st.IsOK()) { @@ -431,17 +430,16 @@ int wmain(int argc, wchar_t* argv[]) { #else int main(int argc, char* argv[]) { #endif - OrtEnv* env = nullptr; + Ort::Env env{nullptr}; int retval = -1; try { - retval = real_main(argc, argv, &env); + retval = real_main(argc, argv, env); } catch (std::exception& ex) { fprintf(stderr, "%s\n", ex.what()); retval = -1; } - if (env) { - OrtReleaseEnv(env); - } else { + // Release the protobuf library if we failed to create an env (the env will release it automatically on destruction) + if (!env) { ::google::protobuf::ShutdownProtobufLibrary(); } return retval; diff --git a/onnxruntime/test/onnx/runner.cc b/onnxruntime/test/onnx/runner.cc index fc00f2ec5b..60801f3703 100644 --- a/onnxruntime/test/onnx/runner.cc +++ b/onnxruntime/test/onnx/runner.cc @@ -34,7 +34,7 @@ void ORT_CALLBACK RunTestCase(ORT_CALLBACK_INSTANCE pci, void* context, ORT_WORK ITestCase* info = task->env.tests[task->task_id]; std::shared_ptr ret; try { - RunSingleTestCase(info, task->env.sf, task->concurrent_runs, task->repeat_count, task->pool, pci, [task](std::shared_ptr result, ORT_CALLBACK_INSTANCE pci) { + RunSingleTestCase(info, task->env.env, task->env.sf, task->concurrent_runs, task->repeat_count, task->pool, pci, [task](std::shared_ptr result, ORT_CALLBACK_INSTANCE pci) { return OnTestCaseFinished(pci, task, result); }); return; @@ -169,7 +169,7 @@ Status RunTests(TestEnv& env, int p_models, int concurrent_runs, size_t repeat_c ORT_EVENT ev; ORT_RETURN_IF_ERROR(CreateOnnxRuntimeEvent(&ev)); try { - RunSingleTestCase(env.tests[i], env.sf, concurrent_runs, repeat_count, tpool, nullptr, [repeat_count, &results, ev, concurrent_runs, test_case_name](std::shared_ptr result, ORT_CALLBACK_INSTANCE pci) { + RunSingleTestCase(env.tests[i], env.env, env.sf, concurrent_runs, repeat_count, tpool, nullptr, [repeat_count, &results, ev, concurrent_runs, test_case_name](std::shared_ptr result, ORT_CALLBACK_INSTANCE pci) { //TODO:output this information to a xml if (concurrent_runs == 1) { TIME_SPEC ts = result->GetSpentTime(); @@ -310,6 +310,7 @@ std::pair CompareGenericValue(const OrtValue* o, co bool post_processing) { return onnxruntime::CompareMLValue(*(MLValue*)o, *(MLValue*)expected_mlvalue, per_sample_tolerance, relative_per_sample_tolerance, post_processing); } + EXECUTE_RESULT DataRunner::RunTaskImpl(size_t task_id) { HeapBuffer holder; std::unordered_map feeds; @@ -400,7 +401,7 @@ EXECUTE_RESULT DataRunner::RunTaskImpl(size_t task_id) { if (compare_result == COMPARE_RESULT::SUCCESS) { const ONNX_NAMESPACE::ValueInfoProto* v = name_output_value_info_proto[output_name]; if (v == nullptr) continue; - ret = VerifyValueInfo(*v, actual_output_value); + ret = VerifyValueInfo(*v, Ort::Unowned{actual_output_value}); compare_result = ret.first; if (compare_result != COMPARE_RESULT::SUCCESS) { switch (compare_result) { @@ -458,16 +459,15 @@ void SeqTestRunner::Start(ORT_CALLBACK_INSTANCE pci, size_t) { finish(pci); } -void RunSingleTestCase(ITestCase* info, const onnxruntime::SessionOptionsWrapper& sf, size_t concurrent_runs, size_t repeat_count, PThreadPool tpool, ORT_CALLBACK_INSTANCE pci, TestCaseCallBack on_finished) { +void RunSingleTestCase(ITestCase* info, Ort::Env& env, const Ort::SessionOptions& sf, size_t concurrent_runs, size_t repeat_count, PThreadPool tpool, ORT_CALLBACK_INSTANCE pci, TestCaseCallBack on_finished) { std::shared_ptr ret; size_t data_count = info->GetDataCount(); try { DataRunner* r = nullptr; std::string node_name = info->GetNodeName(); auto sf2 = sf.clone(); - sf2.SetSessionLogId(info->GetTestCaseName().c_str()); - std::unique_ptr session_object( - sf2.OrtCreateSession(info->GetModelUrl()), OrtReleaseSession); + sf2.SetLogId(info->GetTestCaseName().c_str()); + Ort::Session session_object{env, info->GetModelUrl(), sf2}; LOGF_DEFAULT(INFO, "testing %s\n", info->GetTestCaseName().c_str()); //temp hack. Because we have no resource control. We may not have enough memory to run this test in parallel if (info->GetTestCaseName() == "coreml_FNS-Candy_ImageNet") @@ -479,6 +479,13 @@ void RunSingleTestCase(ITestCase* info, const onnxruntime::SessionOptionsWrapper } r->Start(pci, concurrent_runs); return; + } catch (const Ort::Exception& ex) { + if (ex.GetOrtErrorCode() != ORT_NOT_IMPLEMENTED) + throw; + + LOGF_DEFAULT(ERROR, "Test %s failed:%s", info->GetTestCaseName().c_str(), ex.what()); + std::string node_name; + ret = std::make_shared(data_count, EXECUTE_RESULT::NOT_SUPPORT, ""); } catch (onnxruntime::NotImplementedException& ex) { LOGF_DEFAULT(ERROR, "Test %s failed:%s", info->GetTestCaseName().c_str(), ex.what()); std::string node_name; diff --git a/onnxruntime/test/onnx/runner.h b/onnxruntime/test/onnx/runner.h index a7821b5e9f..630a946657 100644 --- a/onnxruntime/test/onnx/runner.h +++ b/onnxruntime/test/onnx/runner.h @@ -136,5 +136,5 @@ void LoadTests(const std::vector>& input_paths //Do not run this function in the thread pool passed in ::onnxruntime::common::Status RunTests(TestEnv& env, int p_models, int concurrent_runs, size_t repeat_count, PThreadPool tpool); EXECUTE_RESULT StatusCodeToExecuteResult(int input); -void RunSingleTestCase(ITestCase* info, const onnxruntime::SessionOptionsWrapper& sf, size_t concurrent_runs, +void RunSingleTestCase(ITestCase* info, Ort::Env& env, const Ort::SessionOptions& sf, size_t concurrent_runs, size_t repeat_count, PThreadPool tpool, ORT_CALLBACK_INSTANCE pci, TestCaseCallBack on_finished); diff --git a/onnxruntime/test/onnx/testenv.cc b/onnxruntime/test/onnx/testenv.cc index 1826f60931..b5cb918605 100644 --- a/onnxruntime/test/onnx/testenv.cc +++ b/onnxruntime/test/onnx/testenv.cc @@ -5,11 +5,9 @@ #include "FixedCountFinishCallback.h" #include -using onnxruntime::SessionOptionsWrapper; - using onnxruntime::Status; -TestEnv::TestEnv(const std::vector& tests1, TestResultStat& stat1, SessionOptionsWrapper& sf1) - : tests(tests1), next_test_to_run(0), stat(stat1), finished(new FixedCountFinishCallback(static_cast(tests1.size()))), sf(sf1) { +TestEnv::TestEnv(const std::vector& tests1, TestResultStat& stat1, Ort::Env& env1, Ort::SessionOptions& sf1) + : tests(tests1), next_test_to_run(0), stat(stat1), finished(new FixedCountFinishCallback(static_cast(tests1.size()))), env(env1), sf(sf1) { } TestEnv::~TestEnv() { diff --git a/onnxruntime/test/onnx/testenv.h b/onnxruntime/test/onnx/testenv.h index 34b591f733..6bc3607da4 100644 --- a/onnxruntime/test/onnx/testenv.h +++ b/onnxruntime/test/onnx/testenv.h @@ -20,8 +20,9 @@ class TestEnv { std::atomic_int next_test_to_run; TestResultStat& stat; FixedCountFinishCallback* finished; - const onnxruntime::SessionOptionsWrapper& sf; - TestEnv(const std::vector& tests, TestResultStat& stat1, onnxruntime::SessionOptionsWrapper& sf1); + Ort::Env& env; + const Ort::SessionOptions& sf; + TestEnv(const std::vector& tests, TestResultStat& stat1, Ort::Env& env, Ort::SessionOptions& sf1); ~TestEnv(); private: diff --git a/onnxruntime/test/shared_lib/test_allocator.cc b/onnxruntime/test/shared_lib/test_allocator.cc index 92d4533bac..4c7ceb006c 100644 --- a/onnxruntime/test/shared_lib/test_allocator.cc +++ b/onnxruntime/test/shared_lib/test_allocator.cc @@ -18,17 +18,12 @@ TEST_F(CApiTest, allocation_info) { } TEST_F(CApiTest, DefaultAllocator) { - std::unique_ptr default_allocator; - { - OrtAllocator* ptr; - ORT_THROW_ON_ERROR(OrtCreateDefaultAllocator(&ptr)); - default_allocator.reset(ptr); - } - char* p = (char*)OrtAllocatorAlloc(default_allocator.get(), 100); + Ort::Allocator default_allocator = Ort::Allocator::Create_Default(); + char* p = (char*)default_allocator.Alloc(100); ASSERT_NE(p, nullptr); memset(p, 0, 100); - OrtAllocatorFree(default_allocator.get(), p); - const OrtAllocatorInfo* info1 = OrtAllocatorGetInfo(default_allocator.get()); - const OrtAllocatorInfo* info2 = default_allocator->Info(default_allocator.get()); + default_allocator.Free(p); + const OrtAllocatorInfo* info1 = default_allocator.GetInfo(); + const OrtAllocatorInfo* info2 = static_cast(default_allocator)->Info(default_allocator); ASSERT_EQ(0, OrtCompareAllocatorInfo(info1, info2)); } diff --git a/onnxruntime/test/shared_lib/test_inference.cc b/onnxruntime/test/shared_lib/test_inference.cc index 8f7f18b6c5..abc9e006f1 100644 --- a/onnxruntime/test/shared_lib/test_inference.cc +++ b/onnxruntime/test/shared_lib/test_inference.cc @@ -166,7 +166,7 @@ INSTANTIATE_TEST_CASE_P(CApiTestWithProviders, ::testing::Values(0, 1, 2, 3, 4)); struct OrtTensorDimensions : std::vector { - OrtTensorDimensions(onnxruntime::CustomOpApi ort, OrtValue* value) { + OrtTensorDimensions(Ort::CustomOpApi ort, const OrtValue* value) { OrtTensorTypeAndShapeInfo* info = ort.GetTensorShapeAndType(value); auto dimensionCount = ort.GetDimensionCount(info); resize(dimensionCount); @@ -187,21 +187,21 @@ template constexpr size_t countof(T (&)[N]) { return N; } struct MyCustomKernel { - MyCustomKernel(onnxruntime::CustomOpApi ort, const OrtKernelInfo* /*info*/) : ort_(ort) { + MyCustomKernel(Ort::CustomOpApi ort, const OrtKernelInfo* /*info*/) : ort_(ort) { } void GetOutputShape(OrtKernelContext* context, size_t /*output_index*/, OrtTensorTypeAndShapeInfo* info) { - OrtValue* input_X = ort_.KernelContext_GetInput(context, 0); + const OrtValue* input_X = ort_.KernelContext_GetInput(context, 0); OrtTensorDimensions dimensions(ort_, input_X); ort_.SetDimensions(info, dimensions.data(), dimensions.size()); } void Compute(OrtKernelContext* context) { // Setup inputs - OrtValue* input_X = ort_.KernelContext_GetInput(context, 0); - OrtValue* input_Y = ort_.KernelContext_GetInput(context, 1); - float* X = ort_.GetTensorMutableData(input_X); - float* Y = ort_.GetTensorMutableData(input_Y); + const OrtValue* input_X = ort_.KernelContext_GetInput(context, 0); + const OrtValue* input_Y = ort_.KernelContext_GetInput(context, 1); + const float* X = ort_.GetTensorData(input_X); + const float* Y = ort_.GetTensorData(input_Y); // Setup output OrtTensorDimensions dimensions(ort_, input_X); @@ -219,11 +219,11 @@ struct MyCustomKernel { } private: - onnxruntime::CustomOpApi ort_; + Ort::CustomOpApi ort_; }; -struct MyCustomOp : onnxruntime::CustomOpBase { - void* CreateKernel(onnxruntime::CustomOpApi api, const OrtKernelInfo* info) { return new MyCustomKernel(api, info); }; +struct MyCustomOp : Ort::CustomOpBase { + void* CreateKernel(Ort::CustomOpApi api, const OrtKernelInfo* info) { return new MyCustomKernel(api, info); }; const char* GetName() const { return "Foo"; }; size_t GetInputTypeCount() const { return 2; }; diff --git a/onnxruntime/test/shared_lib/test_run_options.cc b/onnxruntime/test/shared_lib/test_run_options.cc index ea20ada5f1..5a07d66354 100644 --- a/onnxruntime/test/shared_lib/test_run_options.cc +++ b/onnxruntime/test/shared_lib/test_run_options.cc @@ -3,13 +3,12 @@ #include "core/session/onnxruntime_cxx_api.h" #include "test_fixture.h" -using namespace onnxruntime; TEST_F(CApiTest, run_options) { - std::unique_ptr options(OrtCreateRunOptions()); + Ort::RunOptions options; ASSERT_NE(options, nullptr); - ASSERT_EQ(OrtRunOptionsSetRunLogVerbosityLevel(options.get(), 1), nullptr); - ASSERT_EQ(OrtRunOptionsSetRunTag(options.get(), "abc"), nullptr); - ASSERT_STREQ(OrtRunOptionsGetRunTag(options.get()), "abc"); - ASSERT_EQ(OrtRunOptionsGetRunLogVerbosityLevel(options.get()), unsigned(1)); + options.SetRunLogVerbosityLevel(1); + options.SetRunTag("abc"); + ASSERT_STREQ(options.GetRunTag(), "abc"); + ASSERT_EQ(options.GetRunLogVerbosityLevel(), unsigned(1)); } diff --git a/onnxruntime/test/shared_lib/test_tensor_loader.cc b/onnxruntime/test/shared_lib/test_tensor_loader.cc index 4445770f40..5fe845af11 100644 --- a/onnxruntime/test/shared_lib/test_tensor_loader.cc +++ b/onnxruntime/test/shared_lib/test_tensor_loader.cc @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "core/session/onnxruntime_cxx_api.h" +#include "core/common/common.h" #include "onnx_protobuf.h" #include "test_fixture.h" diff --git a/onnxruntime/test/util/compare_mlvalue.cc b/onnxruntime/test/util/compare_mlvalue.cc index 96492c8987..26afbb3cc6 100644 --- a/onnxruntime/test/util/compare_mlvalue.cc +++ b/onnxruntime/test/util/compare_mlvalue.cc @@ -341,10 +341,10 @@ std::pair CompareMLValue(const MLValue& o, const ML per_sample_tolerance, relative_per_sample_tolerance, post_processing); } -std::pair VerifyValueInfo(const ONNX_NAMESPACE::ValueInfoProto& v, const OrtValue* o) { +std::pair VerifyValueInfo(const ONNX_NAMESPACE::ValueInfoProto& v, const Ort::Value& o) { if (!v.has_type()) return std::make_pair(COMPARE_RESULT::SUCCESS, ""); if (v.type().has_tensor_type()) { - if (!OrtIsTensor(o)) { + if (!o.IsTensor()) { return std::make_pair(COMPARE_RESULT::TYPE_MISMATCH, ""); } @@ -353,13 +353,8 @@ std::pair VerifyValueInfo(const ONNX_NAMESPACE::Val //if (((TensorTypeBase*)o.Type())->GetElementType() != DataTypeImpl::ElementTypeFromProto(t.elem_type())) { // return COMPARE_RESULT::TYPE_MISMATCH; //} - std::unique_ptr info; - { - OrtTensorTypeAndShapeInfo* t1 = nullptr; - ORT_THROW_ON_ERROR(OrtGetTensorShapeAndType(o, &t1)); - info.reset(t1); - } - ONNXTensorElementDataType real_type = OrtGetTensorElementType(info.get()); + auto info = o.GetTensorTypeAndShapeInfo(); + ONNXTensorElementDataType real_type = info.GetElementType(); ONNXTensorElementDataType expected_type = onnxruntime::utils::CApiElementTypeFromProtoType(t.elem_type()); if (real_type != expected_type) { std::ostringstream oss; @@ -368,7 +363,7 @@ std::pair VerifyValueInfo(const ONNX_NAMESPACE::Val return std::make_pair(COMPARE_RESULT::TYPE_MISMATCH, oss.str()); } - std::vector shape = GetTensorShape(info.get()); + std::vector shape = info.GetShape(); const auto& tensor_shape_proto = t.shape(); if (!AreShapesEqual(shape, tensor_shape_proto)) { std::ostringstream oss; @@ -386,7 +381,7 @@ std::pair VerifyValueInfo(const ONNX_NAMESPACE::Val //Cannot do this check for tensor type. //For tensor type, o.Type() is TensorTypeBase*, but p points to a subclass of TensorTypeBase auto p = DataTypeImpl::TypeFromProto(v.type()); - if (((MLValue*)o)->Type() != p) { + if (((MLValue*)(const OrtValue*)o)->Type() != p) { return std::make_pair(COMPARE_RESULT::TYPE_MISMATCH, ""); } } diff --git a/onnxruntime/test/util/include/test/compare_mlvalue.h b/onnxruntime/test/util/include/test/compare_mlvalue.h index 32024287a7..ce3764e2cf 100644 --- a/onnxruntime/test/util/include/test/compare_mlvalue.h +++ b/onnxruntime/test/util/include/test/compare_mlvalue.h @@ -23,5 +23,5 @@ std::pair CompareMLValue(const MLValue& real, const double relative_per_sample_tolerance, bool post_processing); //verify if the 'value' matches the 'expected' ValueInfoProto. 'value' is a model output -std::pair VerifyValueInfo(const ONNX_NAMESPACE::ValueInfoProto& expected, const OrtValue* value); +std::pair VerifyValueInfo(const ONNX_NAMESPACE::ValueInfoProto& expected, const Ort::Value& value); } // namespace onnxruntime