xref: /aosp_15_r20/external/executorch/backends/xnnpack/runtime/XNNPACKBackend.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1*523fa7a6SAndroid Build Coastguard Worker /*
2*523fa7a6SAndroid Build Coastguard Worker  * Copyright (c) Meta Platforms, Inc. and affiliates.
3*523fa7a6SAndroid Build Coastguard Worker  * All rights reserved.
4*523fa7a6SAndroid Build Coastguard Worker  *
5*523fa7a6SAndroid Build Coastguard Worker  * This source code is licensed under the BSD-style license found in the
6*523fa7a6SAndroid Build Coastguard Worker  * LICENSE file in the root directory of this source tree.
7*523fa7a6SAndroid Build Coastguard Worker  */
8*523fa7a6SAndroid Build Coastguard Worker 
9*523fa7a6SAndroid Build Coastguard Worker #include <executorch/backends/xnnpack/runtime/XNNCompiler.h>
10*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/backend/interface.h>
11*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/core/error.h>
12*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/core/evalue.h>
13*523fa7a6SAndroid Build Coastguard Worker #include <executorch/runtime/platform/profiler.h>
14*523fa7a6SAndroid Build Coastguard Worker 
15*523fa7a6SAndroid Build Coastguard Worker #include <memory>
16*523fa7a6SAndroid Build Coastguard Worker #include <mutex>
17*523fa7a6SAndroid Build Coastguard Worker 
18*523fa7a6SAndroid Build Coastguard Worker #pragma clang diagnostic ignored "-Wglobal-constructors"
19*523fa7a6SAndroid Build Coastguard Worker 
20*523fa7a6SAndroid Build Coastguard Worker namespace executorch {
21*523fa7a6SAndroid Build Coastguard Worker namespace backends {
22*523fa7a6SAndroid Build Coastguard Worker 
23*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::ArrayRef;
24*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Backend;
25*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::BackendExecutionContext;
26*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::BackendInitContext;
27*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::CompileSpec;
28*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::DelegateHandle;
29*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Error;
30*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::EValue;
31*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::FreeableBuffer;
32*523fa7a6SAndroid Build Coastguard Worker using executorch::runtime::Result;
33*523fa7a6SAndroid Build Coastguard Worker 
34*523fa7a6SAndroid Build Coastguard Worker class XnnpackBackend final : public ::executorch::runtime::BackendInterface {
35*523fa7a6SAndroid Build Coastguard Worker  public:
36*523fa7a6SAndroid Build Coastguard Worker   ~XnnpackBackend() = default;
37*523fa7a6SAndroid Build Coastguard Worker 
XnnpackBackend()38*523fa7a6SAndroid Build Coastguard Worker   XnnpackBackend() {
39*523fa7a6SAndroid Build Coastguard Worker     // Initialize XNNPACK
40*523fa7a6SAndroid Build Coastguard Worker     xnn_status status = xnn_initialize(/*allocator=*/nullptr);
41*523fa7a6SAndroid Build Coastguard Worker     if (status != xnn_status_success) {
42*523fa7a6SAndroid Build Coastguard Worker       ET_LOG(
43*523fa7a6SAndroid Build Coastguard Worker           Error,
44*523fa7a6SAndroid Build Coastguard Worker           "Failed to initialize, XNNPACK status: 0x%x",
45*523fa7a6SAndroid Build Coastguard Worker           (unsigned int)status);
46*523fa7a6SAndroid Build Coastguard Worker       return;
47*523fa7a6SAndroid Build Coastguard Worker     }
48*523fa7a6SAndroid Build Coastguard Worker 
49*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE
50*523fa7a6SAndroid Build Coastguard Worker     // Create a workspace for the XNNExecutor to use. This workspace will be
51*523fa7a6SAndroid Build Coastguard Worker     // shared across all delegate instances.
52*523fa7a6SAndroid Build Coastguard Worker     ET_LOG(Debug, "Creating XNN workspace");
53*523fa7a6SAndroid Build Coastguard Worker     xnn_workspace_t workspace = nullptr;
54*523fa7a6SAndroid Build Coastguard Worker     status = xnn_create_workspace(&workspace);
55*523fa7a6SAndroid Build Coastguard Worker     if (status != xnn_status_success) {
56*523fa7a6SAndroid Build Coastguard Worker       ET_LOG(
57*523fa7a6SAndroid Build Coastguard Worker           Error,
58*523fa7a6SAndroid Build Coastguard Worker           "Failed to create XNN workspace, XNNPACK status: 0x%x",
59*523fa7a6SAndroid Build Coastguard Worker           (unsigned int)status);
60*523fa7a6SAndroid Build Coastguard Worker       workspace = nullptr;
61*523fa7a6SAndroid Build Coastguard Worker       return;
62*523fa7a6SAndroid Build Coastguard Worker     }
63*523fa7a6SAndroid Build Coastguard Worker     workspace_.reset(workspace);
64*523fa7a6SAndroid Build Coastguard Worker     ET_LOG(Debug, "Created XNN workspace: %p", workspace_.get());
65*523fa7a6SAndroid Build Coastguard Worker #endif // ENABLE_XNNPACK_SHARED_WORKSPACE
66*523fa7a6SAndroid Build Coastguard Worker   }
67*523fa7a6SAndroid Build Coastguard Worker 
is_available() const68*523fa7a6SAndroid Build Coastguard Worker   bool is_available() const override {
69*523fa7a6SAndroid Build Coastguard Worker     return xnn_status_success == xnn_initialize(/*allocator=*/nullptr);
70*523fa7a6SAndroid Build Coastguard Worker   }
71*523fa7a6SAndroid Build Coastguard Worker 
init(BackendInitContext & context,FreeableBuffer * processed,ArrayRef<CompileSpec> compile_specs) const72*523fa7a6SAndroid Build Coastguard Worker   Result<DelegateHandle*> init(
73*523fa7a6SAndroid Build Coastguard Worker       BackendInitContext& context,
74*523fa7a6SAndroid Build Coastguard Worker       FreeableBuffer* processed,
75*523fa7a6SAndroid Build Coastguard Worker       ArrayRef<CompileSpec> compile_specs) const override {
76*523fa7a6SAndroid Build Coastguard Worker     auto executor = ET_ALLOCATE_INSTANCE_OR_RETURN_ERROR(
77*523fa7a6SAndroid Build Coastguard Worker         context.get_runtime_allocator(), xnnpack::delegate::XNNExecutor);
78*523fa7a6SAndroid Build Coastguard Worker 
79*523fa7a6SAndroid Build Coastguard Worker     // Executor has been allocated but not constructed, ensure that runtime_ is
80*523fa7a6SAndroid Build Coastguard Worker     // nullptr by constructing it in place here. NOTE: Since we use placement
81*523fa7a6SAndroid Build Coastguard Worker     // new and since this type is not trivially destructible, we must call the
82*523fa7a6SAndroid Build Coastguard Worker     // destructor manually in destroy().
83*523fa7a6SAndroid Build Coastguard Worker     new (executor) xnnpack::delegate::XNNExecutor;
84*523fa7a6SAndroid Build Coastguard Worker     Error err = xnnpack::delegate::XNNCompiler::compileModel(
85*523fa7a6SAndroid Build Coastguard Worker         processed->data(),
86*523fa7a6SAndroid Build Coastguard Worker         processed->size(),
87*523fa7a6SAndroid Build Coastguard Worker         executor,
88*523fa7a6SAndroid Build Coastguard Worker         context.get_runtime_allocator(),
89*523fa7a6SAndroid Build Coastguard Worker         workspace_.get());
90*523fa7a6SAndroid Build Coastguard Worker     // This backend does not need its processed data after compiling the model.
91*523fa7a6SAndroid Build Coastguard Worker     processed->Free();
92*523fa7a6SAndroid Build Coastguard Worker 
93*523fa7a6SAndroid Build Coastguard Worker     if (err != Error::Ok) {
94*523fa7a6SAndroid Build Coastguard Worker       // destroy() won't be called on this handle, so we need to clean it up
95*523fa7a6SAndroid Build Coastguard Worker       // now.
96*523fa7a6SAndroid Build Coastguard Worker       executor->~XNNExecutor();
97*523fa7a6SAndroid Build Coastguard Worker 
98*523fa7a6SAndroid Build Coastguard Worker       ET_LOG(
99*523fa7a6SAndroid Build Coastguard Worker           Error, "XNNCompiler::compileModel failed: 0x%x", (unsigned int)err);
100*523fa7a6SAndroid Build Coastguard Worker       return err;
101*523fa7a6SAndroid Build Coastguard Worker     }
102*523fa7a6SAndroid Build Coastguard Worker     return executor;
103*523fa7a6SAndroid Build Coastguard Worker   }
104*523fa7a6SAndroid Build Coastguard Worker 
execute(BackendExecutionContext & context,DelegateHandle * handle,EValue ** args) const105*523fa7a6SAndroid Build Coastguard Worker   Error execute(
106*523fa7a6SAndroid Build Coastguard Worker       BackendExecutionContext& context,
107*523fa7a6SAndroid Build Coastguard Worker       DelegateHandle* handle,
108*523fa7a6SAndroid Build Coastguard Worker       EValue** args) const override {
109*523fa7a6SAndroid Build Coastguard Worker     auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle);
110*523fa7a6SAndroid Build Coastguard Worker 
111*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE
112*523fa7a6SAndroid Build Coastguard Worker     const std::lock_guard<std::mutex> lock(workspace_mutex_);
113*523fa7a6SAndroid Build Coastguard Worker #endif
114*523fa7a6SAndroid Build Coastguard Worker 
115*523fa7a6SAndroid Build Coastguard Worker     // Prepare Inputs/Outputs and Propagate Input Shapes
116*523fa7a6SAndroid Build Coastguard Worker     Error err = executor->prepare_args(args);
117*523fa7a6SAndroid Build Coastguard Worker     if (err != Error::Ok) {
118*523fa7a6SAndroid Build Coastguard Worker       return err;
119*523fa7a6SAndroid Build Coastguard Worker     }
120*523fa7a6SAndroid Build Coastguard Worker 
121*523fa7a6SAndroid Build Coastguard Worker     err = executor->forward(context);
122*523fa7a6SAndroid Build Coastguard Worker 
123*523fa7a6SAndroid Build Coastguard Worker     if (err != Error::Ok) {
124*523fa7a6SAndroid Build Coastguard Worker       return err;
125*523fa7a6SAndroid Build Coastguard Worker     }
126*523fa7a6SAndroid Build Coastguard Worker 
127*523fa7a6SAndroid Build Coastguard Worker     // Resize outputs and recast pointers if necessary
128*523fa7a6SAndroid Build Coastguard Worker     err = executor->resize_outputs(args);
129*523fa7a6SAndroid Build Coastguard Worker 
130*523fa7a6SAndroid Build Coastguard Worker     return err;
131*523fa7a6SAndroid Build Coastguard Worker   }
132*523fa7a6SAndroid Build Coastguard Worker 
destroy(DelegateHandle * handle) const133*523fa7a6SAndroid Build Coastguard Worker   void destroy(DelegateHandle* handle) const override {
134*523fa7a6SAndroid Build Coastguard Worker     if (handle != nullptr) {
135*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_SHARED_WORKSPACE
136*523fa7a6SAndroid Build Coastguard Worker       // This is needed to serialize access to xnn_delete_runtime which is not
137*523fa7a6SAndroid Build Coastguard Worker       // thread safe. This can heppen when multiple threads call destroy() on
138*523fa7a6SAndroid Build Coastguard Worker       // the same backend instance.
139*523fa7a6SAndroid Build Coastguard Worker       const std::lock_guard<std::mutex> lock(workspace_mutex_);
140*523fa7a6SAndroid Build Coastguard Worker #endif
141*523fa7a6SAndroid Build Coastguard Worker       auto executor = static_cast<xnnpack::delegate::XNNExecutor*>(handle);
142*523fa7a6SAndroid Build Coastguard Worker #ifdef ENABLE_XNNPACK_PROFILING
143*523fa7a6SAndroid Build Coastguard Worker       executor->print_avg_op_timings();
144*523fa7a6SAndroid Build Coastguard Worker #endif
145*523fa7a6SAndroid Build Coastguard Worker       // XNNExecutor is not trivially destructible. Since this was constructed
146*523fa7a6SAndroid Build Coastguard Worker       // manually in init(), we must destroy it manually here.
147*523fa7a6SAndroid Build Coastguard Worker       executor->~XNNExecutor();
148*523fa7a6SAndroid Build Coastguard Worker     }
149*523fa7a6SAndroid Build Coastguard Worker   }
150*523fa7a6SAndroid Build Coastguard Worker 
151*523fa7a6SAndroid Build Coastguard Worker  private:
152*523fa7a6SAndroid Build Coastguard Worker   // This is a global workspace for all delegate instances.
153*523fa7a6SAndroid Build Coastguard Worker   mutable std::mutex workspace_mutex_;
154*523fa7a6SAndroid Build Coastguard Worker   std::unique_ptr<xnn_workspace, decltype(&xnn_release_workspace)> workspace_{
155*523fa7a6SAndroid Build Coastguard Worker       nullptr,
156*523fa7a6SAndroid Build Coastguard Worker       &xnn_release_workspace};
157*523fa7a6SAndroid Build Coastguard Worker };
158*523fa7a6SAndroid Build Coastguard Worker 
159*523fa7a6SAndroid Build Coastguard Worker namespace {
160*523fa7a6SAndroid Build Coastguard Worker auto cls = XnnpackBackend();
161*523fa7a6SAndroid Build Coastguard Worker Backend backend{"XnnpackBackend", &cls};
162*523fa7a6SAndroid Build Coastguard Worker static auto success_with_compiler = register_backend(backend);
163*523fa7a6SAndroid Build Coastguard Worker } // namespace
164*523fa7a6SAndroid Build Coastguard Worker 
165*523fa7a6SAndroid Build Coastguard Worker } // namespace backends
166*523fa7a6SAndroid Build Coastguard Worker } // namespace executorch
167