The HIDL MemoryBlock is an abstract layer built on hidl_memory, HIDL
@1.0::IAllocator, and HIDL @1.0::IMapper. It is designed for HIDL services
that have multiple memory blocks to share a single memory heap.
Performance improvements
Using MemoryBlock in apps can significantly reduce the number of
mmap/munmap and user space segmentation faults, thus improving performance.
For example:
- Using per
hidl_memoryfor each buffer allocation averages 238 us/1 allocation. - Using
MemoryBlockand sharing a singlehidl_memoryaverages 2.82 us/1 allocation.
Architecture
The HIDL MemoryBlock architecture includes HIDL services with multiple memory
blocks sharing a single memory heap:
Figure 1. HIDL MemoryBlock architecture
Normal usage
This section provides an example of using MemoryBlock by first declaring the
HAL then implementing the HAL.
Declare the HAL
For the following example IFoo HAL:
import android.hidl.memory.block@1.0::MemoryBlock;
interface IFoo {
getSome() generates(MemoryBlock block);
giveBack(MemoryBlock block);
};
The Android.bp is as follows:
hidl_interface {
...
srcs: [
"IFoo.hal",
],
interfaces: [
"android.hidl.memory.block@1.0",
...
};
Implement the HAL
To implement the example HAL:
Get the
hidl_memory(for details, refer to 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 }));Make a
HidlMemoryDealerinstance with the acquiredhidl_memory:#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);Allocate
MemoryBlock, which is a struct defined with HIDL.Example
MemoryBlock:struct MemoryBlock { IMemoryToken token; uint64_t size; uint64_t offset; };Example using the
MemoryDealerto allocate aMemoryBlock:#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); ...Deallocate
MemoryBlock:Return<void> Foo::giveBack(const MemoryBlock& block) { memory_dealer->deallocate(block.offset); ...Manipulate the data:
#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()));Config
Android.bp:shared_libs: [ "android.hidl.memory@1.0", "android.hidl.memory.block@1.0" "android.hidl.memory.token@1.0", "libhidlbase", "libhidlmemory",Review the flow to determine if you need to
lockMemory.Normally,
MemoryBlockuses reference count to maintain the sharedhidl_memorywhich ismmap()-ed the first time one of itsMemoryBlock instances is mapped and ismunmap()-ed when nothing refers to it. To keephidl_memoryalways mapped, you can uselockMemory, a RAII style object that keeps the correspondinghidl_memory` mapped throughout the lock lifecycle. Example:#include <hidlmemory/mapping.h> sp<RefBase> lockMemory(const sp<IMemoryToken> key);
Extended usage
This section provides details about the extended usage of MemoryBlock.
Use reference count to manage MemoryBlock
In most situations, the most efficient way to use MemoryBlock is to explicitly
allocate/deallocate. However, in complicated apps using reference count
for garbage collection might be a better idea. To have reference count on
MemoryBlock, you can bind MemoryBlock with a binder object, which helps to
count the references and deallocate the MemoryBlock when the count decreases
to zero.
Declare the HAL
When declaring the HAL, describe a HIDL struct that contains a MemoryBlock
instance and an IBase:
import android.hidl.memory.block@1.0::MemoryBlock;
struct MemoryBlockAllocation {
MemoryBlock block;
IBase refcnt;
};
Use MemoryBlockAllocation to replace MemoryBlock and remove the method
to give back MemoryBlock. It's deallocated by reference counting
with MemoryBlockAllocation. Example:
interface IFoo {
allocateSome() generates(MemoryBlockAllocation allocation);
};
Implement the HAL
Example of the service side implementation of the 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);
Example of the client side implementation of the HAL:
ifoo->allocateSome([&](const MemoryBlockAllocation& allocation){
...
);
Attach and retrieve metadata
Some apps need additional data to bind with the allocated MemoryBlock.
You can append and retrieve metadata using two methods:
If the app accesses the metadata as often as the block itself, append the metadata and pass them all in a struct. Example:
import android.hidl.memory.block@1.0::MemoryBlock; struct MemoryBlockWithMetaData{ MemoryBlock block; MetaDataStruct metaData; };If the app accesses the metadata much less frequently than the block, it is more efficient to pass the metadata passively with an interface. Example:
import android.hidl.memory.block@1.0::MemoryBlock; struct MemoryBlockWithMetaData{ MemoryBlock block; IMetaData metaData; };Next, bind the metadata with the
MemoryBlockusingMemoryDealer. Example:MemoryBlockWithMetaData memory_block; memory_block.block = dealer->allocate(size); if(HidlMemoryDealer::isOk(block)){ memory_block.metaData = new MetaData(...);