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_memory
for each buffer allocation averages 238 us/1 allocation. - Using
MemoryBlock
and sharing a singlehidl_memory
averages 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 [email protected]::MemoryBlock;
interface IFoo {
getSome() generates(MemoryBlock block);
giveBack(MemoryBlock block);
};
The Android.bp
is as follows:
hidl_interface {
...
srcs: [
"IFoo.hal",
],
interfaces: [
"[email protected]",
...
};
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
HidlMemoryDealer
instance 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
MemoryDealer
to 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: [ "[email protected]", "[email protected]" "[email protected]", "libhidlbase", "libhidlmemory",
Review the flow to determine if you need to
lockMemory
.Normally,
MemoryBlock
uses reference count to maintain the sharedhidl_memory
which ismmap()
-ed the first time one of itsMemoryBlock instances is mapped and is
munmap()-ed when nothing refers to it. To keep
hidl_memoryalways mapped, you can use
lockMemory, a RAII style object that keeps the corresponding
hidl_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 [email protected]::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 [email protected]::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 [email protected]::MemoryBlock; struct MemoryBlockWithMetaData{ MemoryBlock block; IMetaData metaData; };
Next, bind the metadata with the
MemoryBlock
usingMemoryDealer
. Example:MemoryBlockWithMetaData memory_block; memory_block.block = dealer->allocate(size); if(HidlMemoryDealer::isOk(block)){ memory_block.metaData = new MetaData(...);