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