메모리 풀

이 페이지에서는 드라이버와 프레임워크 간에 피연산자 버퍼를 효율적으로 통신하는 데 사용되는 데이터 구조 및 메서드를 설명합니다.

모델 컴파일 시 프레임워크는 상수 피연산자의 값을 드라이버에 제공합니다. 상수 피연산자의 전체 기간에 따라 값은 HIDL 벡터 또는 공유 메모리 풀에 있습니다.

  • 전체 기간이 CONSTANT_COPY이면 값은 모델 구조의 operandValues 필드에 있습니다. HIDL 벡터의 값은 프로세스 간 통신(IPC) 중에 복사되므로 일반적으로 이 값은 스칼라 피연산자(예: ADD의 활성화 스칼라) 및 작은 텐서 매개변수(예: RESHAPE의 모양 텐서)와 같은 소량의 데이터를 보유하는 데만 사용됩니다.
  • 전체 기간이 CONSTANT_REFERENCE이면 값은 모델 구조의 pools 필드에 있습니다. 원시 값을 복사하는 대신 공유 메모리 풀의 핸들만 IPC 중에 복제됩니다. 따라서 HIDL 벡터보다 공유 메모리 풀을 사용하여 대량의 데이터(예: 컨볼루션의 가중치 매개변수)를 보유하는 것이 더 효율적입니다.

모델 실행 시 프레임워크는 입력 및 출력 피연산자의 버퍼를 드라이버에 제공합니다. HIDL 벡터로 전송될 수 있는 컴파일 타임 상수와 달리, 실행의 입력 및 출력 데이터는 항상 메모리 풀 컬렉션을 통해 전달됩니다.

HIDL 데이터 유형 hidl_memory는 컴파일과 실행 모두에서 매핑되지 않은 공유 메모리 풀을 표현하는 데 사용됩니다. 드라이버는 그에 따라 메모리를 매핑하여 hidl_memory 데이터 유형의 이름을 기반으로 메모리를 사용할 수 있도록 해야 합니다. 지원되는 메모리 이름은 다음과 같습니다.

  • ashmem: Android 공유 메모리입니다. 자세한 내용은 메모리를 참고하세요.
  • mmap_fd: mmap을 통해 파일 설명자가 지원하는 공유 메모리입니다.
  • hardware_buffer_blob: AHARDWARE_BUFFER_FORMAT_BLOB 형식의 AHardwareBuffer가 지원하는 공유 메모리입니다. NN(Neural Networks) HAL 1.2에서 사용할 수 있습니다. 자세한 내용은 AHardwareBuffer를 참고하세요.
  • hardware_buffer: AHARDWARE_BUFFER_FORMAT_BLOB 형식을 사용하지 않는 일반 AHardwareBuffer가 지원하는 공유 메모리입니다. 비 BLOB 모드 하드웨어 버퍼는 모델 실행에서만 지원됩니다. NN HAL 1.2에서 사용할 수 있습니다. 자세한 내용은 AHardwareBuffer를 참고하세요.

NN HAL 1.3부터 NNAPI는 드라이버 관리 버퍼의 할당자 인터페이스를 제공하는 메모리 도메인을 지원합니다. 드라이버 관리 버퍼는 실행 입력 또는 출력으로도 사용할 수 있습니다. 자세한 내용은 메모리 도메인을 참고하세요.

NNAPI 드라이버는 ashmemmmap_fd 메모리 이름 매핑을 지원해야 합니다. NN HAL 1.3부터 드라이버는 hardware_buffer_blob 매핑도 지원해야 합니다. 일반 비 BLOB 모드 hardware_buffer 및 메모리 도메인에 관한 지원은 선택사항입니다.

AHardwareBuffer

AHardwareBuffer는 Gralloc 버퍼를 래핑하는 공유 메모리 유형입니다. Android 10에서 Neural Networks API(NNAPI)는 AHardwareBuffer 사용을 지원합니다. 따라서 드라이버가 데이터를 복사하지 않고도 실행을 수행할 수 있어 앱의 성능과 전력 소비가 개선됩니다. 예를 들어 카메라 HAL 스택은 카메라 NDK 및 미디어 NDK API에 의해 생성된 AHardwareBuffer 핸들을 사용하여 머신러닝 워크로드용 NNAPI에 AHardwareBuffer 객체를 전달할 수 있습니다. 자세한 내용은 ANeuralNetworksMemory_createFromAHardwareBuffer를 참고하세요.

NNAPI에 사용되는 AHardwareBuffer 객체는 이름이 hardware_buffer 또는 hardware_buffer_blobhidl_memory 구조체를 통해 드라이버에 전달됩니다. hidl_memory 구조체 hardware_buffer_blobAHARDWAREBUFFER_FORMAT_BLOB 형식의 AHardwareBuffer 객체만 표현합니다.

프레임워크에 필요한 정보는 hidl_memory 구조체의 hidl_handle 필드에 인코딩됩니다. hidl_handle 필드는 AHardwareBuffer 또는 Gralloc 버퍼에 관한 필수 메타데이터를 모두 인코딩하는 native_handle을 래핑합니다.

