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 <LegacyUtils.h>
22 #include <nnapi/IBurst.h>
23 #include <nnapi/SharedMemory.h>
24 #include <nnapi/Types.h>
25
26 #include <algorithm>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <vector>
32
33 #include "BurstBuilder.h"
34 #include "ExecutionBuilder.h"
35 #include "ExecutionPlan.h"
36 #include "Manager.h"
37 #include "ModelBuilder.h"
38
39 namespace android {
40 namespace nn {
41
CompilationBuilder(const ModelBuilder * model,const std::vector<std::shared_ptr<Device>> & devices,bool explicitDeviceList)42 CompilationBuilder::CompilationBuilder(const ModelBuilder* model,
43 const std::vector<std::shared_ptr<Device>>& devices,
44 bool explicitDeviceList)
45 : mModel(model),
46 mPartitioning(explicitDeviceList ? DeviceManager::kPartitioningWithoutFallback
47 : DeviceManager::get()->getPartitioning()),
48 mDevices(devices),
49 mExplicitDeviceList(explicitDeviceList) {
50 VLOG(COMPILATION) << "CompilationBuilder::CompilationBuilder";
51 }
52
finish()53 int CompilationBuilder::finish() {
54 if (mFinished) {
55 LOG(ERROR) << "ANeuralNetworksCompilation_finish called more than once";
56 return ANEURALNETWORKS_BAD_STATE;
57 }
58 // TODO validate the rest
59
60 const auto deadline = makeDeadline(mTimeoutDuration);
61
62 mFinished = true;
63 if (mIsCacheInfoProvided) {
64 mPlan.setCaching(&mCacheInfo, mToken);
65 }
66 if (mPartitioning) {
67 int n = mModel->partitionTheWork(mDevices, mPreference, mPriority, deadline, &mPlan,
68 mFailPartitioning);
69 switch (n) {
70 case ANEURALNETWORKS_NO_ERROR:
71 return n;
72 case ANEURALNETWORKS_UNEXPECTED_NULL:
73 case ANEURALNETWORKS_BAD_DATA:
74 // The two error codes above should only be used for errors in the user's
75 // request. In case of a user error, we won't try any fallback.
76 // TODO: Document this in NeuralNetworks.h and in the HAL. Make sure
77 // driver writers know which code they can return.
78 return n;
79 default:
80 // The error might be recoverable. Return the error only if falling back
81 // is not allowed.
82 if (!DeviceManager::partitioningAllowsFallback(mPartitioning)) {
83 return n;
84 }
85 if (mModel->hasOEMOperation()) {
86 LOG(ERROR) << "Cannot fall back to CPU because of an OEM operation";
87 return n;
88 }
89 if (mModel->hasExtensionOperation()) {
90 LOG(ERROR) << "Cannot fall back to CPU because of an extension operation";
91 return n;
92 }
93 break;
94 }
95 }
96
97 // Fallback to CPU
98 VLOG(COMPILATION) << "CompilationBuilder::finish with CPU fallback";
99 mPlan.reset();
100 mPlan.becomeSingleStep(DeviceManager::getCpuDevice(), mModel);
101 return mPlan.finish(mPreference, mPriority, deadline, ANEURALNETWORKS_NO_ERROR);
102 }
103
setPreference(int32_t preference)104 int CompilationBuilder::setPreference(int32_t preference) {
105 if (mFinished) {
106 LOG(ERROR) << "ANeuralNetworksCompilation_setPreference can't modify after compilation "
107 "finished";
108 return ANEURALNETWORKS_BAD_STATE;
109 }
110 if (preference >= kNumberOfPreferences) {
111 LOG(ERROR) << "ANeuralNetworksCompilation_setPreference invalid preference " << preference;
112 return ANEURALNETWORKS_BAD_DATA;
113 }
114
115 mPreference = preference;
116 return ANEURALNETWORKS_NO_ERROR;
117 }
118
setCaching(const std::string & cacheDir,const uint8_t * token)119 int CompilationBuilder::setCaching(const std::string& cacheDir, const uint8_t* token) {
120 if (mFinished) {
121 LOG(ERROR)
122 << "ANeuralNetworksCompilation_setCaching can't modify after compilation finished";
123 return ANEURALNETWORKS_BAD_STATE;
124 }
125 std::string path = cacheDir;
126 // Make sure the cache dir can concat with the filename.
127 if (!path.empty() && path.back() != '/') {
128 path.push_back('/');
129 }
130 mCacheInfo.variant = std::move(path);
131 std::copy(token, token + ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, mToken);
132 mIsCacheInfoProvided = true;
133 return ANEURALNETWORKS_NO_ERROR;
134 }
135
createCacheHandle(int fd)136 static GeneralResult<SharedHandle> createCacheHandle(int fd) {
137 std::vector<base::unique_fd> fds;
138 fds.push_back(NN_TRY(dupFd(fd)));
139 return std::make_shared<const Handle>(Handle{
140 .fds = std::move(fds),
141 .ints = {},
142 });
143 }
144
createCacheHandleVec(const int * fds,uint32_t numFds)145 static GeneralResult<std::vector<SharedHandle>> createCacheHandleVec(const int* fds,
146 uint32_t numFds) {
147 std::vector<SharedHandle> handles;
148 handles.reserve(numFds);
149 for (uint32_t i = 0; i < numFds; i++) {
150 handles.push_back(NN_TRY(createCacheHandle(fds[i])));
151 }
152 return handles;
153 }
154
setCachingFromFds(const int * modelCacheFds,const uint32_t numModelCacheFiles,const int * dataCacheFds,const uint32_t numDataCacheFiles,const uint8_t * token)155 int CompilationBuilder::setCachingFromFds(const int* modelCacheFds,
156 const uint32_t numModelCacheFiles,
157 const int* dataCacheFds, const uint32_t numDataCacheFiles,
158 const uint8_t* token) {
159 if (mFinished) {
160 LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't modify after "
161 "compilation finished";
162 return ANEURALNETWORKS_BAD_STATE;
163 }
164 auto modelCache = createCacheHandleVec(modelCacheFds, numModelCacheFiles);
165 if (!modelCache.has_value()) {
166 LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't duplicate model cache "
167 "fds: "
168 << modelCache.error().message;
169 return ANEURALNETWORKS_BAD_DATA;
170 }
171 auto dataCache = createCacheHandleVec(dataCacheFds, numDataCacheFiles);
172 if (!dataCache.has_value()) {
173 LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't duplicate data cache "
174 "fds: "
175 << dataCache.error().message;
176 return ANEURALNETWORKS_BAD_DATA;
177 }
178 mCacheInfo.variant = CacheHandles{
179 .modelCache = std::move(modelCache).value(),
180 .dataCache = std::move(dataCache).value(),
181 };
182 std::copy(token, token + ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, mToken);
183 mIsCacheInfoProvided = true;
184 return ANEURALNETWORKS_NO_ERROR;
185 }
186
setPriority(int32_t priority)187 int CompilationBuilder::setPriority(int32_t priority) {
188 if (mFinished) {
189 LOG(ERROR) << "ANeuralNetworksCompilation_setPriority can't modify after compilation "
190 "finished";
191 return ANEURALNETWORKS_BAD_STATE;
192 }
193 if (priority != ANEURALNETWORKS_PRIORITY_LOW && priority != ANEURALNETWORKS_PRIORITY_MEDIUM &&
194 priority != ANEURALNETWORKS_PRIORITY_HIGH) {
195 LOG(ERROR) << "ANeuralNetworksCompilation_setPriority invalid priority " << priority;
196 return ANEURALNETWORKS_BAD_DATA;
197 }
198
199 mPriority = priority;
200 return ANEURALNETWORKS_NO_ERROR;
201 }
202
setTimeoutDuration(uint64_t duration)203 int CompilationBuilder::setTimeoutDuration(uint64_t duration) {
204 if (mFinished) {
205 LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout can't modify after compilation "
206 "finished";
207 return ANEURALNETWORKS_BAD_STATE;
208 }
209 if (!mExplicitDeviceList || (mDevices.size() != 1)) {
210 LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout called on an "
211 "ANeuralNetworksCompilation that was not created by "
212 "ANeuralNetworksCompilation_createForDevices with numDevices = 1";
213 return ANEURALNETWORKS_BAD_DATA;
214 }
215 if (duration > 0) {
216 mTimeoutDuration = duration;
217 } else {
218 mTimeoutDuration.reset();
219 }
220 return ANEURALNETWORKS_NO_ERROR;
221 }
222
forTest_setPartitioning(uint32_t partitioning)223 int CompilationBuilder::forTest_setPartitioning(uint32_t partitioning) {
224 if (mFinished) {
225 LOG(ERROR) << "CompilationBuilder::forTest_setPartitioning can't modify after compilation "
226 "finished";
227 return ANEURALNETWORKS_BAD_STATE;
228 }
229
230 mPartitioning = partitioning;
231 return ANEURALNETWORKS_NO_ERROR;
232 }
233
forTest_failPartitioning(int fail)234 int CompilationBuilder::forTest_failPartitioning(int fail) {
235 if (mFinished) {
236 LOG(ERROR) << "CompilationBuilder::forTest_failPartitioning can't modify after compilation "
237 "finished";
238 return ANEURALNETWORKS_BAD_STATE;
239 }
240
241 mFailPartitioning = fail;
242 return ANEURALNETWORKS_NO_ERROR;
243 }
244
getPreferredMemoryAlignmentForInput(uint32_t index,uint32_t * alignment) const245 int CompilationBuilder::getPreferredMemoryAlignmentForInput(uint32_t index,
246 uint32_t* alignment) const {
247 CHECK(alignment != nullptr);
248 if (!mFinished) {
249 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
250 "unfinished compilation";
251 return ANEURALNETWORKS_BAD_STATE;
252 }
253 if (!mPlan.isValid()) {
254 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
255 "invalid compilation";
256 return ANEURALNETWORKS_BAD_STATE;
257 }
258 if (index >= mModel->inputCount()) {
259 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
260 "invalid input index "
261 << index;
262 return ANEURALNETWORKS_BAD_DATA;
263 }
264 *alignment = mPlan.getMemoryPreference(IOType::INPUT, index).alignment;
265 return ANEURALNETWORKS_NO_ERROR;
266 }
267
getPreferredMemoryPaddingForInput(uint32_t index,uint32_t * padding) const268 int CompilationBuilder::getPreferredMemoryPaddingForInput(uint32_t index, uint32_t* padding) const {
269 CHECK(padding != nullptr);
270 if (!mFinished) {
271 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
272 "unfinished compilation";
273 return ANEURALNETWORKS_BAD_STATE;
274 }
275 if (!mPlan.isValid()) {
276 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
277 "invalid compilation";
278 return ANEURALNETWORKS_BAD_STATE;
279 }
280 if (index >= mModel->inputCount()) {
281 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
282 "invalid input index "
283 << index;
284 return ANEURALNETWORKS_BAD_DATA;
285 }
286 *padding = mPlan.getMemoryPreference(IOType::INPUT, index).padding;
287 return ANEURALNETWORKS_NO_ERROR;
288 }
289
getPreferredMemoryAlignmentForOutput(uint32_t index,uint32_t * alignment) const290 int CompilationBuilder::getPreferredMemoryAlignmentForOutput(uint32_t index,
291 uint32_t* alignment) const {
292 CHECK(alignment != nullptr);
293 if (!mFinished) {
294 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
295 "unfinished compilation";
296 return ANEURALNETWORKS_BAD_STATE;
297 }
298 if (!mPlan.isValid()) {
299 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
300 "invalid compilation";
301 return ANEURALNETWORKS_BAD_STATE;
302 }
303 if (index >= mModel->outputCount()) {
304 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
305 "invalid output index "
306 << index;
307 return ANEURALNETWORKS_BAD_DATA;
308 }
309 *alignment = mPlan.getMemoryPreference(IOType::OUTPUT, index).alignment;
310 return ANEURALNETWORKS_NO_ERROR;
311 }
312
getPreferredMemoryPaddingForOutput(uint32_t index,uint32_t * padding) const313 int CompilationBuilder::getPreferredMemoryPaddingForOutput(uint32_t index,
314 uint32_t* padding) const {
315 CHECK(padding != nullptr);
316 if (!mFinished) {
317 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
318 "unfinished compilation";
319 return ANEURALNETWORKS_BAD_STATE;
320 }
321 if (!mPlan.isValid()) {
322 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
323 "invalid compilation";
324 return ANEURALNETWORKS_BAD_STATE;
325 }
326 if (index >= mModel->outputCount()) {
327 LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
328 "invalid output index "
329 << index;
330 return ANEURALNETWORKS_BAD_DATA;
331 }
332 *padding = mPlan.getMemoryPreference(IOType::OUTPUT, index).padding;
333 return ANEURALNETWORKS_NO_ERROR;
334 }
335
createExecution(ExecutionBuilder ** execution)336 int CompilationBuilder::createExecution(ExecutionBuilder** execution) {
337 if (!mFinished) {
338 LOG(ERROR) << "ANeuralNetworksExecution_create passed an unfinished compilation";
339 *execution = nullptr;
340 return ANEURALNETWORKS_BAD_STATE;
341 }
342 if (!mPlan.isValid()) {
343 LOG(ERROR) << "ANeuralNetworksExecution_create passed an invalid compilation";
344 *execution = nullptr;
345 return ANEURALNETWORKS_BAD_STATE;
346 }
347 if (mPlan.isSimple()) {
348 *execution = new (std::nothrow) SimpleExecutionBuilder(this);
349 } else {
350 *execution = new (std::nothrow) CompoundExecutionBuilder(this);
351 }
352 return (*execution ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
353 }
354
createBurst(BurstBuilder ** burst)355 int CompilationBuilder::createBurst(BurstBuilder** burst) {
356 if (!mFinished) {
357 LOG(ERROR) << "ANeuralNetworksBurst_create passed an unfinished compilation";
358 *burst = nullptr;
359 return ANEURALNETWORKS_BAD_STATE;
360 }
361 if (!mPlan.isValid()) {
362 LOG(ERROR) << "ANeuralNetworksBurst_create passed an invalid compilation";
363 *burst = nullptr;
364 return ANEURALNETWORKS_BAD_STATE;
365 }
366 std::vector<SharedBurst> burstControllers = mPlan.makeBursts();
367 *burst = new (std::nothrow) BurstBuilder(this, std::move(burstControllers));
368 return (*burst ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
369 }
370
forEachStepRoleOfInput(uint32_t index,const StepRoleCallback & callback) const371 int CompilationBuilder::forEachStepRoleOfInput(uint32_t index,
372 const StepRoleCallback& callback) const {
373 if (!mFinished) {
374 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an unfinished compilation";
375 return ANEURALNETWORKS_BAD_STATE;
376 }
377 if (!mPlan.isValid()) {
378 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid compilation";
379 return ANEURALNETWORKS_BAD_STATE;
380 }
381 if (index >= mModel->inputCount()) {
382 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid input index "
383 << index;
384 return ANEURALNETWORKS_BAD_DATA;
385 }
386 mPlan.forEachStepRoleOfInput(index, callback);
387 return ANEURALNETWORKS_NO_ERROR;
388 }
389
forEachStepRoleOfOutput(uint32_t index,const StepRoleCallback & callback) const390 int CompilationBuilder::forEachStepRoleOfOutput(uint32_t index,
391 const StepRoleCallback& callback) const {
392 if (!mFinished) {
393 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an unfinished compilation";
394 return ANEURALNETWORKS_BAD_STATE;
395 }
396 if (!mPlan.isValid()) {
397 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid compilation";
398 return ANEURALNETWORKS_BAD_STATE;
399 }
400 if (index >= mModel->outputCount()) {
401 LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid output index "
402 << index;
403 return ANEURALNETWORKS_BAD_DATA;
404 }
405 mPlan.forEachStepRoleOfOutput(index, callback);
406 return ANEURALNETWORKS_NO_ERROR;
407 }
408
409 } // namespace nn
410 } // namespace android
411