[API] Implement decoupled APIs for network inferencing
authorDongju Chae <dongju.chae@samsung.com>
Tue, 15 Jun 2021 04:39:21 +0000 (13:39 +0900)
committer채동주/On-Device Lab(SR)/Staff Engineer/삼성전자 <dongju.chae@samsung.com>
Fri, 18 Jun 2021 05:00:54 +0000 (14:00 +0900)
This patch implements decoupled APIs for network inferencing.
It allows for users to configure their requests before submission.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
12 files changed:
include/host/libnpuhost.h
src/core/ne-handler.cc
src/core/ne-handler.h
src/core/ne-host-input-service.cc
src/core/ne-hw-input-service.cc
src/core/ne-inputservice.h
src/core/ne-scheduler.cc
src/core/ne-scheduler.h
src/core/npu/NPUdrvAPI_triv2.cc
src/host/ne-host.cc
tests/unittests/ne_core_sched_test.cc
tests/unittests/ne_libnpuhost_test.cc

index 28ebf38..3374cfc 100644 (file)
@@ -496,7 +496,8 @@ int writeNPU_log (npu_loglevel level, const char *tag, const char *format, ...);
  * @param[in] model_id The model to be inferred.
  * @param[out] req_id The ID of created request
  * @return 0 if no error. Otherwise a negative errno
- * @note the created request is not submitted until runNPU_request is called
+ * @note The created request is not submitted until runNPU_request is called.
+ *       Also, it's automatically destroyed when submitted.
  */
 int createNPU_request (npudev_h dev, uint32_t model_id, int *req_id);
 
