microsoft/onnxruntime-extensions
Publicmirrored from https://github.com/microsoft/onnxruntime-extensionsAvailable
include/op_def_struct.h
281lines · modeblame
c599b00dWenbing Li3 years ago | 1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | // Licensed under the MIT License. | |
| 3 | | |
| 4 | //.A very thin wrapper of ONNXRuntime Custom Operator Callback ABI, which | |
| 5 | // is only used in the custom-op kernels. For the general ORT C++ invocation, like end-to-end | |
| 6 | // testing, the ONNXRuntime public C++ APIs should be used since there is no binary compatible requirement. | |
| 7 | | |
| 8 | #pragma once | |
01e3a636Wenbing Li2 years ago | 9 | #include <cstdint> |
c599b00dWenbing Li3 years ago | 10 | #include <cstddef> |
| 11 | #include <array> | |
| 12 | #include <memory> | |
| 13 | #include <string> | |
| 14 | #include <vector> | |
| 15 | #include <utility> | |
| 16 | #include <type_traits> | |
914509d5Wenbing Li2 years ago | 17 | #include <optional> |
22340011cao lei2 years ago | 18 | #include <functional> |
c599b00dWenbing Li3 years ago | 19 | |
5e44a7c3Scott McKay3 years ago | 20 | #include "exceptions.h" |
a0c26255Wenbing Li2 years ago | 21 | #include "onnxruntime_extensions.h" |
64646279Wenbing Li2 years ago | 22 | #include "custom_op/custom_op_lite.h" |
914509d5Wenbing Li2 years ago | 23 | |
| 24 | #define MIN_ORT_VERSION_SUPPORTED 11 | |
8f36cf32Tang, Cheng3 years ago | 25 | |
914509d5Wenbing Li2 years ago | 26 | namespace Ort { |
| 27 | namespace Custom { | |
| 28 | | |
f9290e8bWenbing Li2 years ago | 29 | template <typename T> |
| 30 | inline OrtStatusPtr ToApiStatus(const T& status) { | |
97ee9eb5Wenbing Li2 years ago | 31 | return (OrtStatus*)status; |
f9290e8bWenbing Li2 years ago | 32 | } |
| 33 | | |
| 34 | template <> | |
| 35 | inline OrtStatusPtr ToApiStatus(const OrtStatusPtr& status) { | |
| 36 | return status; | |
| 37 | } | |
| 38 | | |
| 39 | template <typename RType, typename... Args> | |
914509d5Wenbing Li2 years ago | 40 | struct FunctionKernel { |
f9290e8bWenbing Li2 years ago | 41 | using ComputeFn = std::function<RType(Args...)>; |
914509d5Wenbing Li2 years ago | 42 | |
f9290e8bWenbing Li2 years ago | 43 | RType Compute(Args... args) const { |
914509d5Wenbing Li2 years ago | 44 | return compute_fn_(args...); |
| 45 | } | |
| 46 | | |
| 47 | ComputeFn compute_fn_; | |
| 48 | }; | |
| 49 | | |
| 50 | // primary template handles types that have no nested ::type member: | |
| 51 | template <class, class = void> | |
| 52 | struct IsFunctionKernel { | |
| 53 | typedef std::false_type type; | |
| 54 | }; | |
| 55 | | |
| 56 | // specialization recognizes types that do have a nested ::type member: | |
| 57 | template <class T> | |
a0c26255Wenbing Li2 years ago | 58 | struct IsFunctionKernel<T, std::void_t<typename T::ComputeFn>> { |
914509d5Wenbing Li2 years ago | 59 | typedef std::true_type type; |
| 60 | }; | |
| 61 | | |
| 62 | // Helper type | |
| 63 | template <typename T> | |
| 64 | struct ComputeArgsList; | |
| 65 | | |
| 66 | // Specialization for member function | |
f9290e8bWenbing Li2 years ago | 67 | template <typename RType, typename C, typename... Args> |
| 68 | struct ComputeArgsList<RType (C::*)(Args...) const> { | |
| 69 | using FunctionType = RType (*)(Args...); | |
| 70 | using MemberFunctionType = RType (C::*)(Args...) const; | |
| 71 | using ResultType = RType; | |
914509d5Wenbing Li2 years ago | 72 | }; |
| 73 | | |
3b889fc4Tang, Cheng2 years ago | 74 | template<typename, typename T> |
| 75 | struct HasOnModelAttach { | |
| 76 | static_assert( | |
| 77 | std::integral_constant<T, false>::value, | |
| 78 | "Second template parameter needs to be of function type."); | |
| 79 | }; | |
| 80 | | |
| 81 | // specialization that does the checking | |
| 82 | | |
| 83 | template<typename C, typename Ret, typename... Args> | |
| 84 | struct HasOnModelAttach<C, Ret(Args...)> { | |
| 85 | private: | |
| 86 | template<typename T> | |
| 87 | static constexpr auto check(T*) | |
| 88 | -> typename | |
| 89 | std::is_same< | |
| 90 | decltype( std::declval<T>().OnModelAttach( std::declval<Args>()... ) ), | |
| 91 | Ret | |
| 92 | >::type; // attempt to call it and see if the return type is correct | |
| 93 | | |
| 94 | template<typename> | |
| 95 | static constexpr std::false_type check(...); | |
| 96 | | |
| 97 | typedef decltype(check<C>(0)) type; | |
| 98 | | |
| 99 | public: | |
| 100 | static constexpr bool value = type::value; | |
| 101 | }; | |
| 102 | | |
22340011cao lei2 years ago | 103 | template <typename T, typename = void> |
| 104 | struct CustomOp_defined_getInputMemoryType : std::false_type {}; | |
| 105 | | |
| 106 | template <typename T> | |
| 107 | struct CustomOp_defined_getInputMemoryType<T, std::void_t<decltype(&T::GetInputMemoryType)>> : std::true_type {}; | |
| 108 | | |
914509d5Wenbing Li2 years ago | 109 | template <typename CustomOpKernel> |
| 110 | struct OrtLiteCustomStructV2 : public OrtLiteCustomOp { | |
| 111 | using ComputeFunction = decltype(&CustomOpKernel::Compute); | |
| 112 | using RegularComputeType = typename ComputeArgsList<ComputeFunction>::FunctionType; | |
f9290e8bWenbing Li2 years ago | 113 | using RType = typename ComputeArgsList<ComputeFunction>::ResultType; |
914509d5Wenbing Li2 years ago | 114 | |
| 115 | template <typename... Args> | |
f9290e8bWenbing Li2 years ago | 116 | using MemberComputeType = RType (CustomOpKernel::*)(Args...) const; |
914509d5Wenbing Li2 years ago | 117 | |
| 118 | struct KernelEx : public CustomOpKernel { | |
| 119 | struct { | |
| 120 | std::string ep_{}; | |
| 121 | std::unique_ptr<OrtW::CustomOpApi> api_; | |
| 122 | } extra_; | |
| 123 | }; | |
| 124 | | |
| 125 | template <typename T> | |
| 126 | static OrtStatusPtr InitKernel(KernelEx& kernel, | |
a0c26255Wenbing Li2 years ago | 127 | const OrtApi& api, const OrtKernelInfo& info, RegularComputeType fn, T t) { |
3b889fc4Tang, Cheng2 years ago | 128 | if constexpr (HasOnModelAttach<KernelEx, OrtStatusPtr(const OrtApi&, const OrtKernelInfo&)>::value){ |
| 129 | auto status = kernel.OnModelAttach(api, info); | |
| 130 | return ToApiStatus(status); | |
| 131 | } | |
| 132 | else { | |
| 133 | auto status = kernel.OnModelAttach(OrtAttributeReader(api, info)); | |
| 134 | return ToApiStatus(status); | |
| 135 | } | |
914509d5Wenbing Li2 years ago | 136 | } |
| 137 | | |
| 138 | static OrtStatusPtr InitKernel( | |
a0c26255Wenbing Li2 years ago | 139 | KernelEx& kernel, |
| 140 | const OrtApi& api, const OrtKernelInfo& info, RegularComputeType fn, std::true_type) { | |
914509d5Wenbing Li2 years ago | 141 | kernel.compute_fn_ = fn; |
| 142 | return nullptr; | |
| 143 | } | |
| 144 | | |
| 145 | template <typename... Args> | |
| 146 | void ParseArgs(MemberComputeType<Args...> fn) { | |
| 147 | OrtLiteCustomOp::ParseArgs<Args...>(OrtLiteCustomOp::input_types_, OrtLiteCustomOp::output_types_); | |
| 148 | } | |
| 149 | | |
| 150 | // TODO: consider to disable these legacy functions for mobile build to save binary size | |
| 151 | template <typename... Args> | |
| 152 | void DefineCallbackFunctionsLegacy(MemberComputeType<Args...> fn, RegularComputeType regular_fn) { | |
| 153 | OrtCustomOp::CreateKernel = [](const OrtCustomOp* this_, const OrtApi* ort_api, const OrtKernelInfo* info) { | |
| 154 | auto self = static_cast<const OrtLiteCustomStructV2<CustomOpKernel>*>(this_); | |
| 155 | auto kernel = std::make_unique<KernelEx>(); | |
| 156 | typedef typename IsFunctionKernel<CustomOpKernel>::type type_flag; | |
f9290e8bWenbing Li2 years ago | 157 | auto status = InitKernel(*kernel, *ort_api, *info, self->regular_fn_, type_flag()); |
914509d5Wenbing Li2 years ago | 158 | OrtW::ThrowOnError(*ort_api, status); |
| 159 | | |
| 160 | kernel->extra_.ep_ = self->execution_provider_; | |
| 161 | kernel->extra_.api_ = std::make_unique<OrtW::CustomOpApi>(*ort_api); | |
| 162 | return reinterpret_cast<void*>(kernel.release()); | |
| 163 | }; | |
| 164 | | |
| 165 | OrtCustomOp::KernelCompute = [](void* op_kernel, OrtKernelContext* context) { | |
| 166 | auto kernel = reinterpret_cast<KernelEx*>(op_kernel); | |
| 167 | std::vector<TensorPtr> tensors; | |
| 168 | auto t = CreateTuple<0, 0, Args...>(kernel->extra_.api_.get(), | |
| 169 | context, | |
| 170 | tensors, | |
| 171 | kernel->extra_.api_->KernelContext_GetInputCount(context), | |
| 172 | kernel->extra_.api_->KernelContext_GetOutputCount(context), | |
| 173 | kernel->extra_.ep_); | |
| 174 | std::apply([kernel](Args const&... t_args) { | |
f9290e8bWenbing Li2 years ago | 175 | auto status = kernel->Compute(t_args...); OrtW::API::ThrowOnError(ToApiStatus(status)); }, t); |
914509d5Wenbing Li2 years ago | 176 | }; |
| 177 | | |
| 178 | OrtCustomOp::KernelDestroy = [](void* op_kernel) { | |
| 179 | std::unique_ptr<KernelEx>(reinterpret_cast<KernelEx*>(op_kernel)).reset(); | |
| 180 | }; | |
| 181 | } | |
| 182 | | |
cd7d6459Wenbing Li2 years ago | 183 | #if ORT_API_VERSION >= 16 |
914509d5Wenbing Li2 years ago | 184 | template <typename... Args> |
| 185 | void DefineCallbackFunctions(MemberComputeType<Args...> fn, RegularComputeType regular_fn) { | |
| 186 | OrtCustomOp::CreateKernel = nullptr; | |
| 187 | OrtCustomOp::KernelCompute = nullptr; | |
| 188 | | |
22340011cao lei2 years ago | 189 | if constexpr (CustomOp_defined_getInputMemoryType<CustomOpKernel>::value) { |
| 190 | OrtCustomOp::GetInputMemoryType = [](const OrtCustomOp* /*this_*/, size_t index) -> OrtMemType { | |
| 191 | return CustomOpKernel::GetInputMemoryType(index); | |
| 192 | }; | |
| 193 | } | |
| 194 | | |
914509d5Wenbing Li2 years ago | 195 | OrtCustomOp::CreateKernelV2 = [](const OrtCustomOp* this_, |
| 196 | const OrtApi* api, const OrtKernelInfo* info, void** op_kernel) -> OrtStatusPtr { | |
| 197 | if (api == nullptr) { | |
| 198 | assert(false && "Got a null pointer for ORT api on calling CreateKernelV2"); | |
| 199 | // should never happened, what we can do? | |
| 200 | return nullptr; | |
| 201 | } | |
| 202 | | |
| 203 | if (this_ == nullptr || info == nullptr || op_kernel == nullptr) { | |
| 204 | return api->CreateStatus(ORT_INVALID_ARGUMENT, "OrtCustomOp::CreateKernelV2: received a null pointer"); | |
| 205 | } | |
| 206 | | |
| 207 | auto self = static_cast<const OrtLiteCustomStructV2<CustomOpKernel>*>(this_); | |
| 208 | auto kernel = std::make_unique<KernelEx>(); | |
| 209 | if (kernel == nullptr) { | |
| 210 | return api->CreateStatus(ORT_FAIL, "OrtCustomOp::CreateKernelV2: failed to new a kernel, OOM?"); | |
| 211 | } | |
| 212 | | |
| 213 | typedef typename IsFunctionKernel<CustomOpKernel>::type flag_type; | |
f9290e8bWenbing Li2 years ago | 214 | auto status = InitKernel(*kernel, *api, *info, self->regular_fn_, flag_type()); |
914509d5Wenbing Li2 years ago | 215 | if (status == nullptr) { |
| 216 | kernel->extra_.ep_ = self->execution_provider_; | |
| 217 | kernel->extra_.api_ = std::make_unique<OrtW::CustomOpApi>(*api); | |
| 218 | *op_kernel = reinterpret_cast<void*>(kernel.release()); | |
| 219 | } | |
| 220 | | |
| 221 | return status; | |
| 222 | }; | |
| 223 | | |
| 224 | OrtCustomOp::KernelComputeV2 = [](void* op_kernel, OrtKernelContext* context) -> OrtStatusPtr { | |
a0c26255Wenbing Li2 years ago | 225 | auto kernel = reinterpret_cast<KernelEx*>(op_kernel); |
914509d5Wenbing Li2 years ago | 226 | std::vector<TensorPtr> tensors; |
| 227 | auto t = CreateTuple<0, 0, Args...>(kernel->extra_.api_.get(), | |
| 228 | context, | |
| 229 | tensors, | |
| 230 | kernel->extra_.api_->KernelContext_GetInputCount(context), | |
| 231 | kernel->extra_.api_->KernelContext_GetOutputCount(context), | |
| 232 | kernel->extra_.ep_); | |
f9290e8bWenbing Li2 years ago | 233 | return std::apply([kernel](Args const&... t_args) { |
| 234 | auto status = kernel->Compute(t_args...); | |
| 235 | return ToApiStatus(status); }, t); | |
914509d5Wenbing Li2 years ago | 236 | }; |
| 237 | | |
| 238 | OrtCustomOp::KernelDestroy = [](void* op_kernel) { | |
| 239 | std::unique_ptr<KernelEx>(reinterpret_cast<KernelEx*>(op_kernel)).reset(); | |
| 240 | }; | |
| 241 | } | |
cd7d6459Wenbing Li2 years ago | 242 | #endif // ORT_API_VERSION >= 16 |
914509d5Wenbing Li2 years ago | 243 | |
| 244 | OrtLiteCustomStructV2(const char* op_name, | |
| 245 | const char* execution_provider, | |
| 246 | RegularComputeType fn_compute = nullptr) | |
| 247 | : OrtLiteCustomOp(op_name, execution_provider), regular_fn_(fn_compute) { | |
| 248 | ParseArgs(&CustomOpKernel::Compute); | |
| 249 | | |
cd7d6459Wenbing Li2 years ago | 250 | #if ORT_API_VERSION >= 16 |
f9290e8bWenbing Li2 years ago | 251 | if (OrtCustomOp::version >= 16) { |
914509d5Wenbing Li2 years ago | 252 | DefineCallbackFunctions(&CustomOpKernel::Compute, fn_compute); |
| 253 | } else | |
cd7d6459Wenbing Li2 years ago | 254 | #endif // ORT_API_VERSION >= 16 |
914509d5Wenbing Li2 years ago | 255 | { |
| 256 | DefineCallbackFunctionsLegacy(&CustomOpKernel::Compute, fn_compute); | |
| 257 | } | |
| 258 | } | |
| 259 | | |
| 260 | RegularComputeType regular_fn_{}; | |
| 261 | }; | |
| 262 | | |
f9290e8bWenbing Li2 years ago | 263 | template <typename RType, typename... Args> |
914509d5Wenbing Li2 years ago | 264 | OrtLiteCustomOp* CreateLiteCustomOpV2(const char* op_name, |
| 265 | const char* execution_provider, | |
f9290e8bWenbing Li2 years ago | 266 | RType (*custom_compute_fn)(Args...)) { |
| 267 | using LiteOp = OrtLiteCustomStructV2<FunctionKernel<RType, Args...>>; | |
914509d5Wenbing Li2 years ago | 268 | return std::make_unique<LiteOp>(op_name, execution_provider, custom_compute_fn).release(); |
| 269 | } | |
| 270 | | |
| 271 | template <typename OpKernel> | |
| 272 | OrtLiteCustomOp* CreateLiteCustomOpV2(const char* op_name, | |
| 273 | const char* execution_provider) { | |
| 274 | using LiteOp = OrtLiteCustomStructV2<OpKernel>; | |
| 275 | return std::make_unique<LiteOp>(op_name, execution_provider).release(); | |
| 276 | } | |
| 277 | | |
| 278 | } // namespace Custom | |
| 279 | } // namespace Ort | |
| 280 | | |
8f36cf32Tang, Cheng3 years ago | 281 | namespace ortc = Ort::Custom; |