Bloc mémoire HIDL

Le HIDL MemoryBlock est une couche abstraite construite sur hidl_memory , HIDL @1.0::IAllocator et HIDL @1.0::IMapper . Il est conçu pour les services HIDL disposant de plusieurs blocs de mémoire pour partager un seul tas de mémoire.

Amélioration des performances

L'utilisation de MemoryBlock dans les applications peut réduire considérablement le nombre de défauts de segmentation mmap / munmap et de l'espace utilisateur, améliorant ainsi les performances. Par exemple:

  • L'utilisation par hidl_memory pour chaque allocation de tampon équivaut en moyenne à 238 us/1 allocation.
  • L'utilisation MemoryBlock et le partage d'un seul hidl_memory équivaut en moyenne à une allocation de 2,82 us/1.

Architecture

L'architecture HIDL MemoryBlock comprend des services HIDL avec plusieurs blocs de mémoire partageant un seul tas de mémoire :

Bloc mémoire HIDL

Figure 1. Architecture HIDL MemoryBlock

Utilisation normale

Cette section fournit un exemple d'utilisation de MemoryBlock en déclarant d'abord le HAL, puis en implémentant le HAL.

Déclarer le HAL

Pour l'exemple suivant IFoo HAL :

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

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

Le Android.bp est le suivant :

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

Implémentation du HAL

Pour implémenter l'exemple HAL :

  1. Obtenez le hidl_memory (pour plus de détails, reportez-vous à 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. Créez un HidlMemoryDealer avec le hidl_memory acquis :

    #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. Allouez MemoryBlock , qui est une structure définie avec HIDL.

    Exemple MemoryBlock :

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

    Exemple utilisant le MemoryDealer pour allouer un 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. Désallouer MemoryBlock :

    Return<void> Foo::giveBack(const MemoryBlock& block) {
        memory_dealer->deallocate(block.offset);
    ...
    
  5. Manipulez les données :

    #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. Configuration Android.bp :

    shared_libs: [
            "android.hidl.memory@1.0",
    
            "android.hidl.memory.block@1.0"
    
            "android.hidl.memory.token@1.0",
            "libhidlbase",
            "libhidlmemory",
    
  7. Examinez le flux pour déterminer si vous devez lockMemory .

    Normalement, le MemoryBlock utilise le nombre de références pour conserver la hidl_memory partagée qui est mmap() -ed la première fois qu'un de ses MemoryBlock est mappé et est munmap() -ed lorsque rien n'y fait référence. Pour que le hidl_memory toujours mappé, vous pouvez utiliser lockMemory , un objet de style RAII qui conserve le hidl_memory correspondant mappé tout au long du cycle de vie du verrou. Exemple:

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

Utilisation étendue

Cette section fournit des détails sur l'utilisation étendue de MemoryBlock .

Utiliser le nombre de références pour gérer Memoryblock

Dans la plupart des situations, le moyen le plus efficace d’utiliser MemoryBlock consiste à allouer/désallouer explicitement. Cependant, dans les applications complexes, l'utilisation du nombre de références pour le garbage collection peut s'avérer une meilleure idée. Pour avoir un nombre de références sur MemoryBlock, vous pouvez lier MemoryBlock avec un objet binder, ce qui permet de compter les références et de libérer le MemoryBlock lorsque le nombre diminue jusqu'à zéro.

Déclarer le HAL

Lors de la déclaration du HAL, décrivez une structure HIDL qui contient un MemoryBlock et un IBase :

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

struct MemoryBlockAllocation {
    MemoryBlock block;
    IBase refcnt;
};

Utilisez MemoryBlockAllocation pour remplacer MemoryBlock et supprimez la méthode pour restituer le MemoryBlock . Il sera libéré par comptage de références avec MemoryBlockAllocation . Exemple:

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

Implémentation du HAL

Exemple de mise en œuvre côté service de 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);

Exemple d'implémentation côté client du HAL :

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

Attacher/récupérer des métadonnées

Certaines applications ont besoin de données supplémentaires pour se lier au MemoryBlock alloué. Vous pouvez ajouter/récupérer des métadonnées à l'aide de deux méthodes :

  • Si l'application accède aux métadonnées aussi souvent que le bloc lui-même, ajoutez les métadonnées et transmettez-les toutes dans une structure. Exemple:

    import android.hidl.memory.block@1.0::MemoryBlock;
    
    struct MemoryBlockWithMetaData{
        MemoryBlock block;
        MetaDataStruct metaData;
    };
    
  • Si l'application accède aux métadonnées beaucoup moins fréquemment que le bloc, il est plus efficace de transmettre passivement les métadonnées avec une interface. Exemple:

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

    Ensuite, liez les métadonnées avec le MemoryBlock à l’aide du Memory Dealer. Exemple:

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