Seri işlem yürütmeler ve hızlı mesaj sıraları

Neural Networks HAL 1.2 sürümünde seri yürütme kavramı açıklanmaktadır. Seri çekim yürütme işlemleri, bir kamera yakalamasının kareleri veya arka arkaya ses örnekleri üzerinde çalışan işlemler gibi, aynı hazır modelin hızlı bir şekilde arka arkaya gerçekleştirilen bir dizi yürütmesidir. Seri işlem nesnesi, bir dizi seri işlem yürütmeyi kontrol etmek ve yürütmeler arasında kaynakları korumak için kullanılır. Böylece yürütme işlemlerinin daha düşük ek yükü olur. Seri çekim nesneleri üç optimizasyon sağlar:

  1. Bir dizi yürütme işleminden önce seri işlem nesnesi oluşturulur ve dizi sona erdiğinde serbest bırakılır. Bu nedenle, patlamaya tabi nesnelerin kullanım ömrü, sürücüye bunun yüksek performans durumunda ne kadar süre kalması gerektiğine dair ipuçları verir.
  2. Seri işlem nesnesi, yürütmeler arasında kaynakları koruyabilir. Örneğin, bir sürücü ilk yürütmede bir bellek nesnesini eşleyebilir ve sonraki yürütmelerde yeniden kullanılmak üzere seri işlem nesnesindeki eşlemeyi önbelleğe alabilir. Seri işlem yapan nesne imha edildiğinde veya NNAPI çalışma zamanı, seri işlem nesnesine artık kaynağın gerekli olmadığını bildirdiğinde önbelleğe alınan tüm kaynaklar serbest bırakılabilir.
  3. Seri çekim nesneleri, uygulama ile sürücü işlemleri arasında iletişim kurmak için hızlı mesaj sıralarını (FMQ'lar) kullanır. FMQ HIDL'yi atladığı ve paylaşılan bellekte atomik dairesel bir FIFO aracılığıyla verileri doğrudan başka bir işleme ilettiği için bu işlem gecikmeyi azaltabilir. Tüketici süreci, bir öğeyi sıraya koymayı bilir ve FIFO'daki öğe sayısını yoklayarak veya FMQ'nun yapımcı tarafından sinyal verilen etkinlik bayrağını bekleyerek işlemeye başlar. Bu etkinlik işareti, hızlı bir kullanıcı alanı karşılıklı dışlama işaretidir (futex).

FMQ, süreçler genelinde ömür boyu garanti sunmayan ve FMQ'nun diğer ucundaki sürecin beklendiği gibi çalışıp çalışmadığını belirlemeye yönelik yerleşik bir mekanizmaya sahip olmayan düşük seviyeli bir veri yapısıdır. Sonuç olarak, FMQ üreticisi ölürse tüketici hiç gelmeyen verileri beklerken tıkanabilir. Bu sorunun bir çözümü, sürücünün FMQ'ları üst seviye patlama nesnesiyle ilişkilendirerek seri çekimin ne zaman sona erdiğini algılayabilmesidir.

Seri işlem yürütmeleri aynı bağımsız değişkenler üzerinde çalıştığından ve diğer yürütme yollarıyla aynı sonuçları döndürdüğünden, temel FMQ'ların aynı verileri NNAPI hizmet sürücülerine ve bu sürücülerden iletmesi gerekir. Ancak, FMQ'lar yalnızca düz eski veri türlerini aktarabilir. Karmaşık verilerin aktarılması, iç içe yerleştirilmiş arabelleklerin (vektör türleri) doğrudan FMQ'larda serileştirilmesi ve seri dışı bırakılması ile ve isteğe bağlı olarak bellek havuzu tutma yerlerini aktarmak için HIDL geri çağırma nesnelerinin kullanılmasıyla gerçekleştirilir. FMQ'nun üretici tarafı, sıra engelliyorsa MessageQueue::writeBlocking, sıra engellenmiyorsa MessageQueue::write kullanarak isteği veya sonuç mesajlarını tüketiciye atomik olarak göndermelidir.

Seri çekim arayüzleri

Nöral Ağlar HAL'nin seri arayüzleri hardware/interfaces/neuralnetworks/1.2/ konumunda bulunur ve aşağıda açıklanmıştır. NDK katmanındaki seri çekim arayüzleri hakkında daha fazla bilgi için bkz. frameworks/ml/nn/runtime/include/NeuralNetworks.h.

türler.hal

types.hal, FMQ üzerinden gönderilen veri türünü tanımlar.

  • FmqRequestDatum: Hızlı mesaj sırası boyunca gönderilen, yürütme Request nesnesinin ve MeasureTiming değerinin serileştirilmiş temsilinin tek bir öğesi.
  • FmqResultDatum: Bir yürütmeden (ErrorStatus, OutputShapes ve Timing) döndürülen değerlerin serileştirilmiş temsilinin tek bir öğesidir ve hızlı mesaj sırasında döndürülür.

IBurstContext.hal

IBurstContext.hal, Nöral Ağlar hizmetinde bulunan HIDL arayüz nesnesini tanımlar.

  • IBurstContext: Bir seri çekimin kaynaklarını yönetmek için kullanılan bağlam nesnesi.

IBurstCallback.hal

IBurstCallback.hal, Sinir Ağları çalışma zamanı tarafından oluşturulan bir geri çağırma için HIDL arayüz nesnesini tanımlar ve Nöral Ağlar hizmeti tarafından alan tanımlayıcılarına karşılık gelen hidl_memory nesneleri almak için kullanılır.

  • IBurstCallback: Bir hizmet tarafından bellek nesnelerini almak için kullanılan geri çağırma nesnesi.

IPreparedModel.hal

IPreparedModel.hal, HAL 1.2'de, hazırlanmış bir modelden IBurstContext nesnesi oluşturma yöntemiyle genişletilir.

  • configureExecutionBurst: Hazırlanan bir model üzerinde hızla art arda birden fazla çıkarım yürütmek için kullanılan bir seri işlem nesnesini yapılandırır.

Bir sürücüde seri yürütme işlemlerini destekleme

Bir HIDL NNAPI hizmetinde seri işlem yapan nesneleri desteklemenin en basit yolu, ExecutionBurstServer.h içinde bulunan ve libneuralnetworks_common ile libneuralnetworks_util statik kitaplıklarında paketlenen ::android::nn::ExecutionBurstServer::create seri çekim yardımcı programını kullanmaktır. Bu fabrika işlevinde iki aşırı yükleme var:

  • Bir aşırı yükleme, bir IPreparedModel nesnesine işaretçiyi kabul ediyor. Bu yardımcı program işlevi, modeli yürütmek için IPreparedModel nesnesinde executeSynchronously yöntemini kullanır.
  • Bir aşırı yükleme, birden fazla yürütmede kalıcı olan kaynakları (hidl_memory eşlemeleri gibi) önbelleğe almak için kullanılabilen özelleştirilebilir bir IBurstExecutorWithCache nesnesini kabul eder.

Her aşırı yükleme, kendi özel işleyici iş parçacığını içeren ve yöneten bir IBurstContext nesnesi (seri işlem nesnesini temsil eder) döndürür. Bu ileti dizisi, requestChannel FMQ'dan istek alır, çıkarımı gerçekleştirir, ardından sonuçları resultChannel FMQ aracılığıyla döndürür. Bu iş parçacığı ve IBurstContext nesnesinde bulunan diğer tüm kaynaklar, seri çekimin istemcisi IBurstContext referansını kaybettiğinde otomatik olarak serbest bırakılır.

Alternatif olarak, IPreparedModel::configureExecutionBurst hizmetine iletilen requestChannel ve resultChannel FMQ'ları üzerinden nasıl mesaj gönderip alacağınızı anlayan kendi IBurstContext uygulamanızı oluşturabilirsiniz.

Seri işlem yardımcı program işlevleri ExecutionBurstServer.h içinde bulunabilir.

/**
 * 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);

Aşağıda, frameworks/ml/nn/driver/sample/SampleDriver.cpp adresindeki Nöral Ağlar örnek sürücüsünde bulunan bir seri işlem arayüzünün referans uygulaması verilmiştir.

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();
}