@@ -538,7 +539,7 @@ int setNPU_requestCallback (npudev_h dev, int req_id, npuOutputNotify cb,
 int setNPU_requestMode (npudev_h dev, int req_id, npu_infer_mode mode);
 
 /**
- * @brief [OPTIONAL] Set the request's inference mode
+ * @brief [OPTIONAL] Set the request's constraint
  * @param[in] dev The NPU device handle
  * @param[in] req_id The request ID
  * @param[in] constraint inference constraint (e.g., timeout, priority)
index 9cffcd9..6b0efb2 100644 (file)
@@ -299,6 +299,95 @@ class callbackSync {
 };
 
 /**
+ * @brief Create NPU inferance request
+ * @param[in] model_id The model to be inferred.
+ * @param[out] req_id The ID of created request
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::createRequest (uint32_t model_id, int *req_id) {
+  Model *model = getModel (model_id);
+  if (model == nullptr) {
+    logerr (TAG, "Unable to find a model with model id (0x%x)\n", model_id);
+    return -ENOENT;
+  }
+
+  return device_->createRequest (model, req_id);
+}
+
+/**
+ * @brief Set request's input/output data
+ * @param[in] req_id The request ID
+ * @param[in] input The input data buffers
+ * @param[in] in_info The input data info (format, type)
+ * @param[in] output The output data buffers
+ * @param[in] out_info The output data info (format, type)
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::setRequestData (int req_id, input_buffers *input,
+                             tensors_data_info *in_info, output_buffers *output,
+                             tensors_data_info *out_info) {
+  return device_->setRequestData (req_id, input, in_info, output, out_info);
+}
+
+/**
+ * @brief Set output callback of the request
+ * @param[in] req_id The request ID
+ * @param[in] cb The output callback handler
+ * @param[in] [nullable] data The data to pass to callback handler
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::setRequestCallback (int req_id, npuOutputNotify cb, void *data) {
+  return device_->setRequestCallback (req_id, cb, data);
+}
+
+/**
+ * @brief Set the request's inference mode
+ * @param[in] req_id The request ID
+ * @param[in] mode Configures how this inference works.
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::setRequestMode (int req_id, npu_infer_mode mode) {
+  return device_->setRequestMode (req_id, mode);
+}
+
+/**
+ * @brief Set the request's constraint
+ * @param[in] dev The NPU device handle
+ * @param[in] req_id The request ID
+ * @param[in] constraint inference constraint (e.g., timeout, priority)
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::setRequestConstraint (int req_id, npu_constraint constraint) {
+  return device_->setRequestConstraint (req_id, constraint);
+}
+
+/**
+ * @brief Set the request's VD NPU manager parameter
+ * @param[in] req_id The request ID
+ * @param[in] param npumgr parameter
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::setRequestNpumgrParam (int req_id, npumgr_param param) {
+  return device_->setRequestNpumgrParam (req_id, param);
+}
+
+/**
+ * @brief Submit the request to the NPU
+ * @param[in] req_id The request ID
+ * @return 0 if no error. Otherwise a negative errno
+ */
+int
+HostHandler::submitRequest (int req_id) {
+  return device_->submitRequest (req_id);
+}
+
+/**
  * @brief Execute inference.
  * @param[in] model_id The model id to be inferred
  * @param[in] mode Configures how this inference works.
@@ -994,7 +1083,8 @@ TrinityVision2::run (npu_input_opmode opmode, const Model *model,
     }
   }
 
-  Request *req = new Request (opmode);
+  Request *req = scheduler_->createRequest (opmode);
+
   req->setModel (model);
   req->setInferData (segt);
   req->setOutputBuffers (output);
@@ -1023,7 +1113,8 @@ TrinityVision2::runInternal (npu_input_opmode opmode, const Model *model,
     return -EINVAL;
   }
 
-  Request *req = new Request (opmode);
+  Request *req = scheduler_->createRequest (opmode);
+
   req->setModel (model);
   req->setInferData (segt);
   req->setHwDevice (hw_dev);
@@ -1105,6 +1196,179 @@ TrinityVision2::callback (Request *req, npuOutputNotify cb, void *cb_data) {
   delete segt;
 }
 
+int
+TrinityVision2::createRequest (const Model *model, int *req_id) {
+  Request *req;
+
+  if (model == nullptr || req_id == nullptr) {
+    logerr (TAG, "Invalid arguments detected\n");
+    return -EINVAL;
+  }
+
+  req = scheduler_->createRequest (NPUINPUT_HOST);
+  req->setModel (model);
+
+  *req_id = req->getID ();
+
+  return 0;
+}
+
+int
+TrinityVision2::setRequestData (int req_id, input_buffers *input,
+                                tensors_data_info *in_info,
+                                output_buffers *output,
+                                tensors_data_info *out_info) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  const Model *model = req->getModel ();
+  if (model == nullptr) {
+    logerr (TAG, "Unable to find the target model\n");
+    return -EINVAL;
+  }
+
+  if (input == nullptr || in_info == nullptr || output == nullptr ||
+      out_info == nullptr) {
+    logerr (TAG, "Invalid arguments detected\n");
+    return -EINVAL;
+  }
+
+  /* FIXME: should be per request, not per model */
+  const_cast<Model *> (model)->setDataInfo (in_info, out_info);
+
+  /** this device uses segment table */
+  SegmentTable *segt = prepareSegmentTable (model, input, output);
+  if (segt == nullptr) {
+    logerr (TAG, "Failed to create segment table instance\n");
+    return -EINVAL;
+  }
+
+  /** extract input data */
+  for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
+    if (!segt->getInputSegment (idx)->isExternal ()) {
+      uint32_t seg_offset = segt->getInputSegmentOffset (idx);
+      auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
+                             std::placeholders::_1, std::placeholders::_2,
+                             std::placeholders::_3);
+      int status = comm_.extractGenericBuffer (
+          &input->bufs[idx],
+          segt->getInputSegment (idx)->getData () + seg_offset, func);
+      if (status != 0) {
+        logerr (TAG, "Failed to feed input segment: %d\n", status);
+        return status;
+      }
+    }
+  }
+
+  req->setInferData (segt);
+  req->setOutputBuffers (output);
+
+  return 0;
+}
+
+int
+TrinityVision2::setRequestCallback (int req_id, npuOutputNotify cb,
+                                    void *data) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, data));
+
+  return 0;
+}
+
+int
+TrinityVision2::setRequestMode (int req_id, npu_infer_mode mode) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  req->setInferMode (mode);
+
+  return 0;
+}
+
+int
+TrinityVision2::setRequestConstraint (int req_id, npu_constraint constraint) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  const Model *model = req->getModel ();
+  if (model == nullptr) {
+    logerr (TAG, "Unable to find the target model\n");
+    return -EINVAL;
+  }
+
+  /* FIXME: should be per request, not per model */
+  const_cast<Model *> (model)->setConstraint (constraint);
+
+  return 0;
+}
+
+int
+TrinityVision2::setRequestNpumgrParam (int req_id, npumgr_param param) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  req->setNpumgrParam (param);
+
+  return 0;
+}
+
+int
+TrinityVision2::submitRequest (int req_id) {
+  Request *req = scheduler_->findRequest (req_id);
+  if (req == nullptr) {
+    logerr (TAG, "Unable to find the request with ID (%d)\n", req_id);
+    return -ENOENT;
+  }
+
+  int status = -EINVAL;
+
+  switch (req->getInferMode ()) {
+    case NPU_INFER_BLOCKING: {
+      callbackSync sync;
+
+      if (req->getCallback () != nullptr)
+        logwarn (TAG, "Blocking call don't use user callback...\n");
+
+      req->setCallback (std::bind (&TrinityVision2::callback, this, req,
+                                   (npuOutputNotify) callbackSync::callback,
+                                   static_cast<void *> (&sync)));
+
+      status = scheduler_->submitRequest (req);
+      if (status >= 0)
+        sync.wait ();
+    } break;
+    case NPU_INFER_NON_BLOCKING:
+      if (req->getCallback () == nullptr) {
+        logwarn (TAG, "inferencing without output callback...\n");
+        req->setCallback (
+            std::bind (&TrinityVision2::callback, this, req, nullptr, nullptr));
+      }
+      status = scheduler_->submitRequest (req);
+      break;
+    default:
+      logerr (TAG, "Unknown inference mode\n");
+  }
+
+  return status;
+}
+
 /** Implement data manipulation (each device may have different impl.) */
 
 #ifdef ENABLE_MANIP
