HIDL Memory Block

HIDL MemoryBlock は、hidl_memoryHIDL @1.0::IAllocatorHIDL @1.0::IMapper に構築された抽象レイヤです。複数のメモリブロックが 1 つのメモリヒープを共有する HIDL サービス用に設計されています。

パフォーマンスの改善

アプリケーションで MemoryBlock を使用すると、mmapmunmap、ユーザー空間セグメンテーション違反の回数を大幅に削減し、パフォーマンスを向上できます。 例:

  • バッファ割り当てごとに hidl_memory を使用すると、平均して 238 us/1 割り当てになります。
  • MemoryBlock を使用して 1 つの hidl_memory を共有すると、平均して 2.82 us/1 割り当てになります。

アーキテクチャ

HIDL MemoryBlock アーキテクチャには、複数のメモリブロックが 1 つのメモリヒープを共有する HIDL サービスが含まれています。

HIDL MemoryBlock

図 1:HIDL MemoryBlock アーキテクチャ

通常使用

このセクションでは、まず HAL を宣言し、次に HAL を実装して、MemoryBlock を使用する例を示します。

HAL の宣言

次に IFoo HAL の例を示します。

import android.hidl.memory.block@1.0::MemoryBlock;

    interface IFoo {
        getSome() generates(MemoryBlock block);
        giveBack(MemoryBlock block);
    };
    

Android.bp は次のようになります。

