1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "CompilationBuilder"
18 
19 #include "CompilationBuilder.h"
20 
21 #include <algorithm>
22 #include <limits>
23 #include <memory>
24 #include <string>
25 #include <utility>
26 #include <vector>
27 
28 #include "BurstBuilder.h"
29 #include "ExecutionBuilder.h"
30 #include "ExecutionBurstController.h"
31 #include "ExecutionPlan.h"
32 #include "Manager.h"
33 #include "ModelBuilder.h"
34 #include "Utils.h"
35 
36 namespace android {
37 namespace nn {
38 
39 using namespace hal;
40 
CompilationBuilder(const ModelBuilder * model,const std::vector<std::shared_ptr<Device>> & devices,bool explicitDeviceList)41 CompilationBuilder::CompilationBuilder(const ModelBuilder* model,
42                                        const std::vector<std::shared_ptr<Device>>& devices,
43                                        bool explicitDeviceList)
44     : mModel(model),
45       mPartitioning(explicitDeviceList ? DeviceManager::kPartitioningWithoutFallback
46                                        : DeviceManager::get()->getPartitioning()),
47       mDevices(devices),
48       mExplicitDeviceList(explicitDeviceList) {
49     VLOG(COMPILATION) << "CompilationBuilder::CompilationBuilder";
50 }
51 
finish()52 int CompilationBuilder::finish() {
53     if (mFinished) {
54         LOG(ERROR) << "ANeuralNetworksCompilation_finish called more than once";
55         return ANEURALNETWORKS_BAD_STATE;
56     }
57     // TODO validate the rest
58 
59     const auto deadline = makeDeadline(mTimeoutDuration);
60 
61     mFinished = true;
62     if (mIsCacheInfoProvided) {
63         mPlan.setCaching(&mCacheDir, mToken);
64     }
65     if (mPartitioning) {
66         int n = mModel->partitionTheWork(mDevices, mPreference, mPriority, deadline, &mPlan);
67         switch (n) {
68             case ANEURALNETWORKS_NO_ERROR:
69                 return n;
70             case ANEURALNETWORKS_UNEXPECTED_NULL:
71             case ANEURALNETWORKS_BAD_DATA:
72                 // The two error codes above should only be used for errors in the user's
73                 // request. In case of a user error, we won't try any fallback.
74                 // TODO: Document this in NeuralNetworks.h and in the HAL. Make sure
75                 // driver writers know which code they can return.
76                 return n;
77             default:
78                 // The error might be recoverable. Return the error only if falling back
79                 // is not allowed.
80                 if (!DeviceManager::partitioningAllowsFallback(mPartitioning)) {
81                     return n;
82                 }
83                 if (mModel->hasOEMOperation()) {
84                     LOG(ERROR) << "Cannot fall back to CPU because of an OEM operation";
85                     return n;
86                 }
87                 if (mModel->hasExtensionOperation()) {
88                     LOG(ERROR) << "Cannot fall back to CPU because of an extension operation";
89                     return n;
90                 }
91                 break;
92         }
93     }
94 
95     // Fallback to CPU
96     VLOG(COMPILATION) << "CompilationBuilder::finish with CPU fallback";
97     mPlan.reset();
98     mPlan.becomeSingleStep(DeviceManager::getCpuDevice(), mModel);
99     return mPlan.finish(mPreference, mPriority, deadline);
100 }
101 
setPreference(int32_t preference)102 int CompilationBuilder::setPreference(int32_t preference) {
103     if (mFinished) {
104         LOG(ERROR) << "ANeuralNetworksCompilation_setPreference can't modify after compilation "
105                       "finished";
106         return ANEURALNETWORKS_BAD_STATE;
107     }
108     if (preference >= kNumberOfPreferences) {
109         LOG(ERROR) << "ANeuralNetworksCompilation_setPreference invalid preference " << preference;
110         return ANEURALNETWORKS_BAD_DATA;
111     }
112 
113     mPreference = preference;
114     return ANEURALNETWORKS_NO_ERROR;
115 }
116 
setCaching(const std::string & cacheDir,const uint8_t * token)117 int CompilationBuilder::setCaching(const std::string& cacheDir, const uint8_t* token) {
118     if (mFinished) {
119         LOG(ERROR)
120                 << "ANeuralNetworksCompilation_setCaching can't modify after compilation finished";
121         return ANEURALNETWORKS_BAD_STATE;
122     }
123     mCacheDir = cacheDir;
124     // Make sure the cache dir can concat with the filename.
125     if (!mCacheDir.empty() && mCacheDir.back() != '/') {
126         mCacheDir.push_back('/');
127     }
128     std::copy(token, token + ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, mToken);
129     mIsCacheInfoProvided = true;
130     return ANEURALNETWORKS_NO_ERROR;
131 }
132 
setPriority(int32_t priority)133 int CompilationBuilder::setPriority(int32_t priority) {
134     if (mFinished) {
135         LOG(ERROR) << "ANeuralNetworksCompilation_setPriority can't modify after compilation "
136                       "finished";
137         return ANEURALNETWORKS_BAD_STATE;
138     }
139     if (priority != ANEURALNETWORKS_PRIORITY_LOW && priority != ANEURALNETWORKS_PRIORITY_MEDIUM &&
140         priority != ANEURALNETWORKS_PRIORITY_HIGH) {
141         LOG(ERROR) << "ANeuralNetworksCompilation_setPriority invalid priority " << priority;
142         return ANEURALNETWORKS_BAD_DATA;
143     }
144 
145     mPriority = priority;
146     return ANEURALNETWORKS_NO_ERROR;
147 }
148 
setTimeoutDuration(uint64_t duration)149 int CompilationBuilder::setTimeoutDuration(uint64_t duration) {
150     if (mFinished) {
151         LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout can't modify after compilation "
152                       "finished";
153         return ANEURALNETWORKS_BAD_STATE;
154     }
155     if (!mExplicitDeviceList || (mDevices.size() != 1)) {
156         LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout called on an "
157                       "ANeuralNetworksCompilation that was not created by "
158                       "ANeuralNetworksCompilation_createForDevices with numDevices = 1";
159         return ANEURALNETWORKS_BAD_DATA;
160     }
161     if (duration > 0) {
162         mTimeoutDuration = duration;
163     } else {
164         mTimeoutDuration.reset();
165     }
166     return ANEURALNETWORKS_NO_ERROR;
167 }
168 
setPartitioning(uint32_t partitioning)169 int CompilationBuilder::setPartitioning(uint32_t partitioning) {
170     if (mFinished) {
171         LOG(ERROR) << "ANeuralNetworksCompilation_setPartitioning can't modify after compilation "
172                       "finished";
173         return ANEURALNETWORKS_BAD_STATE;
174     }
175 
176     mPartitioning = partitioning;
177     return ANEURALNETWORKS_NO_ERROR;
178 }
179 
createExecution(ExecutionBuilder ** execution)180 int CompilationBuilder::createExecution(ExecutionBuilder** execution) {
181     if (!mFinished) {
182         LOG(ERROR) << "ANeuralNetworksExecution_create passed an unfinished compilation";
183         *execution = nullptr;
184         return ANEURALNETWORKS_BAD_STATE;
185     }
186     if (!mPlan.isValid()) {
187         LOG(ERROR) << "ANeuralNetworksExecution_create passed an invalid compilation";
188         *execution = nullptr;
189         return ANEURALNETWORKS_BAD_STATE;
190     }
191     *execution = new (std::nothrow) ExecutionBuilder(this);
192     return (*execution ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
193 }
194 
createBurst(BurstBuilder ** burst)195 int CompilationBuilder::createBurst(BurstBuilder** burst) {
196     if (!mFinished) {
197         LOG(ERROR) << "ANeuralNetworksBurst_create passed an unfinished compilation";
198         *burst = nullptr;
199         return ANEURALNETWORKS_BAD_STATE;
200     }
201     if (!mPlan.isValid()) {
202         LOG(ERROR) << "ANeuralNetworksBurst_create passed an invalid compilation";
203         *burst = nullptr;
204         return ANEURALNETWORKS_BAD_STATE;
205     }
206     std::vector<std::shared_ptr<ExecutionBurstController>> burstControllers =
207             mPlan.makeBursts(mPreference);
208     *burst = new (std::nothrow) BurstBuilder(this, std::move(burstControllers));
209     return (*burst ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
210 }
211 
forEachStepRoleOfInput(uint32_t index,const StepRoleCallback & callback) const212 int CompilationBuilder::forEachStepRoleOfInput(uint32_t index,
213                                                const StepRoleCallback& callback) const {
214     if (!mFinished) {
215         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an unfinished compilation";
216         return ANEURALNETWORKS_BAD_STATE;
217     }
218     if (!mPlan.isValid()) {
219         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid compilation";
220         return ANEURALNETWORKS_BAD_STATE;
221     }
222     if (index >= mModel->inputCount()) {
223         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid input index "
224                    << index;
225         return ANEURALNETWORKS_BAD_DATA;
226     }
227     mPlan.forEachStepRoleOfInput(index, callback);
228     return ANEURALNETWORKS_NO_ERROR;
229 }
230 
forEachStepRoleOfOutput(uint32_t index,const StepRoleCallback & callback) const231 int CompilationBuilder::forEachStepRoleOfOutput(uint32_t index,
232                                                 const StepRoleCallback& callback) const {
233     if (!mFinished) {
234         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an unfinished compilation";
235         return ANEURALNETWORKS_BAD_STATE;
236     }
237     if (!mPlan.isValid()) {
238         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid compilation";
239         return ANEURALNETWORKS_BAD_STATE;
240     }
241     if (index >= mModel->outputCount()) {
242         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid output index "
243                    << index;
244         return ANEURALNETWORKS_BAD_DATA;
245     }
246     mPlan.forEachStepRoleOfOutput(index, callback);
247     return ANEURALNETWORKS_NO_ERROR;
248 }
249 
250 }  // namespace nn
251 }  // namespace android
252