index 63df3ec..fb18e23 100644 (file)
@@ -63,6 +63,16 @@ class HostHandler {
                    std::string hw_dev);
   int stopInternal (int id);
 
+  int createRequest (uint32_t model_id, int *req_id);
+  int setRequestData (int req_id, input_buffers *input,
+                      tensors_data_info *in_info, output_buffers *output,
+                      tensors_data_info *out_info);
+  int setRequestCallback (int req_id, npuOutputNotify cb, void *data);
+  int setRequestMode (int req_id, npu_infer_mode mode);
+  int setRequestConstraint (int req_id, npu_constraint constraint);
+  int setRequestNpumgrParam (int req_id, npumgr_param param);
+  int submitRequest (int req_id);
+
   /** @brief get statistics */
   int getMemoryStatus (size_t *alloc_total, size_t *free_total);
   int getDeviceStatus (npu_status *status, uint32_t *num_requests);
@@ -138,6 +148,18 @@ class Device {
     return -EPERM;
   }
 
+  virtual int createRequest (const Model *model, int *req_id) = 0;
+  virtual int setRequestData (int req_id, input_buffers *input,
+                              tensors_data_info *in_info,
+                              output_buffers *output,
+                              tensors_data_info *out_info) = 0;
+  virtual int setRequestCallback (int req_id, npuOutputNotify cb,
+                                  void *data) = 0;
+  virtual int setRequestMode (int req_id, npu_infer_mode mode) = 0;
+  virtual int setRequestConstraint (int req_id, npu_constraint constraint) = 0;
+  virtual int setRequestNpumgrParam (int req_id, npumgr_param param) = 0;
+  virtual int submitRequest (int req_id) = 0;
+
  protected:
   /** the device instance has ownership of all related components */
   std::unique_ptr<DriverAPI> api_;       /**< device api */
@@ -188,6 +210,16 @@ class TrinityVision2 : public Device {
   int runInternal (npu_input_opmode opmode, const Model *model,
                    std::string hw_dev);
 
+  int createRequest (const Model *model, int *req_id);
+  int setRequestData (int req_id, input_buffers *input,
+                      tensors_data_info *in_info, output_buffers *output,
+                      tensors_data_info *out_info);
+  int setRequestCallback (int req_id, npuOutputNotify cb, void *data);
+  int setRequestMode (int req_id, npu_infer_mode mode);
+  int setRequestConstraint (int req_id, npu_constraint constraint);
+  int setRequestNpumgrParam (int req_id, npumgr_param param);
+  int submitRequest (int req_id);
+
  private:
   void callback (Request *req, npuOutputNotify cb, void *cb_data);
 };
index 4eb9aea..483a190 100644 (file)
@@ -29,19 +29,20 @@ HostInputService::getInstance () {
 
 int
 HostInputService::submit (const DriverAPI *api, int id, const Model *model,
-                          HWmem *data, outputCallback callback) {
+                          HWmem *data, outputCallback callback,
+                          const npumgr_param *param) {
   if (api == nullptr)
     return -EINVAL;
 
   if (dynamic_cast<Buffer *> (data)) {
     /* empty model is possible */
     return submit_buffer (api, id, model, dynamic_cast<Buffer *> (data),
-                          callback);
+                          callback, param);
   } else if (dynamic_cast<SegmentTable *> (data)) {
     if (model == nullptr)
       return -EINVAL;
     return submit_segt (api, id, model, dynamic_cast<SegmentTable *> (data),
-                        callback);
+                        callback, param);
   } else {
     return -EINVAL;
   }
