• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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