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