@@ -59,9 +60,10 @@ HostInputService::submit (const DriverAPI *api, int id, const Model *model,
 int
 HostInputService::submit_buffer (const DriverAPI *api, int id,
                                  const Model *model, Buffer *buffer,
-                                 outputCallback callback) {
+                                 outputCallback callback,
+                                 const npumgr_param *param) {
   taskFunc func = std::bind (&HostInputService::invoke_buffer, this, api, model,
-                             buffer, callback, id);
+                             buffer, callback, id, param);
   ThreadTask *task = new ThreadTask (id, func);
 
   return ThreadPool::getInstance ().enqueueTask (task);
@@ -78,9 +80,10 @@ HostInputService::submit_buffer (const DriverAPI *api, int id,
  */
 int
 HostInputService::submit_segt (const DriverAPI *api, int id, const Model *model,
-                               SegmentTable *segt, outputCallback callback) {
+                               SegmentTable *segt, outputCallback callback,
+                               const npumgr_param *param) {
   taskFunc func = std::bind (&HostInputService::invoke_segt, this, api, model,
-                             segt, callback, id);
+                             segt, callback, id, param);
   ThreadTask *task = new ThreadTask (id, func);
 
   return ThreadPool::getInstance ().enqueueTask (task);
@@ -108,7 +111,7 @@ HostInputService::remove (int id) {
 int
 HostInputService::invoke_buffer (const DriverAPI *api, const Model *model,
                                  Buffer *buffer, outputCallback callback,
-                                 int req_id) {
+                                 int req_id, const npumgr_param *param) {
   input_config_t input_config;
   device_state_t state;
   int ret = -EINVAL;
@@ -164,7 +167,7 @@ handle_callback:
 int
 HostInputService::invoke_segt (const DriverAPI *api, const Model *model,
                                SegmentTable *segt, outputCallback callback,
-                               int req_id) {
+                               int req_id, const npumgr_param *param) {
   input_config_t input_config;
   device_state_t state;
   npuConstraint constraint;
@@ -186,6 +189,15 @@ HostInputService::invoke_segt (const DriverAPI *api, const Model *model,
     goto handle_callback;
   }
 
+  /** npumgr parameter setting if exists */
+  if (param != nullptr) {
+    input_config.task_handle = param->task_handle;
+    input_config.subtask_idx = param->subtask_idx;
+  } else {
+    input_config.task_handle = UINT32_MAX;
+    input_config.subtask_idx = UINT32_MAX;
+  }
+
   input_config.model_id = model->getInternalID ();
   input_config.dbuf_fd = segt->getDmabuf ();
   input_config.num_segments = segt->getNumTotalSegments ();
index 1b39fcd..f37c69a 100644 (file)
@@ -40,7 +40,8 @@ HwInputService::getInstance () {
  */
 int
 HwInputService::submit (const DriverAPI *api, int id, const Model *model,
-                        HWmem *data, outputCallback callback) {
+                        HWmem *data, outputCallback callback,
+                        const npumgr_param *param) {
   if (api == nullptr || model == nullptr)
     return -EINVAL;
 
@@ -48,7 +49,7 @@ HwInputService::submit (const DriverAPI *api, int id, const Model *model,
   if (segt == nullptr)
     return -EINVAL;
 
-  return invoke (api, model, segt, callback, id);
+  return invoke (api, model, segt, callback, id, param);
 }
 
 /**
@@ -61,8 +62,8 @@ HwInputService::submit (const DriverAPI *api, int id, const Model *model,
  */
 int
 HwInputService::invoke (const DriverAPI *api, const Model *model,
-                        SegmentTable *segt, outputCallback callback,
-                        int req_id) {
+                        SegmentTable *segt, outputCallback callback, int req_id,
+                        const npumgr_param *param) {
   input_config_t input_config;
   device_state_t state;
   npuConstraint constraint;
@@ -106,6 +107,15 @@ HwInputService::invoke (const DriverAPI *api, const Model *model,
   input_config.hw_input_seg = model->getMetadata ()->getInputSegmentIndex (0);
   input_config.hw_output_seg = model->getMetadata ()->getOutputSegmentIndex (0);
 
+  /** npumgr parameter setting if exists */
+  if (param != nullptr) {
+    input_config.task_handle = param->task_handle;
+    input_config.subtask_idx = param->subtask_idx;
+  } else {
+    input_config.task_handle = UINT32_MAX;
+    input_config.subtask_idx = UINT32_MAX;
+  }
+
 #ifndef ENABLE_EMUL
   struct stat devnode_stat;
   int fd;
index 73b526b..e97f076 100644 (file)
@@ -31,7 +31,8 @@ class InputService {
  public:
   /** @brief submit request to somewhere (depends on each impl.) */
   virtual int submit (const DriverAPI *api, int request_id, const Model *model,
-                      HWmem *data, outputCallback callback = nullptr) {
+                      HWmem *data, outputCallback callback = nullptr,
+                      const npumgr_param *param = nullptr) {
     return -EPERM;
   }
   /** @brief [OPTIONAL] remove the submitted request (if possible) */
@@ -49,20 +50,25 @@ class HostInputService : public InputService {
   static HostInputService &getInstance ();
 
   int submit (const DriverAPI *api, int request_id, const Model *model,
-              HWmem *data, outputCallback callback = nullptr);
+              HWmem *data, outputCallback callback = nullptr,
+              const npumgr_param *param = nullptr);
   int remove (int request_id);
 
  private:
   int submit_buffer (const DriverAPI *api, int request_id, const Model *model,
-                     Buffer *buffer, outputCallback callback = nullptr);
+                     Buffer *buffer, outputCallback callback = nullptr,
+                     const npumgr_param *param = nullptr);
   int submit_segt (const DriverAPI *api, int request_id, const Model *model,
-                   SegmentTable *segt, outputCallback callback = nullptr);
+                   SegmentTable *segt, outputCallback callback = nullptr,
+                   const npumgr_param *param = nullptr);
 
   /** do not allow to directly call invoke () */
   int invoke_buffer (const DriverAPI *api, const Model *model, Buffer *buffer,
-                     outputCallback callback, int task_id);
+                     outputCallback callback, int task_id,
+                     const npumgr_param *param);
   int invoke_segt (const DriverAPI *api, const Model *model, SegmentTable *segt,
-                   outputCallback callback, int task_id);
+                   outputCallback callback, int task_id,
+                   const npumgr_param *param);
 
   static std::unique_ptr<HostInputService> instance_;
   static std::once_flag once_flag_;
@@ -74,12 +80,14 @@ class HwInputService : public InputService {
   static HwInputService &getInstance ();
 
   int submit (const DriverAPI *api, int request_id, const Model *model,
-              HWmem *data, outputCallback callback = nullptr);
+              HWmem *data, outputCallback callback = nullptr,
+              const npumgr_param *param = nullptr);
 
  private:
   /** do not allow to directly call invoke () */
   int invoke (const DriverAPI *api, const Model *model, SegmentTable *segt,
-              outputCallback callback, int task_id);
+              outputCallback callback, int task_id,
+              const npumgr_param *param = nullptr);
 
   static std::unique_ptr<HwInputService> instance_;
   static std::once_flag once_flag_;
index db3fd75..eb666e4 100644 (file)
@@ -15,6 +15,7 @@
 #include "ne-inputservice.h"
 
 #include <assert.h>
+#include <unistd.h>
 
 #define TAG _N3
 
@@ -28,7 +29,9 @@ Request::Request (npu_input_opmode opmode)
       model_ (nullptr),
       data_ (nullptr),
       cb_ (nullptr),
-      out_bufs_ (nullptr) {
+      out_bufs_ (nullptr),
+      infer_mode_ (NPU_INFER_BLOCKING /* default */),
+      npumgr_param_ (nullptr) {
   request_id_ = Request::global_request_id_.fetch_add (1);
 }
 
@@ -100,15 +103,12 @@ Scheduler::handleStop (Request *req) {
 int
 Scheduler::handleHostInput (Request *req, InputService *service) {
   int req_id = req->getID ();
-  int status = request_map_.insert (req_id, req);
-  assert (status ==
-          0); /** request ID is atomic value. So, should be successful */
-
   const Model *model = req->getModel ();
   HWmem *data = req->getInferData ();
   auto callback = std::bind (&Scheduler::handleCallback, this, req);
+  const npumgr_param *param = req->getNpumgrParam ();
 
-  status = service->submit (api_, req_id, model, data, callback);
+  int status = service->submit (api_, req_id, model, data, callback, param);
   if (status < 0)
     return status;
 
@@ -125,10 +125,6 @@ Scheduler::handleHostInput (Request *req, InputService *service) {
 int
 Scheduler::handleHwInput (Request *req, InputService *service) {
   int req_id = req->getID ();
-  int status = request_map_.insert (req_id, req);
-  assert (status ==
-          0); /** request ID is atomic value. So, should be successful */
-
   const Model *model = req->getModel ();
   HWmem *data = req->getInferData ();
   auto callback = std::bind (&Scheduler::handleCallback, this, req);
@@ -144,9 +140,33 @@ Scheduler::handleHwInput (Request *req, InputService *service) {
 }
 
 /**
+ * @brief create a request to be scheduled
+ * @param[in] opmode operation mode
+ * @return new request instance
+ */
+Request *
+Scheduler::createRequest (npu_input_opmode opmode) {
+  Request *req = new Request (opmode);
+  /** request ID is atomic value. So, should be successful */
+  assert (request_map_.insert (req->getID (), req) == 0);
+  return req;
+}
+
+/**
+ * @brief remove the request submitted
+ * @param[in] req the request instance
+ */
+void
+Scheduler::removeRequest (Request *req) {
+  if (req)
+    request_map_.remove (req->getID ());
+}
+
+/**
  * @brief submit request to inference engine
  * @param[in] req the request instance
  * @return 0 or positive value if no error. otherwise a negative errno.
+ * @note the request is destroyed only when its submission is sucessful.
  */
 int
 Scheduler::submitRequest (Request *req) {
@@ -162,26 +182,19 @@ Scheduler::submitRequest (Request *req) {
 
   npu_input_opmode opmode = req->getOpmode ();
   InputService *service = getInputService (opmode);
-  int status = 0;
 
   switch (opmode) {
     case NPUINPUT_STOP:
       return handleStop (req);
     case NPUINPUT_HOST:
-      status = handleHostInput (req, service);
+      return handleHostInput (req, service);
       break;
     case NPUINPUT_HW_RECURRING:
-      status = handleHwInput (req, service);
+      return handleHwInput (req, service);
       break;
     default:
       return -EINVAL;
   }
-
-  /** if failed to invoke input service, directly handle callback (if exists) */
-  if (status < 0)
-    handleCallback (req);
-
-  return status;
 }
 
 /**
@@ -195,5 +208,15 @@ Scheduler::handleCallback (Request *req) {
     callback ();
 
   /** the request instance is also deleted here */
-  request_map_.remove (req->getID ());
+  removeRequest (req);
+}
+
+/**
+ * @brief find request using id
+ * @return the target request if exists. Otherwise nullptr
+ */
+Request *
+Scheduler::findRequest (int req_id) {
+  /* thread-safe map */
+  return request_map_.find (req_id);
 }
index 2f6b3f4..896fb53 100644 (file)
@@ -28,6 +28,7 @@
 #include <atomic>
 
 /** @brief class def. of requests created from host handler */
+
 class Request {
  public:
   Request (npu_input_opmode opmode);
@@ -56,6 +57,14 @@ class Request {
   void setOutputBuffers (output_buffers *out_bufs) { out_bufs_ = out_bufs; }
   output_buffers *getOutputBuffers () { return out_bufs_; }
 
+  void setInferMode (npu_infer_mode infer_mode) { infer_mode_ = infer_mode; }
+  npu_infer_mode getInferMode () { return infer_mode_; }
+
+  void setNpumgrParam (const npumgr_param &param) {
+    memcpy (npumgr_param_, &param, sizeof (npumgr_param));
+  }
+  const npumgr_param *getNpumgrParam () { return npumgr_param_; }
+
  private:
   static std::atomic<int> global_request_id_;
   int request_id_; /**< request id */
@@ -70,6 +79,9 @@ class Request {
   outputCallback cb_;        /**< request callback */
   output_buffers *out_bufs_; /**< output buffers */
   std::string hw_dev_;       /**< HW device path */
+
+  npu_infer_mode infer_mode_;
+  npumgr_param *npumgr_param_;
 };
 
 /** @brief class def. of scheduler to handle requests */
@@ -79,7 +91,10 @@ class Scheduler {
   ~Scheduler ();
 
   /** @brief submit the request */
+  Request *createRequest (npu_input_opmode opmode);
+  void removeRequest (Request *req);
   int submitRequest (Request *req);
+  Request *findRequest (int req_id);
 
  private:
   /**
index 8279d51..a670a7b 100644 (file)
@@ -542,12 +542,6 @@ TrinityVision2API::runInput (input_config_t *input_config) const {
     this->munmap (segt, PAGE_SIZE);
   }
 
-  /**
-   * TODO: this value is an indicator whether to use VD NPU Scheduler.
-   * When VD NPU manager is available, set this value properly.
-   */
-  input_config->task_handle = UINT32_MAX;
-
   ret = ioctl (this->getDeviceFD (), TRINITY_IOCTL_RUN_INPUT, input_config);
   if (ret < 0)
     IOCTL_RETURN_ERRNO (TRINITY_IOCTL_RUN_INPUT);
index 1092fbd..9f8d501 100644 (file)
@@ -736,8 +736,9 @@ writeNPU_log (npu_loglevel level, const char *tag, const char *format, ...) {
  */
 int
 createNPU_request (npudev_h dev, uint32_t model_id, int *req_id) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->createRequest (model_id, req_id);
 }
 
 /**
@@ -757,8 +758,10 @@ int
 setNPU_requestData (npudev_h dev, int req_id, input_buffers *input,
                     tensors_data_info *in_info, output_buffers *output,
                     tensors_data_info *out_info) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->setRequestData (req_id, input, in_info, output,
+                                       out_info);
 }
 
 /**
@@ -772,8 +775,9 @@ setNPU_requestData (npudev_h dev, int req_id, input_buffers *input,
 int
 setNPU_requestCallback (npudev_h dev, int req_id, npuOutputNotify cb,
                         void *data) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->setRequestCallback (req_id, cb, data);
 }
 
 /**
@@ -785,12 +789,13 @@ setNPU_requestCallback (npudev_h dev, int req_id, npuOutputNotify cb,
  */
 int
 setNPU_requestMode (npudev_h dev, int req_id, npu_infer_mode mode) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->setRequestMode (req_id, mode);
 }
 
 /**
- * @brief [OPTIONAL] Set the request's inference mode
+ * @brief [OPTIONAL] Set the request's constraint
  * @param[in] dev The NPU device handle
  * @param[in] req_id The request ID
  * @param[in] constraint inference constraint (e.g., timeout, priority)
@@ -799,8 +804,9 @@ setNPU_requestMode (npudev_h dev, int req_id, npu_infer_mode mode) {
  */
 int
 setNPU_requestConstraint (npudev_h dev, int req_id, npu_constraint constraint) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->setRequestConstraint (req_id, constraint);
 }
 
 /**
@@ -812,8 +818,9 @@ setNPU_requestConstraint (npudev_h dev, int req_id, npu_constraint constraint) {
  */
 int
 setNPU_requestNpumgrParam (npudev_h dev, int req_id, npumgr_param param) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->setRequestNpumgrParam (req_id, param);
 }
 
 /**
@@ -824,6 +831,7 @@ setNPU_requestNpumgrParam (npudev_h dev, int req_id, npumgr_param param) {
  */
 int
 submitNPU_request (npudev_h dev, int req_id) {
-  /* NYI */
-  return -EPERM;
+  INIT_HOST_HANDLER (host_handler, dev);
+
+  return host_handler->submitRequest (req_id);
 }
index 37d905f..3f2e667 100644 (file)
  * @brief test set/get primitives of request
  */
 TEST (ne_core_sched_test, request_set_get_primitives) {
+  std::unique_ptr<DriverAPI> api;
+  api = DriverAPI::createDriverAPI (NPUCOND_TRIV2_CONN_SOCIP, 0);
+  ASSERT_NE (api.get (), nullptr);
+
   /** create dummy model & buffer */
   std::unique_ptr<Model> model (new Model (new HWmemDevice));
   std::unique_ptr<Buffer> buffer (new Buffer (new HWmemDevice));
@@ -28,7 +32,9 @@ TEST (ne_core_sched_test, request_set_get_primitives) {
   auto callback = std::bind (test_callback, &num_called, &m, &cv);
   bool force_stop = true;
 
-  Request *req = new Request (NPUINPUT_HOST);
+  Scheduler *sched = new Scheduler (api.get ());
+  Request *req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
   EXPECT_EQ (req->getOpmode (), NPUINPUT_HOST);
   EXPECT_GT (req->getID (), (uint32_t) 0);
 
@@ -48,9 +54,15 @@ TEST (ne_core_sched_test, request_set_get_primitives) {
  * @brief test set, get api with null value
  */
 TEST (ne_core_sched_test, request_set_get_args_n) {
+  std::unique_ptr<DriverAPI> api;
+  api = DriverAPI::createDriverAPI (NPUCOND_TRIV2_CONN_SOCIP, 0);
+  ASSERT_NE (api.get (), nullptr);
+
   bool force_stop = false;
   std::string hwDevice = "";
-  Request *req = new Request (NPUINPUT_HOST);
+  Scheduler *sched = new Scheduler (api.get ());
+  Request *req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
   EXPECT_EQ (req->getOpmode (), NPUINPUT_HOST);
   EXPECT_GT (req->getID (), (uint32_t) 0);
 
@@ -92,24 +104,26 @@ TEST (ne_core_sched_test, submit_request) {
   std::mutex m;
   auto callback = std::bind (test_callback, &num_called, &m, &cv);
 
-  Request *req = new Request (NPUINPUT_HOST);
+  Scheduler *sched = new Scheduler (api.get ());
+
+  Request *req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
   req->setModel (model.get ());
   req->setInferData (buffer.get ());
   req->setCallback (callback);
 
-  Request *req2 = new Request (NPUINPUT_HOST);
+  Request *req2 = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req2, nullptr);
   req2->setModel (model.get ());
   req2->setInferData (buffer.get ());
   req2->setCallback (callback);
 
-  Request *req3 = new Request (NPUINPUT_HOST);
+  Request *req3 = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req3, nullptr);
   req3->setModel (model.get ());
   req3->setInferData (buffer.get ());
   /** it's fine to have no callback */
 
-  Scheduler *sched;
-
-  sched = new Scheduler (api.get ());
   EXPECT_EQ (sched->submitRequest (req), req->getID ());
   EXPECT_EQ (sched->submitRequest (req2), req2->getID ());
   EXPECT_EQ (sched->submitRequest (req3), req3->getID ());
@@ -144,19 +158,28 @@ TEST (ne_core_sched_test, submit_request_args_n) {
   std::mutex m;
   auto callback = std::bind (test_callback, &num_called, &m, &cv);
 
-  Request *req = new Request (NPUINPUT_HOST);
+  /** no API provided */
+  Scheduler *sched = new Scheduler (nullptr);
+  Request *req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
+
   req->setModel (model.get ());
   req->setInferData (buffer.get ());
   req->setCallback (callback);
 
-  Scheduler *sched;
-  /** no API provided */
-  sched = new Scheduler (nullptr);
-  EXPECT_NE (sched->submitRequest (req), 0);
+  EXPECT_LT (sched->submitRequest (req), 0);
+  sched->removeRequest (req);
   delete sched;
 
   /** no request provided */
   sched = new Scheduler (api.get ());
+  req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
+
+  req->setModel (model.get ());
+  req->setInferData (buffer.get ());
+  req->setCallback (callback);
+
   EXPECT_NE (sched->submitRequest (nullptr), 0);
   EXPECT_EQ (sched->submitRequest (req), req->getID ());
 
@@ -183,23 +206,30 @@ TEST (ne_core_sched_test, submit_request_opmode_n) {
   buffer->alloc (4096);
 
   Scheduler *sched = new Scheduler (api.get ());
-
   Request *req;
+
   /** requests with not-supported opmode */
-  req = new Request (NPUINPUT_INTERNAL_CAM);
+  req = sched->createRequest (NPUINPUT_INTERNAL_CAM);
+  ASSERT_NE (req, nullptr);
+
   req->setModel (model.get ());
   req->setInferData (buffer.get ());
 
-  EXPECT_NE (sched->submitRequest (req), 0);
+  EXPECT_LT (sched->submitRequest (req), 0);
+  sched->removeRequest (req);
+
+  req = sched->createRequest (NPUINPUT_I2S_MIC);
+  ASSERT_NE (req, nullptr);
 
-  req = new Request (NPUINPUT_I2S_MIC);
   req->setModel (model.get ());
   req->setInferData (buffer.get ());
 
-  EXPECT_NE (sched->submitRequest (req), 0);
+  EXPECT_LT (sched->submitRequest (req), 0);
+  sched->removeRequest (req);
 
   delete sched;
 }
+
 /**
  * @brief test submitRequest() with empty request
  */
@@ -209,12 +239,14 @@ TEST (ne_core_sched_test, submit_request_empty_n) {
   ASSERT_NE (api.get (), nullptr);
 
   Scheduler *sched = new Scheduler (api.get ());
-  Request *req = new Request (NPUINPUT_HOST);
-
-  EXPECT_NE (sched->submitRequest (req), 0);
+  Request *req = sched->createRequest (NPUINPUT_HOST);
+  ASSERT_NE (req, nullptr);
+  EXPECT_LT (sched->submitRequest (req), 0);
+  sched->removeRequest (req);
 
   delete sched;
 }
+
 /**
  * @brief main function for unit test
  */
index b6edfb1..aa2cc0f 100644 (file)
@@ -767,6 +767,105 @@ TEST (ne_libnpuhost_test, statistics_apis_n) {
   putNPUdevice (dev);
 }
 
+typedef struct {
+  std::mutex m;
+  std::condition_variable cv;
+  bool done;
+} test_sync;
+
+static void
+test_callback (output_buffers *output, int req_id, void *data) {
+  test_sync *sync = static_cast<test_sync *> (data);
+
+  free (output->bufs[0].addr);
+
+  std::unique_lock<std::mutex> lock (sync->m);
+  sync->done = true;
+  sync->cv.notify_one ();
+}
+
+/**
+ * @brief test request decoupled APIs
+ */
+TEST (ne_libnpuhost_test, request_decoupled_apis) {
+  npudev_h dev;
+  uint32_t modelid;
+
+  std::string model_dir (NE_DATADIR);
+  model_dir += "/testdata/TRIV235_2TOPS/CONV_2D_000";
+
+  std::string model_path = model_dir + "/model.tvn";
+
+  off_t model_size = get_file_size (model_path.c_str ());
+  if (model_size <= 0)
+    /* skip */
+    return;
+
+  generic_buffer modelfile;
+  modelfile.type = BUFFER_FILE;
+  modelfile.filepath = model_path.c_str ();
+  modelfile.size = model_size;
+
+  ASSERT_EQ (getNPUdeviceByTypeAny (&dev, NPUCOND_TRIV2_CONN_SOCIP, 2), 0);
+  ASSERT_EQ (registerNPUmodel (dev, &modelfile, &modelid), 0);
+
+  /* tensor input/output data */
+  generic_buffers input, output = {0};
+  std::string input_path = model_dir + "/input_fmap_0.bin";
+  std::string output_path = model_dir + "/output_fmap_0.bin";
+
+  input.num_buffers = 1;
+  input.bufs[0].size = get_file_size (input_path.c_str ());
+  input.bufs[0].filepath = input_path.c_str ();
+  input.bufs[0].type = BUFFER_FILE;
+
+  /* tensor data info */
+  tensors_data_info info_in, info_out;
+
+  info_in.num_info = 1;
+  info_in.info[0].layout = DATA_LAYOUT_NHWC;
+  info_in.info[0].type = DATA_TYPE_QASYMM8;
+
+  info_out.num_info = 1;
+  info_out.info[0].layout = DATA_LAYOUT_NHWC;
+  info_out.info[0].type = DATA_TYPE_QASYMM8;
+
+  /* misc (optional) */
+  npu_constraint constraint;
+
+  constraint.timeout_ms = 1000;
+  constraint.priority = NPU_PRIORITY_MID;
+  constraint.notimode = NPU_INTERRUPT;
+
+  /* actual testing (blocking) */
+  int req_id;
+  EXPECT_EQ (createNPU_request (dev, modelid, &req_id), 0);
+  EXPECT_EQ (
+      setNPU_requestData (dev, req_id, &input, &info_in, &output, &info_out),
+      0);
+  EXPECT_EQ (setNPU_requestMode (dev, req_id, NPU_INFER_BLOCKING), 0);
+  EXPECT_EQ (setNPU_requestConstraint (dev, req_id, constraint), 0);
+  EXPECT_GE (submitNPU_request (dev, req_id), 0);
+
+  /* actual testing (non-blocking) */
+  test_sync sync;
+  sync.done = false;
+
+  EXPECT_EQ (createNPU_request (dev, modelid, &req_id), 0);
+  EXPECT_EQ (
+      setNPU_requestData (dev, req_id, &input, &info_in, &output, &info_out),
+      0);
+  EXPECT_EQ (setNPU_requestMode (dev, req_id, NPU_INFER_NON_BLOCKING), 0);
+  EXPECT_EQ (setNPU_requestCallback (dev, req_id, test_callback, &sync), 0);
+  EXPECT_EQ (setNPU_requestConstraint (dev, req_id, constraint), 0);
+  EXPECT_GE (submitNPU_request (dev, req_id), 0);
+
+  std::unique_lock<std::mutex> lock (sync.m);
+  sync.cv.wait (lock, [&]() { return sync.done == true; });
+
+  putNPUdevice (dev);
+}
+
 /**
  * @brief main function for unit test
  */