নিউরাল নেটওয়ার্কস এইচএএল ১.২ বার্স্ট এক্সিকিউশন-এর ধারণাটি প্রবর্তন করে। বার্স্ট এক্সিকিউশন হলো একই প্রস্তুতকৃত মডেলের একাধিক এক্সিকিউশনের একটি ক্রম যা দ্রুত গতিতে ঘটে, যেমন ক্যামেরা ক্যাপচারের ফ্রেম বা পরপর অডিও স্যাম্পলের উপর পরিচালিত এক্সিকিউশন। একগুচ্ছ বার্স্ট এক্সিকিউশন নিয়ন্ত্রণ করতে এবং এক্সিকিউশনগুলোর মধ্যে রিসোর্স সংরক্ষণ করতে একটি বার্স্ট অবজেক্ট ব্যবহৃত হয়, যা এক্সিকিউশনগুলোর ওভারহেড কমাতে সাহায্য করে। বার্স্ট অবজেক্ট তিনটি অপটিমাইজেশন সক্ষম করে:
- একটি বার্স্ট অবজেক্ট একাধিক কার্য সম্পাদনের ক্রম শুরু হওয়ার আগে তৈরি করা হয় এবং ক্রমটি শেষ হলে মুক্ত করা হয়। এই কারণে, বার্স্ট অবজেক্টের জীবনকাল ড্রাইভারকে ইঙ্গিত দেয় যে এটিকে কতক্ষণ উচ্চ-কর্মক্ষমতাসম্পন্ন অবস্থায় থাকতে হবে।
- একটি বার্স্ট অবজেক্ট একাধিক এক্সিকিউশনের মধ্যে রিসোর্স সংরক্ষণ করতে পারে। উদাহরণস্বরূপ, একটি ড্রাইভার প্রথম এক্সিকিউশনে একটি মেমরি অবজেক্ট ম্যাপ করতে পারে এবং পরবর্তী এক্সিকিউশনগুলোতে পুনরায় ব্যবহারের জন্য সেই ম্যাপিংটি বার্স্ট অবজেক্টে ক্যাশ করে রাখতে পারে। যেকোনো ক্যাশ করা রিসোর্স তখন মুক্ত করা যেতে পারে যখন বার্স্ট অবজেক্টটি ধ্বংস হয়ে যায় অথবা যখন NNAPI রানটাইম বার্স্ট অবজেক্টকে জানিয়ে দেয় যে রিসোর্সটির আর প্রয়োজন নেই।
- একটি বার্স্ট অবজেক্ট অ্যাপ এবং ড্রাইভার প্রসেসের মধ্যে যোগাযোগের জন্য ফাস্ট মেসেজ কিউ (FMQ) ব্যবহার করে। এটি ল্যাটেন্সি কমাতে পারে, কারণ FMQ, HIDL-কে বাইপাস করে এবং শেয়ার্ড মেমরিতে থাকা একটি অ্যাটমিক সার্কুলার FIFO-এর মাধ্যমে সরাসরি অন্য প্রসেসে ডেটা পাঠায়। কনজিউমার প্রসেসটি FIFO-তে থাকা উপাদানের সংখ্যা পোলিং করে অথবা প্রডিউসার দ্বারা সিগন্যাল করা FMQ-এর ইভেন্ট ফ্ল্যাগের জন্য অপেক্ষা করে একটি আইটেম ডিকিউ করতে এবং প্রসেসিং শুরু করতে পারে। এই ইভেন্ট ফ্ল্যাগটি একটি ফাস্ট ইউজারস্পেস মিউটেক্স (futex)।
FMQ হলো একটি নিম্ন-স্তরের ডেটা স্ট্রাকচার যা বিভিন্ন প্রসেসের মধ্যে এর স্থায়িত্বের কোনো নিশ্চয়তা দেয় না এবং FMQ-এর অপর প্রান্তের প্রসেসটি প্রত্যাশিতভাবে চলছে কিনা তা নির্ধারণ করার জন্য এর কোনো অন্তর্নির্মিত ব্যবস্থা নেই। ফলস্বরূপ, যদি FMQ-এর প্রডিউসার বন্ধ হয়ে যায়, তবে কনজিউমার এমন ডেটার জন্য অপেক্ষা করতে বাধ্য হতে পারে যা কখনোই আসে না। এই সমস্যার একটি সমাধান হলো, ড্রাইভারের মাধ্যমে FMQ-গুলোকে উচ্চ-স্তরের বার্স্ট অবজেক্টের সাথে যুক্ত করা, যাতে বার্স্ট এক্সিকিউশন কখন শেষ হয়েছে তা শনাক্ত করা যায়।
যেহেতু বার্স্ট এক্সিকিউশনগুলো অন্যান্য এক্সিকিউশন পাথের মতোই একই আর্গুমেন্টের উপর কাজ করে এবং একই ফলাফল প্রদান করে, তাই অন্তর্নিহিত FMQ-গুলোকে অবশ্যই NNAPI সার্ভিস ড্রাইভারের কাছে এবং তার থেকে একই ডেটা আদান-প্রদান করতে হয়। তবে, FMQ-গুলো শুধুমাত্র সাধারণ ডেটা টাইপ স্থানান্তর করতে পারে। জটিল ডেটা স্থানান্তর করা হয় সরাসরি FMQ-এর মধ্যেই নেস্টেড বাফার (ভেক্টর টাইপ) সিরিয়ালাইজ ও ডিসিরিয়ালাইজ করার মাধ্যমে এবং চাহিদা অনুযায়ী মেমরি পুল হ্যান্ডেল স্থানান্তরের জন্য HIDL কলব্যাক অবজেক্ট ব্যবহার করে। FMQ-এর প্রডিউসার সাইডকে অবশ্যই কনজিউমারের কাছে রিকোয়েস্ট বা রেজাল্ট মেসেজগুলো অ্যাটমিকভাবে পাঠাতে হবে; যদি কিউটি ব্লকিং হয় তবে MessageQueue::writeBlocking ব্যবহার করে, অথবা যদি কিউটি নন-ব্লকিং হয় তবে MessageQueue::write ব্যবহার করে।
বার্স্ট ইন্টারফেস
নিউরাল নেটওয়ার্কস HAL-এর বার্স্ট ইন্টারফেসগুলো hardware/interfaces/neuralnetworks/1.2/ ফোল্ডারে পাওয়া যায় এবং নিচে সেগুলোর বর্ণনা দেওয়া হলো। NDK লেয়ারের বার্স্ট ইন্টারফেস সম্পর্কে আরও তথ্যের জন্য frameworks/ml/nn/runtime/include/NeuralNetworks.h দেখুন।
প্রকার.হাল
types.hal FMQ-এর মাধ্যমে প্রেরিত ডেটার ধরণ নির্ধারণ করে।
-
FmqRequestDatum: একটি এক্সিকিউশনRequestঅবজেক্টের সিরিয়ালাইজড উপস্থাপনার একটি একক উপাদান এবং একটিMeasureTimingমান, যা ফাস্ট মেসেজ কিউ-এর মাধ্যমে পাঠানো হয়। -
FmqResultDatum: কোনো এক্সিকিউশন থেকে ফেরত আসা মানগুলোর (ErrorStatus,OutputShapes, এবংTiming) সিরিয়ালাইজড উপস্থাপনার একটি একক উপাদান, যা ফাস্ট মেসেজ কিউ-এর মাধ্যমে ফেরত দেওয়া হয়।
IBurstContext.hal
IBurstContext.hal নিউরাল নেটওয়ার্কস সার্ভিসে অবস্থিত HIDL ইন্টারফেস অবজেক্টটিকে সংজ্ঞায়িত করে।
-
IBurstContext: একটি বার্স্টের রিসোর্সসমূহ পরিচালনা করার জন্য ব্যবহৃত কনটেক্সট অবজেক্ট।
IBurstCallback.hal
IBurstCallback.hal নিউরাল নেটওয়ার্কস রানটাইম দ্বারা তৈরি একটি কলব্যাকের জন্য HIDL ইন্টারফেস অবজেক্ট সংজ্ঞায়িত করে এবং নিউরাল নেটওয়ার্কস সার্ভিস দ্বারা স্লট আইডেন্টিফায়ারগুলোর সাথে সম্পর্কিত hidl_memory অবজেক্টগুলো পুনরুদ্ধার করতে এটি ব্যবহৃত হয়।
- IBurstCallback : কোনো সার্ভিস দ্বারা মেমরি অবজেক্ট পুনরুদ্ধার করতে ব্যবহৃত কলব্যাক অবজেক্ট।
IPreparedModel.hal
HAL 1.2-এ IPreparedModel.hal একটি প্রিপেয়ার্ড মডেল থেকে IBurstContext অবজেক্ট তৈরি করার জন্য একটি মেথড দিয়ে সম্প্রসারিত করা হয়েছে।
-
configureExecutionBurst: একটি বার্স্ট অবজেক্ট কনফিগার করে যা একটি প্রস্তুত মডেলে দ্রুত পরপর একাধিক ইনফারেন্স সম্পাদন করতে ব্যবহৃত হয়।
ড্রাইভারে বার্স্ট এক্সিকিউশন সমর্থন করুন
একটি HIDL NNAPI সার্ভিসে বার্স্ট অবজেক্ট সমর্থন করার সবচেয়ে সহজ উপায় হলো ::android::nn::ExecutionBurstServer::create নামক বার্স্ট ইউটিলিটি ফাংশনটি ব্যবহার করা, যা ExecutionBurstServer.h ফাইলে পাওয়া যায় এবং libneuralnetworks_common ও libneuralnetworks_util স্ট্যাটিক লাইব্রেরির অন্তর্ভুক্ত। এই ফ্যাক্টরি ফাংশনটির দুটি ওভারলোড রয়েছে:
- একটি ওভারলোড একটি
IPreparedModelঅবজেক্টের পয়েন্টার গ্রহণ করে। এই ইউটিলিটি ফাংশনটি মডেলটি এক্সিকিউট করার জন্যIPreparedModelঅবজেক্টেরexecuteSynchronouslyমেথডটি ব্যবহার করে। - একটি ওভারলোড একটি কাস্টমাইজযোগ্য
IBurstExecutorWithCacheঅবজেক্ট গ্রহণ করে, যা একাধিক এক্সিকিউশন জুড়ে টিকে থাকা রিসোর্স (যেমনhidl_memoryম্যাপিং) ক্যাশ করতে ব্যবহার করা যেতে পারে।
প্রতিটি ওভারলোড একটি IBurstContext অবজেক্ট রিটার্ন করে (যা বার্স্ট অবজেক্টকে প্রতিনিধিত্ব করে) এবং এই অবজেক্টটি তার নিজস্ব ডেডিকেটেড লিসেনার থ্রেড ধারণ ও পরিচালনা করে। এই থ্রেডটি requestChannel FMQ থেকে অনুরোধ গ্রহণ করে, ইনফারেন্স সম্পাদন করে এবং তারপর resultChannel FMQ-এর মাধ্যমে ফলাফল ফেরত পাঠায়। যখন বার্স্টের ক্লায়েন্ট IBurstContext এর রেফারেন্স হারায়, তখন এই থ্রেড এবং IBurstContext অবজেক্টে থাকা অন্য সমস্ত রিসোর্স স্বয়ংক্রিয়ভাবে রিলিজ হয়ে যায়।
বিকল্পভাবে, আপনি IBurstContext এর নিজস্ব একটি ইমপ্লিমেন্টেশন তৈরি করতে পারেন, যা IPreparedModel::configureExecutionBurst এ পাঠানো requestChannel এবং resultChannel FMQ-গুলোর মাধ্যমে কীভাবে মেসেজ পাঠাতে ও গ্রহণ করতে হয় তা বোঝে।
বার্স্ট ইউটিলিটি ফাংশনগুলো ExecutionBurstServer.h ফাইলে পাওয়া যায়।
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param executorWithCache Object which maintains a local cache of the
* memory pools and executes using the cached memory pools.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(
const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
std::shared_ptr<IBurstExecutorWithCache> executorWithCache);
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param preparedModel PreparedModel that the burst object was created from.
* IPreparedModel::executeSynchronously will be used to perform the
* execution.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
IPreparedModel* preparedModel);
নিম্নলিখিতটি হলো frameworks/ml/nn/driver/sample/SampleDriver.cpp এ অবস্থিত নিউরাল নেটওয়ার্কস স্যাম্পল ড্রাইভারের একটি বার্স্ট ইন্টারফেসের রেফারেন্স ইমপ্লিমেন্টেশন।
Return<void> SamplePreparedModel::configureExecutionBurst(
const sp<V1_2::IBurstCallback>& callback,
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
configureExecutionBurst_cb cb) {
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
"SampleDriver::configureExecutionBurst");
// Alternatively, the burst could be configured via:
// const sp<V1_2::IBurstContext> burst =
// ExecutionBurstServer::create(callback, requestChannel,
// resultChannel, this);
//
// However, this alternative representation does not include a memory map
// caching optimization, and adds overhead.
const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
callback, requestChannel, resultChannel, executorWithCache);
if (burst == nullptr) {
cb(ErrorStatus::GENERAL_FAILURE, {});
} else {
cb(ErrorStatus::NONE, burst);
}
return Void();
}