HIDL MemoryBlock は、hidl_memory
、HIDL
@1.0::IAllocator
、HIDL @1.0::IMapper
上に構築された抽象レイヤです。複数のメモリブロックが 1 つのメモリヒープを共有する HIDL サービス用に設計されています。
パフォーマンスの改善
アプリで MemoryBlock を使用すると、mmap
/ munmap
とユーザー空間セグメンテーション違反の数を大幅に削減して、パフォーマンスを改善できます。次に例を示します。
- バッファ割り当てごとに
hidl_memory
を使用すると、平均して 238 us/1 割り当てになります。 MemoryBlock
を使用して 1 つのhidl_memory
を共有すると、平均して 2.82 us/1 割り当てになります。
アーキテクチャ
HIDL MemoryBlock アーキテクチャには、複数のメモリブロックが 1 つのメモリヒープを共有する HIDL サービスが含まれています。
図 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 を実装するには、以下を実行します。
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 }));
取得した
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);
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); ...
MemoryBlock
の割り当てを解除します。Return<void> Foo::giveBack(const MemoryBlock& block) { memory_dealer->deallocate(block.offset); ...
データの操作:
#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()));
Android.bp
を構成します。shared_libs: [ "android.hidl.memory@1.0", "android.hidl.memory.block@1.0" "android.hidl.memory.token@1.0", "libhidlbase", "libhidlmemory",
フローを確認して
lockMemory
が必要かどうかを判断します。通常、MemoryBlock は参照カウントを使用して、共有
hidl_memory
を管理します。これは、初めてMemoryBlock
のいずれかがマッピングされたときにmmap()
でマップされ、どこからも参照されていないときにmunmap()
でマップ解除されます。常に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(...);