드라이버는 제공된 hidl_handle 필드를 제대로 디코딩하고 hidl_handle에 의해 설명된 메모리에 액세스해야 합니다. getSupportedOperations_1_2, getSupportedOperations_1_1 또는 getSupportedOperations 메서드가 호출되면 드라이버는 제공된 hidl_handle을 디코딩하고 hidl_handle에 의해 설명된 메모리에 액세스할 수 있는지 감지해야 합니다. 상수 피연산자에 사용되는 hidl_handle 필드가 지원되지 않으면 모델 준비는 실패해야 합니다. 실행의 입력 또는 출력 피연산자에 사용되는 hidl_handle 필드가 지원되지 않으면 실행은 실패해야 합니다. 모델 준비 또는 실행이 실패하면 드라이버가 GENERAL_FAILURE 오류 코드를 반환하는 것이 좋습니다.

메모리 도메인

Android 11 이상을 실행하는 기기의 경우 NNAPI는 드라이버 관리 버퍼의 할당자 인터페이스를 제공하는 메모리 도메인을 지원합니다. 이를 통해 실행 전반에 걸쳐 기기 네이티브 메모리를 전달할 수 있으며, 동일한 드라이버에서 연속 실행 간에 불필요한 데이터 복사 및 변환을 억제할 수 있습니다. 이 흐름은 그림 1에 나와 있습니다.

메모리 도메인 유무에 따른 버퍼 데이터 흐름

그림 1. 메모리 도메인을 사용한 버퍼 데이터 흐름

메모리 도메인 기능은 일반적으로 드라이버 내부에 있고 클라이언트 측에서 자주 액세스할 필요가 없는 텐서용입니다. 이러한 텐서의 예로는 시퀀스 모델의 상태 텐서가 있습니다. 클라이언트 측에서 빈번한 CPU 액세스가 필요한 텐서의 경우 공유 메모리 풀을 사용하는 것이 좋습니다.

메모리 도메인 기능을 지원하려면 IDevice::allocate를 구현하여 프레임워크가 드라이버 관리 버퍼 할당을 요청할 수 있도록 합니다. 할당 중에 프레임워크는 버퍼에 대해 다음 속성 및 사용 패턴을 제공합니다.

  • BufferDesc는 버퍼의 필수 속성을 설명합니다.
  • BufferRole은 버퍼의 잠재적 사용 패턴을 준비된 모델의 입력 또는 출력으로 설명합니다. 버퍼 할당 중에 여러 역할을 지정할 수 있으며 할당된 버퍼는 지정된 역할로만 사용할 수 있습니다.

할당된 버퍼는 드라이버 내부에 있습니다. 드라이버는 버퍼 위치 또는 데이터 레이아웃을 선택할 수 있습니다. 버퍼가 성공적으로 할당되면 드라이버의 클라이언트는 반환된 토큰 또는 IBuffer 객체를 사용하여 버퍼를 참조하거나 버퍼와 상호작용할 수 있습니다.

IDevice::allocate의 토큰은 실행의 Request 구조에서 MemoryPool 객체 중 하나로 버퍼를 참조할 때 제공됩니다. 프로세스가 다른 프로세스에 할당된 버퍼에 액세스하지 못하도록 하려면 드라이버가 버퍼를 사용할 때마다 적절한 유효성 검사를 적용해야 합니다. 드라이버는 버퍼 사용이 할당 중에 제공된 BufferRole 역할 중 하나인지 확인해야 하며, 사용이 부적절한 경우 즉시 실행을 실패하게 만들어야 합니다.

IBuffer 객체는 명시적 메모리 복사에 사용됩니다. 특정 상황에서 드라이버의 클라이언트는 공유 메모리 풀에서 드라이버 관리 버퍼를 초기화하거나 버퍼를 공유 메모리 풀로 복사해야 합니다. 사용 사례의 예는 다음과 같습니다.

  • 상태 텐서 초기화
  • 중간 결과 캐싱
  • CPU에서 대체 실행

이러한 사용 사례를 지원하려면 드라이버가 메모리 도메인 할당을 지원하는 경우 ashmem, mmap_fdhardware_buffer_blob과 함께 IBuffer::copyToIBuffer::copyFrom을 구현해야 합니다. 드라이버가 비 BLOB 모드 hardware_buffer를 지원하는 것은 선택사항입니다.

버퍼 할당 중에 버퍼 크기는 BufferRole로 지정된 모든 역할의 상응하는 모델 피연산자 및 BufferDesc에 제공된 크기에서 추론할 수 있습니다. 모든 크기 관련 정보를 결합하면 버퍼에 알 수 없는 측정기준이나 순위가 있을 수 있습니다. 이 경우 버퍼는 모델 입력으로 사용될 때는 크기가 고정된 유연한 상태에 있으며 모델 출력으로 사용될 때는 동적 상태에 있습니다. 다양한 실행에서 다른 모양의 출력에 동일한 버퍼를 사용할 수 있으며, 드라이버는 버퍼 크기 조정을 적절하게 처리해야 합니다.

메모리 도메인은 선택적 기능입니다. 드라이버는 여러 이유로 특정 할당 요청을 지원할 수 없음을 파악할 수 있습니다. 예를 들면 다음과 같습니다.

  • 요청된 버퍼에 동적 크기가 있습니다.
  • 드라이버가 메모리 제약으로 인해 대규모 버퍼를 처리하지 못합니다.

여러 다른 스레드가 드라이버 관리 버퍼에서 동시에 읽을 수 있습니다. 쓰기 또는 읽기/쓰기를 위해 버퍼에 동시에 액세스하는 것은 정의되어 있지 않지만 드라이버 서비스를 비정상 종료하거나 호출자를 무기한으로 차단해서는 안 됩니다. 드라이버는 오류를 반환하거나 버퍼의 콘텐츠를 미확정 상태로 둘 수 있습니다.