Thực thi hàng loạt và hàng đợi tin nhắn nhanh

Neural Networks HAL 1.2 giới thiệu khái niệm về các lần thực thi hàng loạt. Thực thi hàng loạt là một chuỗi quá trình thực thi cùng một mô hình đã chuẩn bị diễn ra liên tiếp và nhanh chóng, chẳng hạn như quá trình thực thi hoạt động trên các khung hình của một lần chụp máy ảnh/máy quay hoặc mẫu âm thanh liên tiếp. Đối tượng bắn hàng loạt được dùng để kiểm soát một tập hợp các lần thực thi bắn hàng loạt và để bảo tồn tài nguyên giữa các lần thực thi, cho phép các lần thực thi có mức hao tổn thấp hơn. Đối tượng Burst hỗ trợ 3 tính năng tối ưu hoá:

  1. Đối tượng bắn được tạo trước một trình tự thực thi và được giải phóng khi trình tự kết thúc. Do đó, vòng đời của đối tượng tăng tốc sẽ gợi ý cho trình điều khiển thời lượng mà đối tượng đó sẽ ở trạng thái hiệu suất cao.
  2. Đối tượng bắn hàng loạt có thể giữ lại tài nguyên giữa các lần thực thi. Ví dụ: trình điều khiển có thể liên kết một đối tượng bộ nhớ trong lần thực thi đầu tiên và lưu liên kết vào bộ nhớ đệm trong đối tượng burst để sử dụng lại trong các lần thực thi tiếp theo. Mọi tài nguyên được lưu vào bộ nhớ đệm đều có thể được giải phóng khi đối tượng bắn phá bị huỷ hoặc khi thời gian chạy NNAPI thông báo cho đối tượng bắn phá rằng tài nguyên đó không còn cần thiết nữa.
  3. Đối tượng bắn phá sử dụng hàng đợi tin nhắn nhanh (FMQ) để giao tiếp giữa các quy trình ứng dụng và trình điều khiển. Điều này có thể giảm độ trễ vì FMQ bỏ qua HIDL và truyền dữ liệu trực tiếp đến một quy trình khác thông qua FIFO vòng tròn nguyên tử trong bộ nhớ dùng chung. Quy trình của người dùng biết cách xoá một mục khỏi hàng đợi và bắt đầu xử lý bằng cách thăm dò số lượng phần tử trong FIFO hoặc bằng cách chờ cờ sự kiện của FMQ do nhà sản xuất báo hiệu. Cờ sự kiện này là một mutex không gian người dùng nhanh (futex).

FMQ là một cấu trúc dữ liệu cấp thấp không đảm bảo thời gian hoạt động trên các quy trình và không có cơ chế tích hợp để xác định xem quy trình ở đầu kia của FMQ có đang chạy như dự kiến hay không. Do đó, nếu trình tạo của FMQ bị lỗi, thì trình tiêu thụ có thể bị treo khi chờ dữ liệu không bao giờ đến. Một giải pháp cho vấn đề này là trình điều khiển liên kết FMQ với đối tượng burst cấp cao hơn để phát hiện thời điểm quá trình thực thi burst kết thúc.

Vì các lần thực thi hàng loạt hoạt động trên cùng một đối số và trả về cùng một kết quả như các đường dẫn thực thi khác, nên các FMQ cơ bản phải truyền cùng một dữ liệu đến và từ trình điều khiển dịch vụ NNAPI. Tuy nhiên, FMQ chỉ có thể chuyển các loại dữ liệu cũ. Việc chuyển dữ liệu phức tạp được thực hiện bằng cách chuyển đổi tuần tự và huỷ chuyển đổi tuần tự các vùng đệm lồng nhau (các loại vectơ) ngay trong FMQ và sử dụng các đối tượng gọi lại HIDL để chuyển các tay điều khiển nhóm bộ nhớ theo yêu cầu. Phía nhà sản xuất của FMQ phải gửi yêu cầu hoặc thông báo kết quả đến trình tiêu thụ một cách nguyên tử bằng cách sử dụng MessageQueue::writeBlocking nếu hàng đợi đang chặn hoặc bằng cách sử dụng MessageQueue::write nếu hàng đợi không chặn.

Giao diện chụp ảnh liên tục

Bạn có thể tìm thấy các giao diện truyền dữ liệu theo luồng cho HAL Mạng nơron trong hardware/interfaces/neuralnetworks/1.2/ và được mô tả bên dưới. Để biết thêm thông tin về giao diện chụp nhanh trong lớp NDK, hãy xem frameworks/ml/nn/runtime/include/NeuralNetworks.h.

types.hal

types.hal xác định loại dữ liệu được gửi qua FMQ.

  • FmqRequestDatum: Một phần tử duy nhất của bản trình bày tuần tự của đối tượng Request thực thi và giá trị MeasureTiming, được gửi qua hàng đợi thông báo nhanh.
  • FmqResultDatum: Một phần tử duy nhất của bản trình bày tuần tự các giá trị được trả về từ một quá trình thực thi (ErrorStatus, OutputShapesTiming), được trả về thông qua hàng đợi thông báo nhanh.

IBurstContext.hal

IBurstContext.hal xác định đối tượng giao diện HIDL nằm trong dịch vụ Mạng nơron.

  • IBurstContext: Đối tượng ngữ cảnh để quản lý tài nguyên của một luồng.

IBurstCallback.hal