hidl_interface {
        ...
        srcs: [
            "IFoo.hal",
        ],
        interfaces: [
            "android.hidl.memory.block@1.0",
            ...
    };
    

HAL の実装

上の例の HAL を実装するには、以下を実行します。

  1. hidl_memory を取得します(詳細については、HIDL C++をご覧ください)。

    #include <android/hidl/allocator/1.0/IAllocator.h>
    
        using ::android::hidl::allocator::V1_0::IAllocator;
        using ::android::hardware::hidl_memory;
        ...
          sp<IAllocator> allocator = IAllocator::getService("ashmem");
          allocator->allocate(2048, [&](bool success, const hidl_memory& mem)
          {
                if (!success) { /* error */ }
                // you can now use the hidl_memory object 'mem' or pass it
          }));
        
  2. 取得した hidl_memory を使用して HidlMemoryDealer を作成します。

    #include <hidlmemory/HidlMemoryDealer.h>
    
        using ::android::hardware::HidlMemoryDealer
        /* The mem argument is acquired in the Step1, returned by the ashmemAllocator->allocate */
        sp<HidlMemoryDealer> memory_dealer = HidlMemoryDealer::getInstance(mem);
        
  3. MemoryBlock を割り当てます。これは HIDL で定義された構造体です。

    MemoryBlock の例:

    struct MemoryBlock {
        IMemoryToken token;
        uint64_t size;
        uint64_t offset;
        };
        

    MemoryDealer を使用して MemoryBlock を割り当てる例:

    #include <android/hidl/memory/block/1.0/types.h>
    
        using ::android::hidl::memory::block::V1_0::MemoryBlock;
    
        Return<void> Foo::getSome(getSome_cb _hidl_cb) {
            MemoryBlock block = memory_dealer->allocate(1024);
            if(HidlMemoryDealer::isOk(block)){
                _hidl_cb(block);
            ...
        
  4. MemoryBlock の割り当てを解除します。

    Return<void> Foo::giveBack(const MemoryBlock& block) {
            memory_dealer->deallocate(block.offset);
        ...
        
  5. データの操作:

    #include <hidlmemory/mapping.h>
        #include <android/hidl/memory/1.0/IMemory.h>
    
        using ::android::hidl::memory::V1_0::IMemory;
    
        sp<IMemory> memory = mapMemory(block);
        uint8_t* data =
    
        static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
        
  6. Android.bp を構成します。

    shared_libs: [
                "android.hidl.memory@1.0",
    
                "android.hidl.memory.block@1.0"
    
                "android.hidl.memory.token@1.0",
                "libhidlbase",
                "libhidlmemory",
        
  7. フローを確認して lockMemory が必要かどうかを判断します。

    通常、MemoryBlock は参照カウントを使用して、初めて MemoryBlock の 1 つがマッピングされたときに mmap() され、どこからも参照されていないときに munmap() される共有 hidl_memory を維持します。常に hidl_memory がマッピングされるようにするには、lockMemory を使用できます。これは、ロック ライフサイクル全体を通じて対応する hidl_memory がマッピングされた状態を維持する、RAII スタイルのオブジェクトです。例:

    #include <hidlmemory/mapping.h>
    
        sp<RefBase> lockMemory(const sp<IMemoryToken> key);
        

拡張された使用方法

このセクションでは、MemoryBlock の拡張された使用方法の詳細について説明します。

参照カウントを使用した Memoryblock の管理

ほとんどの場合、MemoryBlock を使用する最も効率的な方法は、明示的に割り当てと割り当て解除を行うことです。しかし、複雑なアプリケーションでは、ガベージ コレクションに参照カウントを使用したほうが良い場合があります。MemoryBlock で参照カウントを使用するには、MemoryBlock をバインダー オブジェクトにバインドします。これにより、参照数がカウントされ、カウントがゼロになると MemoryBlock の割り当てが解除されます。

HAL の宣言

HAL を宣言する場合は、MemoryBlock と IBase を含む HIDL 構造体を記述します。

import android.hidl.memory.block@1.0::MemoryBlock;

    struct MemoryBlockAllocation {
        MemoryBlock block;
        IBase refcnt;
    };
    

MemoryBlockAllocation を使用して MemoryBlock を置き換え、メソッドを削除して MemoryBlock を返します。MemoryBlockAllocation を使用した参照カウントによって割り当てが解除されます。例:

interface IFoo {
        allocateSome() generates(MemoryBlockAllocation allocation);
    };
    

HAL の実装

HAL のサービス側の実装例:

class MemoryBlockRefCnt: public virtual IBase {
       MemoryBlockRefCnt(uint64_t offset, sp<MemoryDealer> dealer)
         : mOffset(offset), mDealer(dealer) {}
       ~MemoryBlockRefCnt() {
           mDealer->deallocate(mOffset);
       }
     private:
       uint64_t mOffset;
       sp<MemoryDealer> mDealer;
    };

    Return<void> Foo::allocateSome(allocateSome_cb _hidl_cb) {
        MemoryBlockAllocation allocation;
        allocation.block = memory_dealer->allocate(1024);
        if(HidlMemoryDealer::isOk(block)){
            allocation.refcnt= new MemoryBlockRefCnt(...);
            _hidl_cb(allocation);
    

HAL のクライアント側の実装例:

ifoo->allocateSome([&](const MemoryBlockAllocation& allocation){
        ...
    );
    

メタデータの追加と取得

一部のアプリケーションでは、割り当てられた MemoryBlock にバインドする追加のデータが必要です。 メタデータは、次の 2 つの方法で追加または取得できます。

  • アプリケーションがブロック自体と同じ頻度でメタデータにアクセスする場合は、メタデータを追加して構造体にすべて渡します。例:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
        struct MemoryBlockWithMetaData{
            MemoryBlock block;
            MetaDataStruct metaData;
        };
        
  • アプリケーションがブロックよりも頻繁にメタデータにアクセスしない場合、インターフェースでメタデータを受動的に渡す方が効率的です。例:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
        struct MemoryBlockWithMetaData{
            MemoryBlock block;
            IMetaData metaData;
        };
        

    次に、Memory Dealer を使用して MemoryBlock にメタデータをバインドします。例:

    MemoryBlockWithMetaData memory_block;
        memory_block.block = dealer->allocate(size);
        if(HidlMemoryDealer::isOk(block)){
            memory_block.metaData = new MetaData(...);