IBurstCallback.hal xác định đối tượng giao diện HIDL cho lệnh gọi lại do môi trường thời gian chạy Neural Networks tạo ra và được dịch vụ Neural Networks sử dụng để truy xuất các đối tượng hidl_memory tương ứng với giá trị nhận dạng khe.

  • IBurstCallback: Đối tượng gọi lại mà dịch vụ sử dụng để truy xuất đối tượng bộ nhớ.

IPreparedModel.hal

IPreparedModel.hal được mở rộng trong HAL 1.2 bằng một phương thức để tạo đối tượng IBurstContext từ mô hình đã chuẩn bị.

  • configureExecutionBurst: Định cấu hình đối tượng burst dùng để thực thi nhiều suy luận trên một mô hình đã chuẩn bị liên tiếp nhanh chóng.

Hỗ trợ các lần thực thi hàng loạt trong trình điều khiển

Cách đơn giản nhất để hỗ trợ các đối tượng bắn trong dịch vụ HIDL NNAPI là sử dụng hàm tiện ích bắn ::android::nn::ExecutionBurstServer::create có trong ExecutionBurstServer.h và được đóng gói trong thư viện tĩnh libneuralnetworks_commonlibneuralnetworks_util. Hàm nhà máy này có hai phương thức nạp chồng:

  • Một phương thức nạp chồng chấp nhận con trỏ đến đối tượng IPreparedModel. Hàm tiện ích này sử dụng phương thức executeSynchronously trong đối tượng IPreparedModel để thực thi mô hình.
  • Một phương thức nạp chồng chấp nhận đối tượng IBurstExecutorWithCache có thể tuỳ chỉnh, có thể dùng để lưu các tài nguyên vào bộ nhớ đệm (chẳng hạn như các mối liên kết hidl_memory) tồn tại trên nhiều lần thực thi.

Mỗi phương thức nạp chồng sẽ trả về một đối tượng IBurstContext (đại diện cho đối tượng đợt phát) chứa và quản lý luồng trình nghe chuyên dụng của riêng nó. Luồng này nhận yêu cầu từ FMQ requestChannel, thực hiện suy luận, sau đó trả về kết quả thông qua FMQ resultChannel. Luồng này và tất cả tài nguyên khác có trong đối tượng IBurstContext sẽ tự động được phát hành khi ứng dụng của luồng bị mất tham chiếu đến IBurstContext.

Ngoài ra, bạn có thể tạo cách triển khai IBurstContext của riêng mình để hiểu cách gửi và nhận tin nhắn qua FMQ requestChannelresultChannel được truyền đến IPreparedModel::configureExecutionBurst.

Bạn có thể tìm thấy các hàm tiện ích của luồng dữ liệu trong ExecutionBurstServer.h.

/**
 * Create automated context to manage FMQ-based executions.
 *
 * This function is intended to be used by a service to automatically:
 * 1) Receive data from a provided FMQ
 * 2) Execute a model with the given information
 * 3) Send the result to the created FMQ
 *
 * @param callback Callback used to retrieve memories corresponding to
 *     unrecognized slots.
 * @param requestChannel Input FMQ channel through which the client passes the
 *     request to the service.
 * @param resultChannel Output FMQ channel from which the client can retrieve
 *     the result of the execution.
 * @param executorWithCache Object which maintains a local cache of the
 *     memory pools and executes using the cached memory pools.
 * @result IBurstContext Handle to the burst context.
 */
static sp<ExecutionBurstServer> create(
        const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
        const FmqResultDescriptor& resultChannel,
        std::shared_ptr<IBurstExecutorWithCache> executorWithCache);

/**
 * Create automated context to manage FMQ-based executions.
 *
 * This function is intended to be used by a service to automatically:
 * 1) Receive data from a provided FMQ
 * 2) Execute a model with the given information
 * 3) Send the result to the created FMQ
 *
 * @param callback Callback used to retrieve memories corresponding to
 *     unrecognized slots.
 * @param requestChannel Input FMQ channel through which the client passes the
 *     request to the service.
 * @param resultChannel Output FMQ channel from which the client can retrieve
 *     the result of the execution.
 * @param preparedModel PreparedModel that the burst object was created from.
 *     IPreparedModel::executeSynchronously will be used to perform the
 *     execution.
 * @result IBurstContext Handle to the burst context.
 */
  static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
                                         const FmqRequestDescriptor& requestChannel,
                                         const FmqResultDescriptor& resultChannel,
                                         IPreparedModel* preparedModel);

Sau đây là cách triển khai tham chiếu của giao diện truyền dữ liệu theo luồng trong trình điều khiển mẫu Mạng nơron tại frameworks/ml/nn/driver/sample/SampleDriver.cpp.

Return<void> SamplePreparedModel::configureExecutionBurst(
        const sp<V1_2::IBurstCallback>& callback,
        const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
        const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
        configureExecutionBurst_cb cb) {
    NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
                 "SampleDriver::configureExecutionBurst");
    // Alternatively, the burst could be configured via:
    // const sp<V1_2::IBurstContext> burst =
    //         ExecutionBurstServer::create(callback, requestChannel,
    //                                      resultChannel, this);
    //
    // However, this alternative representation does not include a memory map
    // caching optimization, and adds overhead.
    const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
            std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
    const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
            callback, requestChannel, resultChannel, executorWithCache);
    if (burst == nullptr) {
        cb(ErrorStatus::GENERAL_FAILURE, {});
    } else {
        cb(ErrorStatus::NONE, burst);
    }
    return Void();
}